MediaWiki:Gadget-ProveIt.js
Apariencia
Nota: Después de guardar, debes refrescar la caché de tu navegador para ver los cambios. Internet Explorer: mantén presionada Ctrl mientras pulsas Actualizar. Firefox: mientras presionas Mayús pulsas el botón Actualizar, (o presiona Ctrl-Shift-R). Los usuarios de Google Chrome y Safari pueden simplemente pulsar el botón Recargar. Para más detalles e instrucciones acerca de otros exploradores, véase Ayuda:Cómo limpiar la caché.
/**
* ProveIt is a powerful GUI tool for viewing, editing and creating references in Wikipedia
*
* Copyright 2008-2011 Georgia Tech Research Corporation, Atlanta, GA 30332-0415
* Copyright 2011-? Matthew Flaschen
* Rewritten and internationalized by Luis Felipe Schenone in 2014
*
* ProveIt is available under the GNU Free Documentation License (http://www.gnu.org/copyleft/fdl.html),
* Creative Commons Attribution/Share-Alike License 3.0 (http://creativecommons.org/licenses/by-sa/3.0/),
* and the GNU General Public License 2 (http://www.gnu.org/licenses/gpl-2.0.html)
*/
var proveit = {
//Localization to Spanish of the ProveIt gadget
templates: {
'Cita web': {
'url': { visible: true, required: true, type: 'url', label: 'URL' },
'título': { visible: true, required: false, type: 'text', label: 'Título' },
'fechaacceso': { visible: true, required: false, type: 'date', label: 'Fecha de acceso' },
'cita': { visible: true, required: false, type: 'text', label: 'Cita' },
'suscripción': { visible: false, required: false, type: 'text', label: 'Requiere suscripción?' },
'apellido': { visible: false, required: false, type: 'text', label: 'Apellido', alias: [ 'apellidos', 'last' ] },
'nombre': { visible: false, required: false, type: 'text', label: 'Nombre', alias: [ 'nombres', 'first' ] },
'autor': { visible: false, required: false, type: 'text', label: 'Autor' },
'enlaceautor': { visible: false, required: false, type: 'text', label: 'Artículo del autor' },
'coautores': { visible: false, required: false, type: 'text', label: 'Coautores' },
'fecha': { visible: false, required: false, type: 'date', label: 'Fecha' },
'año': { visible: false, required: false, type: 'text', label: 'Año' },
'mes': { visible: false, required: false, type: 'text', label: 'Mes' },
'urltrad': { visible: false, required: false, type: 'url', label: 'URL de traducción' },
'enlaceroto': { visible: false, required: false, type: 'text', label: 'Enlace roto?' },
'formato': { visible: false, required: false, type: 'text', label: 'Formato' },
'obra': { visible: false, required: false, type: 'text', label: 'Obra' },
'editor': { visible: false, required: false, type: 'text', label: 'Editor' },
'editorial': { visible: false, required: false, type: 'text', label: 'Editorial' },
'ubicación': { visible: false, required: false, type: 'text', label: 'Ubicación' },
'página': { visible: false, required: false, type: 'text', label: 'Página' },
'páginas': { visible: false, required: false, type: 'text', label: 'Páginas' },
'idioma': { visible: false, required: false, type: 'text', label: 'Idioma' },
'doi': { visible: false, required: false, type: 'text', label: 'DOI' },
'urlarchivo': { visible: false, required: false, type: 'url', label: 'URL de archivo' },
'fechaarchivo': { visible: false, required: false, type: 'date', label: 'Fecha de archivo' }
},
'Cita libro': {
'título': { visible: true, required: true, type: 'text', label: 'Título' },
'apellido': { visible: true, required: true, type: 'text', label: 'Apellido', alias: [ 'apellidos', 'last' ] },
'nombre': { visible: true, required: false, type: 'text', label: 'Nombre' },
'enlaceautor': { visible: true, required: false, type: 'text', label: 'Artículo del autor' },
'año': { visible: true, required: false, type: 'text', label: 'Año' },
'cita': { visible: true, required: false, type: 'text', label: 'Cita' },
'autor': { visible: false, required: false, type: 'text', label: 'Autor' },
'url': { visible: false, required: false, type: 'url', label: 'URL' },
'fechaacceso': { visible: false, required: false, type: 'date', label: 'Fecha de acceso' },
'idioma': { visible: false, required: false, type: 'text', label: 'Idioma' },
'otros': { visible: false, required: false, type: 'text', label: 'Otros' },
'edición': { visible: false, required: false, type: 'text', label: 'Edición' },
'editor': { visible: false, required: false, type: 'text', label: 'Editor' },
'editorial': { visible: false, required: false, type: 'text', label: 'Editorial' },
'ubicación': { visible: false, required: false, type: 'text', label: 'Ubicación' },
'isbn': { visible: false, required: false, type: 'text', label: 'ISBN' },
'capítulo': { visible: false, required: false, type: 'text', label: 'Capítulo' },
'páginas': { visible: false, required: false, type: 'text', label: 'Páginas' }
},
'Cita enciclopedia': {
'título': { visible: true, required: true, type: 'text', label: 'Título' },
'enciclopedia': { visible: true, required: true, type: 'text', label: 'Enciclopedia' },
'apellido': { visible: true, required: false, type: 'text', label: 'Apellido' },
'nombre': { visible: true, required: false, type: 'text', label: 'Nombre' },
'cita': { visible: true, required: false, type: 'text', label: 'Cita' },
'autor': { visible: false, required: false, type: 'text', label: 'Autor' },
'enlaceautor': { visible: false, required: false, type: 'text', label: 'Artículo del autor' },
'coautores': { visible: false, required: false, type: 'text', label: 'Coautores' },
'editor': { visible: false, required: false, type: 'text', label: 'Editor' },
'idioma': { visible: false, required: false, type: 'text', label: 'Idioma' },
'url': { visible: false, required: false, type: 'url', label: 'URL' },
'fechaacceso': { visible: false, required: false, type: 'date', label: 'Fecha de acceso' },
'edición': { visible: false, required: false, type: 'text', label: 'Edición' },
'fecha': { visible: false, required: false, type: 'date', label: 'Fecha' },
'editorial': { visible: false, required: false, type: 'text', label: 'Editorial' },
'volumen': { visible: false, required: false, type: 'text', label: 'Volumen' },
'ubicación': { visible: false, required: false, type: 'text', label: 'Ubicación' },
'isbn': { visible: false, required: false, type: 'text', label: 'ISBN' },
'páginas': { visible: false, required: false, type: 'text', label: 'Páginas' },
'isbn': { visible: false, required: false, type: 'text', label: 'ISBN' },
'oclc': { visible: false, required: false, type: 'text', label: 'OCLC' },
'doi': { visible: false, required: false, type: 'text', label: 'DOI' }
},
'Cita noticia': {
'título': { visible: true, required: true, type: 'text', label: 'Título' },
'periódico': { visible: true, required: true, type: 'text', label: 'Periódico' },
'fecha': { visible: true, required: false, type: 'date', label: 'Fecha' },
'nombre': { visible: true, required: false, type: 'text', label: 'Nombre' },
'apellido': { visible: true, required: false, type: 'text', label: 'Apellido' },
'enlaceautor': { visible: false, required: false, type: 'text', label: 'Artículo del autor' },
'autor': { visible: false, required: false, type: 'text', label: 'Autor' },
'coautores': { visible: false, required: false, type: 'text', label: 'Coautor' },
'url': { visible: false, required: false, type: 'url', label: 'URL' },
'formato': { visible: false, required: false, type: 'text', label: 'Formato' },
'agencia': { visible: false, required: false, type: 'text', label: 'Agencia' },
'editorial': { visible: false, required: false, type: 'text', label: 'Editorial' },
'id': { visible: false, required: false, type: 'text', label: 'Identificador' },
'páginas': { visible: false, required: false, type: 'text', label: 'Páginas' },
'página': { visible: false, required: false, type: 'text', label: 'Página' },
'fechaacceso': { visible: false, required: false, type: 'date', label: 'Fecha de acceso' },
'idioma': { visible: false, required: false, type: 'text', label: 'Idioma' },
'ubicación': { visible: false, required: false, type: 'text', label: 'Ubicación' },
'cita': { visible: false, required: false, type: 'text', label: 'Cita' }
},
'Cita conferencia': {
'título': { visible: true, required: true, type: 'text', label: 'Título' },
'apellido': { visible: true, required: true, type: 'text', label: 'Apellido' },
'nombre': { visible: true, required: false, type: 'text', label: 'Nombre' },
'fecha': { visible: true, required: false, type: 'text', label: 'Fecha' },
'conferencia': { visible: true, required: false, type: 'date', label: 'Conferencia' },
'enlaceautor': { visible: false, required: false, type: 'text', label: 'Artículo del autor' },
'coaturoes': { visible: false, required: false, type: 'text', label: 'Coautores' },
'año': { visible: false, required: false, type: 'text', label: 'Año' },
'mes': { visible: false, required: false, type: 'text', label: 'Mes' },
'urlconferencia': { visible: false, required: false, type: 'url', label: 'URL de la conferencia' },
'títulolibro': { visible: false, required: false, type: 'text', label: 'Título del libro' },
'editor': { visible: false, required: false, type: 'text', label: 'Editor' },
'otros': { visible: false, required: false, type: 'text', label: 'Otros créditos' },
'volumen': { visible: false, required: false, type: 'text', label: 'Volumen' },
'edición': { visible: false, required: false, type: 'text', label: 'Edición' },
'publicación': { visible: false, required: false, type: 'text', label: 'Publicación' },
'ubicación': { visible: false, required: false, type: 'text', label: 'Ubicación' },
'páginas': { visible: false, required: false, type: 'text', label: 'Páginas' },
'url': { visible: false, required: false, type: 'url', label: 'URL' },
'fechaacceso': { visible: false, required: false, type: 'date', label: 'Fecha de acceso' },
'formato': { visible: false, required: false, type: 'text', label: 'Formato' },
'doi': { visible: false, required: false, type: 'text', label: 'DOI' },
'id': { visible: false, required: false, type: 'text', label: 'Identificador' },
'isbn': { visible: false, required: false, type: 'text', label: 'ISBN' },
'co-isbn': { visible: false, required: false, type: 'text', label: 'Co-ISBN' }
},
'Cita vídeo': {
'título': { visible: true, required: true, type: 'text', label: 'Título' },
'año': { visible: true, required: false, type: 'text', label: 'Año' },
'url': { visible: true, required: false, type: 'url', label: 'URL' },
'fechaacceso': { visible: true, required: false, type: 'date', label: 'Fecha de acceso' },
'persona': { visible: false, required: false, type: 'text', label: 'Persona' },
'medio': { visible: false, required: false, type: 'text', label: 'Medio' },
'editorial': { visible: false, required: false, type: 'text', label: 'Editorial' },
'localización': { visible: false, required: false, type: 'text', label: 'Localización' },
'tiempo': { visible: false, required: false, type: 'text', label: 'Tiempo' },
'cita': { visible: false, required: false, type: 'text', label: 'Cita' },
'id': { visible: false, required: false, type: 'text', label: 'Identificador' }
},
'Cita episodio': {
'título': { visible: true, required: true, type: 'text', label: 'Título' },
'serie': { visible: true, required: true, type: 'text', label: 'Serie' },
'fecha': { visible: true, required: false, type: 'date', label: 'Fecha' },
'minutos': { visible: true, required: false, type: 'text', label: 'Minutos' },
'créditos': { visible: false, required: false, type: 'text', label: 'Créditos' },
'cadena': { visible: false, required: false, type: 'text', label: 'Cadena' },
'cadenalocal': { visible: false, required: false, type: 'text', label: 'Cadena local' },
'ubicación': { visible: false, required: false, type: 'text', label: 'Ubicación' },
'serial': { visible: false, required: false, type: 'text', label: 'Serial' },
'inicio': { visible: false, required: false, type: 'text', label: 'Fecha de inicio' },
'fin': { visible: false, required: false, type: 'text', label: 'Fecha de finalización' },
'temporada': { visible: false, required: false, type: 'text', label: 'Temporada' },
'número': { visible: false, required: false, type: 'text', label: 'Número' },
'transcripción': { visible: false, required: false, type: 'text', label: 'Transcripción' },
'transcripciónurl': { visible: false, required: false, type: 'url', label: 'Transcripción URL' }
},
'Cita publicación': {
'título': { visible: true, required: true, type: 'text', label: 'Título' },
'apellido': { visible: true, required: true, type: 'text', label: 'Apellido' },
'nombre': { visible: true, required: false, type: 'text', label: 'Nombre' },
'publicación': { visible: true, required: false, type: 'text', label: 'Publicación' },
'año': { visible: true, required: false, type: 'text', label: 'Año' },
'url': { visible: true, required: false, type: 'url', label: 'URL' },
'enlaceautor': { visible: false, required: false, type: 'text', label: 'Artículo del autor' },
'volumen': { visible: false, required: false, type: 'number', label: 'Volumen' },
'número': { visible: false, required: false, type: 'number', label: 'Número' },
'páginas': { visible: false, required: false, type: 'text', label: 'Páginas' },
'página': { visible: false, required: false, type: 'text', label: 'Página' },
'ubicación': { visible: false, required: false, type: 'text', label: 'Ubicación' },
'editorial': { visible: false, required: false, type: 'text', label: 'Editorial' },
'issn': { visible: false, required: false, type: 'text', label: 'ISSN' },
'doi': { visible: false, required: false, type: 'text', label: 'DOI' },
'fechaacceso': { visible: false, required: false, type: 'date', label: 'Fecha de acceso' }
}
},
messages: {
edit_tab: 'Editar',
add_tab: 'Agregar',
template_label: 'Plantilla',
ref_name_label: '<ref> name',
add_field_button: 'Agregar campo',
insert_button: 'Insertar',
update_button: 'Actualizar',
show_all_params_button: 'Mostrar todos los parámetros',
no_references: 'No se han encontrado referencias',
summary: 'Editado con ProveIt'
},
LOGO: '//proveit-js.googlecode.com/hg/static/logo.png',
ICON: '/media/wikipedia/commons/thumb/1/19/ProveIt_logo_for_user_boxes.svg/22px-ProveIt_logo_for_user_boxes.svg.png',
/**
* Configuration options
* When the gadget is added as a default gadget, users can't change these,
* unless they copy the code to their private JavaScript
* Instead, these options should be configurable from a GUI and saved in cookies
* Or alternatively, they could be removed, and the "best" configuration enforced
*/
startVisible: true,
startMaximized: false,
shouldAddSummary: true,
/**
* Initializes ProveIt
*/
start: function() {
//Initialize only when editing
if ( wgAction == 'edit' || wgAction == 'submit' ) {
var dependencies = [
'jquery.ui.tabs',
'jquery.textSelection',
'jquery.effects.highlight'
];
mw.loader.using( dependencies, function() {
//If the user is using WikiEditor, add the ProveIt button to the toolbar
var textbox = proveit.getTextbox();
textbox.bind( 'wikiEditor-toolbar-buildSection-main', function( event, section ) {
section.groups.insert.tools.proveit = {
label: 'ProveIt',
type: 'button',
icon: proveit.ICON,
action: {
type: 'callback',
execute: function() {
jQuery( '#proveit' ).toggle();
}
}
};
delete section.groups.insert.tools.reference; //Also delete the references button
});
proveit.createGUI();
proveit.scanForReferences();
//Only show the gadget on pages that are likely to contain references
if ( wgCanonicalNamespace == '' || wgCanonicalNamespace == 'User' ) {
if ( proveit.startVisible ) {
jQuery( '#proveit' ).show();
}
if ( proveit.startMaximized ) {
jQuery( '#proveit-logo' ).click();
}
}
});
}
},
//Convenience function that returns the message for the given key in the current language
getMessage: function( key ) {
return this.messages[ key ];
},
//Convenience function that returns all the registered templates
getRegisteredTemplates: function() {
return this.templates;
},
//Convenience function that returns the edit textarea
getTextbox: function() {
return jQuery( '#wpTextbox1' );
},
//Convenience function that returns the text in the edit textarea
getTextboxText: function() {
return this.getTextbox().val();
},
/**
* Provides the x (left) and y (top) offsets to a given element.
* From QuirksMode (http://www.quirksmode.org/js/findpos.html), a freely available site by Peter-Paul Koch
* @param {Node} node any HTML node
* @return {Object} offsets to node, as object with left and top properties.
*/
getOffset: function( node ) {
var left = 0, top = 0;
do {
left += node.offsetLeft;
top += node.offsetTop;
} while ( node = node.offsetParent );
return { 'left': left, 'top': top };
},
/**
* Highlights a given length of text, starting at a particular index
* @param {Number} startInd start index in Wikipedia edit textbox
* @param {Number} length length of string to highlight
* @return {Boolean} always true
*/
highlightLengthAtIndex: function( startIndex, length ) {
var textbox = this.getTextbox()[0];
var referenceStringText = textbox.value;
var editTop = this.getOffset( textbox ).top;
textbox.value = referenceStringText.substring( 0, startIndex );
textbox.focus();
textbox.scrollTop = 1000000; //Larger than any real textarea (hopefully)
var curScrollTop = textbox.scrollTop;
textbox.value += referenceStringText.substring( startIndex );
if ( curScrollTop > 0 ) {
textbox.scrollTop = curScrollTop + 200;
}
jQuery( textbox ).focus().textSelection( 'setSelection', {
start: startIndex,
end: startIndex + length
});
editTop = this.getOffset( textbox ).top;
window.scroll( 0, editTop );
return true;
},
/**
* Highlights the first instance of a given string in the MediaWiki edit textbox
* @param {String} the string in the edit textbox to highlight
* @return {Boolean} true if successful, false otherwise
*/
highlightString: function( string ) {
var textboxText = this.getTextboxText();
var startIndex = textboxText.indexOf( string );
if ( startIndex == -1 ) {
return false;
}
return this.highlightLengthAtIndex( startIndex, string.length );
},
/**
* Adds the ProveIt edit summary to the edit summary field
*/
addSummary: function() {
if ( ! this.shouldAddSummary ) {
return false;
}
var summary = jQuery( '#wpSummary' ).val()
if ( summary ) { //If the user has already added a summary, don't screw it up
return false;
}
summary = this.getMessage( 'summary' );
jQuery( '#wpSummary' ).val( summary );
return true;
},
/**
* Populates the edit form with the data of the selected reference
* @param {TemplateReference} the selected reference
*/
loadEditForm: function( reference ) {
//Remove all the fields
var editFields = jQuery( '#proveit-edit-fields' );
editFields.children().remove( 'label' );
var refNameLabel = jQuery( '<label/>', { 'text': this.getMessage( 'ref_name_label' ) } );
var refNameInput = jQuery( '<input/>', { 'type': 'text', id: 'edit-ref-name', 'value': reference.name } );
refNameLabel.append( refNameInput );
editFields.append( refNameLabel );
var params = reference.params;
var registeredParams = reference.getRegisteredParams();
var visibleParams = reference.getVisibleParams();
var requiredParams = reference.getRequiredParams();
for ( var paramName in registeredParams ) {
var paramType = registeredParams[ paramName ][ 'type' ];
var paramLabel = registeredParams[ paramName ][ 'label' ];
var paramValue = reference.params[ paramName ];
if ( 'placeholder' in registeredParams[ paramName ] ) {
var paramPlaceholder = registeredParams[ paramName ][ 'placeholder' ];
} else {
var paramPlaceholder = '';
}
var label = jQuery( '<label/>', { 'text': paramLabel } );
var input = jQuery( '<input/>', { 'type': paramType, 'name': paramName, 'value': paramValue, 'placeholder': paramPlaceholder } );
//Hide the non-visible parameters, unless they are filled
if ( ! ( paramName in visibleParams ) && ! paramValue ) {
label.addClass( 'hidden' );
}
label.append( input );
editFields.append( label );
}
//Make sure this button is visible
jQuery( '#proveit-edit-buttons .show-all-params-button' ).show();
/* Event handlers */
jQuery( '.update-button' ).click( function() {
reference.update();
});
jQuery( '#proveit-edit-buttons .show-all-params-button' ).click( function() {
jQuery( this ).hide();
jQuery( '#proveit-edit-fields label' ).css({ 'display': 'block' });
});
},
/**
* Cross-browser implementation of ECMAScript String.prototype.split function 1.0.1
* (c) Steven Levithan <stevenlevithan.com>; MIT License
* http://blog.stevenlevithan.com/archives/cross-browser-split
*
* @param {String} input string to split
* @param separator separator to split on, as RegExp or String
* @param {Number} limit limit on number of splits. If the parameter is absent, no limit is imposed.
* @return {Array} array resulting from split
*/
_compliantExecNpcg: /()??/.exec( '' )[1] === undefined, //NPCG: non-participating capturing group
_nativeSplit: String.prototype.split,
split: function( string, separator, limit ) {
//If 'separator' is not a regex, use the native 'split'
if ( Object.prototype.toString.call( separator ) !== "[object RegExp]" ) {
return proveit._nativeSplit.call( string, separator, limit );
}
var output = [],
lastLastIndex = 0,
flags = ( separator.ignoreCase ? "i" : "" ) + ( separator.multiline ? "m" : "" ) + ( separator.sticky ? "y" : "" ),
separator = RegExp( separator.source, flags + "g" ), //Make 'global' and avoid 'lastIndex' issues by working with a copy
separator2,
match,
lastIndex,
lastLength;
string = string + ""; //Type conversion
if ( ! proveit._compliantExecNpcg ) {
separator2 = RegExp( "^" + separator.source + "jQuery(?!\\s)", flags ); //Doesn't need /g or /y, but they don't hurt
}
/**
* Behavior for 'limit'. If it's...
* - undefined: no limit
* - NaN or zero: return an empty array
* - a positive number: use Math.floor(limit)
* - a negative number: no limit
* - other: type-convert, then use the above rules
*/
if ( limit === undefined || +limit < 0 ) {
limit = Infinity;
} else {
limit = Math.floor( +limit );
if ( ! limit) {
return [];
}
}
while ( match = separator.exec( string ) ) {
lastIndex = match.index + match[0].length; //separator.lastIndex is not reliable cross-browser
if ( lastIndex > lastLastIndex ) {
output.push( string.slice( lastLastIndex, match.index ) );
//Fix browsers whose 'exec' methods don't consistently return 'undefined' for nonparticipating capturing groups
if ( ! proveit._compliantExecNpcg && match.length > 1 ) {
match[0].replace( separator2, function () {
for ( var i = 1; i < arguments.length - 2; i++ ) {
if ( arguments[ i ] === undefined ) {
match[ i ] = undefined;
}
}
});
}
if ( match.length > 1 && match.index < string.length ) {
Array.prototype.push.apply( output, match.slice(1) );
}
lastLength = match[0].length;
lastLastIndex = lastIndex;
if ( output.length >= limit ) {
break;
}
}
if ( separator.lastIndex === match.index ) {
separator.lastIndex++; //Avoid an infinite loop
}
}
if ( lastLastIndex === string.length ) {
if ( lastLength || ! separator.test("") ) {
output.push("");
}
} else {
output.push( string.slice( lastLastIndex ) );
}
return output.length > limit ? output.slice( 0, limit ) : output;
},
//TODO: Remove the split code, and just use a regular regex (with two main groups for key and value), iteratively
//Regex.find? Make key and value indices match, and rework calling code as needed
//Also, check how this was done in the original code
/**
* Overly clever regex to parse template string (e.g. |last=Smith|first=John|title=My Life Story) into key and value pairs
* @param {String} templateString template string to parse
* @return {Object} object with two properties, keys and values
*/
splitKeysAndValues: function( templateString ) {
var split = {};
//The first component is "ordinary" text (no pipes), while the second is a correctly balanced wikilink, with optional pipe. Any combination of the two can appear.
split.keys = proveit.split( templateString.substring( templateString.indexOf( '|' ) + 1 ), /=(?:[^|]*?(?:\[\[[^|\]]*(?:\|(?:[^|\]]*))?\]\])?)+(?:\||\}\})/ );
split.keys.length--; //Remove single empty element at end
split.values = proveit.split( templateString.substring( templateString.indexOf( '=' ) + 1, templateString.indexOf( '}}' ) ), /\|[^|=]*=/ );
return split;
},
/**
* Scan for references in the textbox, and create a list item for each
*/
scanForReferences: function() {
jQuery( '#proveit-reference-list' ).children().remove();
//First look for all the citations
var text = this.getTextboxText();
var citations = [];
//Three possibilities: <ref name="foo" />, <ref name='foo' /> and <ref name=foo />
var citationsRegExp = /<\s*ref\s+name\s*=\s*["|']?\s*([^"'\s]+)\s*["|']?\s*\/\s*>/gi;
//Then create an object for each and store it for later
while ( match = citationsRegExp.exec( text ) ) {
citations.push({
'string': match[0],
'index': match['index'],
'name': match[1]
})
}
//Next, look for all the raw references and template references
var referencesRegExp = new RegExp( '<\s*ref.*?<\s*\/\s*ref\s*>', 'gi' );
var matches = text.match( referencesRegExp );
if ( ! matches ) {
var noReferencesMessage = jQuery( '<div/>', { 'id': 'proveit-no-references-message', 'text': this.getMessage( 'no_references' ) } );
jQuery( '#proveit-reference-list' ).append( noReferencesMessage );
return;
}
for ( var i = 0; i < matches.length; i++ ) {
var reference = this.makeReference( matches[ i ] );
//For each reference, check if there are any citations to it
for ( var j = 0; j < citations.length; j++ ) {
var citation = citations[ j ];
//And if there are, add the citation to the reference
if ( reference.name == citation.name ) {
reference.citations.push( citation );
}
}
//Finally, insert all the references into the reference list
var referenceItem = this.makeReferenceItem( reference );
jQuery( '#proveit-reference-list' ).append( referenceItem );
}
},
/**
* Takes a reference string and returns a reference object
*/
makeReference: function( referenceString ) {
var registeredTemplates = proveit.getRegisteredTemplates();
var registeredTemplatesArray = [];
for ( var registeredTemplate in registeredTemplates ) {
registeredTemplatesArray.push( registeredTemplate );
}
var registeredTemplatesDisjunction = registeredTemplatesArray.join( '|' );
var regExp = new RegExp( '{{(' + registeredTemplatesDisjunction + ').*}}', 'i' );
var match = referenceString.match( regExp );
var reference = match ? new this.TemplateReference({}) : new this.RawReference({});
//Extract the name of the template
if ( reference.type == 'TemplateReference' ) {
var templateString = match[0];
var regExp = new RegExp( registeredTemplatesDisjunction, 'i' );
var template = templateString.match( regExp )[0];
//Normalize the uppercase and lowercase letters
for ( var registeredTemplate in registeredTemplates ) {
if ( template.toLowerCase() == registeredTemplate.toLowerCase() ) {
template = registeredTemplate;
}
}
reference.template = template;
}
//Extract the name of the reference, if any
//There are three possible syntaxes: <ref name="foo">, <ref name='foo'> and <ref name=foo>
var regExp = /<[\s]*ref[\s]*name[\s]*=[\s]*(?:(?:\"(.*?)\")|(?:\'(.*?)\')|(?:(.*?)))[\s]*\/?[\s]*>/i;
var match = referenceString.match( regExp );
if ( match ) {
reference.name = match[1] || match[2] || match[3];
}
reference.string = referenceString;
//Extract the parameters
if ( reference.type == 'TemplateReference' ) {
var split = this.splitKeysAndValues( templateString );
var keys = split.keys;
var values = split.values;
for ( var i = 0; i < keys.length; i++ ) {
var paramName = keys[ i ];
var paramValue = values[ i ];
//Drop blank space, and |'s without params, which are never correct for citation templates
//TODO: Does the splitKeysAndValues method return |'s without params?
paramName = jQuery.trim( paramName ).replace( /(?:\s*\|)*(.*)/, "jQuery1" );
paramValue = jQuery.trim( paramValue );
//If there is no value, forget it
//TODO: Does the splitKeysAndValues method return empty values?
if ( ! paramValue ) {
continue;
}
//If the parameter is registered, set the value and continue
var registeredParams = reference.getRegisteredParams();
if ( paramName in registeredParams ) {
reference.params[ paramName ] = paramValue;
continue;
}
//If the parameter is NOT registered, check all the aliases
var registeredAliases = reference.getRegisteredAliases();
if ( paramName in registeredAliases ) {
paramName = registeredAliases[ paramName ]; //Normalize the parameter
reference.params[ paramName ] = paramValue;
continue;
}
}
}
return reference;
},
/**
* Generates a reference list item, to be used by scanForReferences
*/
makeReferenceItem: function( reference ) {
var item = jQuery( '<li/>', { 'class': 'reference-item' } );
if ( reference.type == 'RawReference' ) {
var itemContent = reference.string;
} else if ( reference.type == 'TemplateReference' ) {
var itemContent = '<span class="template">' + reference.template + '</span>';
var requiredParams = reference.getRequiredParams();
for ( var requiredParam in requiredParams ) {
var label = requiredParams[ requiredParam ][ 'label' ];
var value = reference.params[ requiredParam ];
itemContent += '<span class="label">' + label + '</span>: <span class="value">' + value + '</span>';
}
}
itemContent += '<span class="citations">';
for ( var i = 0; i < reference.citations.length; i++ ) {
var citationIndex = i + 1;
itemContent += '<a href="#" class="citation">' + citationIndex + '</a>';
}
itemContent += '</span>';
item.html( itemContent );
//Event handlers
jQuery( item ).click( function() {
proveit.highlightString( reference.string );
if ( reference.type == 'TemplateReference' ) {
proveit.loadEditForm( reference );
jQuery( '#proveit-reference-list' ).hide();
jQuery( '#proveit-edit-form' ).show();
}
});
jQuery( 'a.citation', item ).click( function( event ) {
event.stopPropagation();
var i = jQuery( this ).text() - 1;
var citation = reference.citations[ i ];
//TODO: If the user edits the textbox manually, the following will cease to highlight correctly
proveit.highlightLengthAtIndex( citation.index, citation.string.length );
return false;
});
return item;
},
/**
* Constructs a reference out of the contents of the add form and inserts it into the textbox
*/
insertReference: function() {
var referenceName = jQuery( '#add-ref-name' ).val();
var referenceTemplate = jQuery( '#add-template' ).val();
var reference = new this.TemplateReference( { 'name': referenceName, 'template': referenceTemplate } );
var addTab = jQuery( '#proveit-add-tab' );
var labels = jQuery( 'label', addTab );
var label, input, paramName, paramType, paramValue;
for ( var i = 0; i < labels.length; i++ ) {
label = labels[ i ];
input = jQuery( 'input', label );
paramName = input.attr( 'name' );
paramType = input.attr( 'type' );
paramValue = input.val();
if ( paramName != '' && paramValue != '' ) {
reference.params[ paramName ] = paramValue;
}
}
reference.string = reference.toString();
//Replace existing selection (if any), then scroll
var textbox = proveit.getTextbox();
textbox.textSelection( 'encapsulateSelection', {
peri: reference.string,
replace: true
});
this.addSummary();
//Update the reference list by scanning the references again
proveit.scanForReferences()
},
/**
* Changes the params in the add form according to the template selected
* @param {string} template selected from the dropdown menu
*/
loadAddForm: function( template ) {
//Remove all the fields except the <ref> name and the dropdown menu (the first two elements)
var addFields = jQuery( '#proveit-add-fields' );
addFields.children().slice(2).remove( 'label' );
var reference = new this.TemplateReference( { 'template': template } );
var registeredParams = reference.getRegisteredParams();
var visibleParams = reference.getVisibleParams();
var requiredParams = reference.getRequiredParams();
for ( var paramName in registeredParams ) {
var paramType = registeredParams[ paramName ][ 'type' ];
var paramLabel = registeredParams[ paramName ][ 'label' ];
if ( 'placeholder' in registeredParams[ paramName ] ) {
var paramPlaceholder = registeredParams[ paramName ][ 'placeholder' ];
} else {
var paramPlaceholder = '';
}
var label = jQuery( '<label/>', { 'text': paramLabel } );
var input = jQuery( '<input/>', { 'type': paramType, 'name': paramName, 'placeholder': paramPlaceholder } );
if ( ! ( paramName in visibleParams ) ) {
label.addClass( 'hidden' );
}
label.append( input );
addFields.append( label );
}
//Make sure this button is visible
jQuery( '#proveit-add-buttons .show-all-params-button' ).show();
},
createGUI: function() {
var gui = jQuery( '<div/>', { id: 'proveit' } );
var tabs = jQuery( '<div/>', { id: 'proveit-tabs' } );
var logo = jQuery( '<img/>', { id: 'proveit-logo', src: proveit.LOGO } );
tabs.append( logo );
/* Tabs list */
var tabsList = jQuery( '<ul/>' );
//Edit tab link
var editItem = jQuery( '<li/>' );
var editLink = jQuery( '<a/>', { id: 'edit-link', 'class': 'tab-link', href: '#proveit-edit-tab', text: this.getMessage( 'edit_tab' ) } );
editItem.append( editLink );
tabsList.append( editItem );
//Add tab link
var addItem = jQuery( '<li/>' );
var addLink = jQuery( '<a/>', { id: 'add-link', 'class': 'tab-link', href: '#proveit-add-tab', text: this.getMessage( 'add_tab' ) } );
addItem.append( addLink );
tabsList.append( addItem );
tabs.append( tabsList );
/* Tabs wrapper */
var tabsWrapper = jQuery( '<div/>', { id: 'proveit-tabs-wrapper' } );
/* Edit tab */
var editTab = jQuery( '<div/>', { id: 'proveit-edit-tab' } );
var referenceList = jQuery( '<ul/>', { id: 'proveit-reference-list' } );
var editForm = jQuery( '<div/>', { id: 'proveit-edit-form' } );
var editFields = jQuery( '<div/>', { id: 'proveit-edit-fields' } );
var editButtons = jQuery( '<div/>', { id: 'proveit-edit-buttons' } );
var updateButton = jQuery( '<button/>', { 'class': 'update-button', text: this.getMessage( 'update_button' ) } );
var showAllParamsButton = jQuery( '<button/>', { 'class': 'show-all-params-button', text: this.getMessage( 'show_all_params_button' ) } );
editButtons.append( showAllParamsButton );
editButtons.append( updateButton );
editForm.append( editFields );
editForm.append( editButtons );
editTab.append( referenceList );
editTab.append( editForm );
tabsWrapper.append( editTab );
/* Add tab */
var addTab = jQuery( '<div/>', { id: 'proveit-add-tab' } );
var addFields = jQuery( '<div/>', { id: 'proveit-add-fields' } );
//First insert the <ref> name
var refNameLabel = jQuery( '<label/>', { 'text': this.getMessage( 'ref_name_label' ) } );
var refNameInput = jQuery( '<input/>', { 'type': 'text', id: 'add-ref-name' } );
refNameLabel.append( refNameInput );
addFields.append( refNameLabel );
//Then insert the dropdown menu
var templateLabel = jQuery( '<label/>', { 'text': this.getMessage( 'template_label' ) } );
var templateSelect = jQuery( '<select/>', { id: 'add-template' } );
var templates = proveit.getRegisteredTemplates();
for ( var templateName in templates ) {
var templateOption = jQuery( '<option/>', { 'value': templateName, 'text': templateName } );
templateSelect.append( templateOption );
}
templateLabel.append( templateSelect );
addFields.append( templateLabel );
addTab.append( addFields );
//After the fields, insert the buttons
var addButtons = jQuery( '<div/>', { id: 'proveit-add-buttons' } );
var insertButton = jQuery( '<button/>', { 'class': 'insert-button', 'text': this.getMessage( 'insert_button' ) } );
var showAllParamsButton = jQuery( '<button/>', { 'class': 'show-all-params-button', 'text': this.getMessage( 'show_all_params_button' ) } );
addButtons.append( showAllParamsButton );
addButtons.append( insertButton );
addTab.append( addButtons );
//And add the tab to the rest
tabsWrapper.append( addTab );
// Finally, add everything to the DOM
tabs.append( tabsWrapper );
gui.append( tabs );
jQuery( document.body ).prepend( gui );
//Now that everything is on the DOM, we can call this method to generate the rest of the add tab
this.loadAddForm( templateSelect.val() );
//Set up tabs
jQuery( '#proveit-tabs' ).tabs();
/* Event handlers */
logo.click( function() {
tabsWrapper.toggle();
});
editLink.click( function() {
if ( tabsWrapper.is( ':visible' ) && editTab.is( ':visible' ) ) {
referenceList.show();
editForm.hide();
} else {
tabsWrapper.show();
editTab.show();
}
});
addLink.click( function() {
tabsWrapper.show();
addTab.show();
});
insertButton.click( function() {
proveit.insertReference();
jQuery( '#proveit-tabs' ).tabs( { selected: '#proveit-view-tab' } );
});
templateSelect.change( function() {
proveit.loadAddForm( templateSelect.val() );
});
jQuery( '#proveit-add-buttons .show-all-params-button' ).click( function() {
jQuery( this ).hide();
jQuery( '#proveit-add-fields label' ).css({ 'display': 'block' });
});
},
RawReference: function( argObj ) {
this.type = 'RawReference'; //TODO: This should be extractable in some other way
this.name = argObj.name ? argObj.name : '';
this.string = argObj.string ? argObj.string : '';
this.citations = [];
this.toString = function() {
return this.string;
};
this.updateInTextbox = function() {
var textbox = proveit.getTextbox();
var textboxText = proveit.getTextboxText();
var oldString = this.string;
var newString = this.toString();
textboxText = textboxText.replace( oldString, newString );
textbox.val( textboxText );
this.string = newString;
proveit.highlightString( newString );
};
},
TemplateReference: function( argObj ) {
//Extend RawReference
proveit.RawReference.call( this, argObj );
//Override
this.type = 'TemplateReference'; //TODO: This should be extractable in some other way
this.params = {};
this.template = argObj.template ? argObj.template : '';
this.update = function() {
var editForm = jQuery( '#proveit-edit-form' );
var name = jQuery( '#edit-ref-name' ).val();
this.name = name ? name : null; //Save blank names as null
//Clear old params
this.params = {};
var labels = jQuery( 'label', editForm );
var label, input, paramName, paramType, paramValue;
for ( var i = 0; i < labels.length; i++ ) {
label = labels[ i ];
input = jQuery( 'input', label );
paramName = input.attr( 'name' );
paramType = input.attr( 'type' );
paramValue = input.val();
if ( paramName != '' && paramValue != '' ) {
this.params[ paramName ] = paramValue;
}
}
this.updateInTextbox();
proveit.scanForReferences();
proveit.addSummary();
jQuery( '#proveit-logo' ).click();
return this;
}
//Override
this.toString = function() {
var string;
if ( this.name ) {
string = '<ref name="' + this.name + '">';
} else {
string = '<ref>';
}
string += '{{' + this.template;
for ( var name in this.params ) {
string += ' |' + name + '=' + this.params[ name ];
}
string += '}}</ref>';
return string;
};
this.getRegisteredParams = function() {
var registeredTemplates = proveit.getRegisteredTemplates();
return registeredTemplates[ this.template ];
};
/**
* Returns an object that maps aliases to registered parameters
*/
this.getRegisteredAliases = function() {
var registeredAliases = {};
var registeredParams = this.getRegisteredParams();
for ( var registeredParam in registeredParams ) {
var aliases = registeredParams[ registeredParam ][ 'alias' ];
if ( jQuery.type( aliases ) === 'array' ) {
for ( var alias in aliases ) {
registeredAliases[ alias ] = registeredParam;
}
}
if ( jQuery.type( aliases ) === 'string' ) {
registeredAliases[ aliases ] = registeredParam;
}
}
return registeredAliases;
};
this.getRequiredParams = function() {
var requiredParams = {};
var registeredParams = this.getRegisteredParams();
for ( var registeredParam in registeredParams ) {
if ( registeredParams[ registeredParam ][ 'required' ] === true ) {
requiredParams[ registeredParam ] = registeredParams[ registeredParam ];
}
}
return requiredParams;
};
this.getVisibleParams = function() {
var visibleParams = {};
var registeredParams = this.getRegisteredParams();
for ( var registeredParam in registeredParams ) {
if ( registeredParams[ registeredParam ][ 'visible' ] === true ) {
visibleParams[ registeredParam ] = registeredParams[ registeredParam ];
}
}
return visibleParams;
};
}
}
jQuery( proveit.start );