Ir al contenido

MediaWiki:Gadget-ProveIt.js

De Wikipedia, la enciclopedia libre
Esta es una versión antigua de esta página, editada a las 11:27 8 sep 2014 por XanaG (discusión · contribs.). La dirección URL es un enlace permanente a esta versión, que puede ser diferente de la versión actual.

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-2014 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),
 * the 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 = window.proveit = {

	templates: {
		'Cita web': {
			'url':          { type: 'url',  label: 'URL', required: true },
			'título':       { type: 'text', label: 'Título', required: true },
			'fechaacceso':  { type: 'date', label: 'Fecha de acceso', required: true },
			'cita':         { type: 'text', label: 'Cita' },
			'suscripción':  { type: 'text', label: 'Requiere suscripción?' },
			'apellido':     { type: 'text', label: 'Apellido', alias: [ 'apellidos', 'last' ] },
			'nombre':       { type: 'text', label: 'Nombre', alias: [ 'nombres', 'first' ] },
			'autor':        { type: 'text', label: 'Autor', hidden: true },
			'enlaceautor':  { type: 'text', label: 'Artículo del autor', hidden: true },
			'coautores':    { type: 'text', label: 'Coautores', hidden: true },
			'fecha':        { type: 'date', label: 'Fecha', hidden: true },
			'año':          { type: 'text', label: 'Año', hidden: true },
			'mes':          { type: 'text', label: 'Mes', hidden: true },
			'urltrad':      { type: 'url',  label: 'URL de traducción', hidden: true },
			'enlaceroto':   { type: 'text', label: 'Enlace roto?', hidden: true },
			'formato':      { type: 'text', label: 'Formato', hidden: true },
			'obra':         { type: 'text', label: 'Obra', hidden: true },
			'editor':       { type: 'text', label: 'Editor', hidden: true },
			'editorial':    { type: 'text', label: 'Editorial', hidden: true },
			'ubicación':    { type: 'text', label: 'Ubicación', hidden: true },
			'página':       { type: 'text', label: 'Página', hidden: true },
			'páginas':      { type: 'text', label: 'Páginas', hidden: true },
			'idioma':       { type: 'text', label: 'Idioma', hidden: true },
			'doi':          { type: 'text', label: 'DOI', hidden: true },
			'urlarchivo':   { type: 'url',  label: 'URL de archivo', hidden: true },
			'fechaarchivo': { type: 'date', label: 'Fecha de archivo', hidden: true }
		},
		'Cita libro': {
			'título':      { type: 'text', label: 'Título', required: true },
			'apellido':    { type: 'text', label: 'Apellido', required: true, alias: [ 'apellidos', 'last' ] },
			'nombre':      { type: 'text', label: 'Nombre', alias: [ 'nombres', 'first' ] },
			'enlaceautor': { type: 'text', label: 'Artículo del autor' },
			'año':         { type: 'text', label: 'Año' },
			'cita':        { type: 'text', label: 'Cita' },
			'autor':       { type: 'text', label: 'Autor', hidden: true },
			'url':         { type: 'url',  label: 'URL', hidden: true },
			'fechaacceso': { type: 'date', label: 'Fecha de acceso', hidden: true },
			'idioma':      { type: 'text', label: 'Idioma', hidden: true },
			'otros':       { type: 'text', label: 'Otros', hidden: true },
			'edición':     { type: 'text', label: 'Edición', hidden: true },
			'editor':      { type: 'text', label: 'Editor', hidden: true },
			'editorial':   { type: 'text', label: 'Editorial', hidden: true },
			'ubicación':   { type: 'text', label: 'Ubicación', hidden: true },
			'isbn':        { type: 'text', label: 'ISBN', hidden: true },
			'capítulo':    { type: 'text', label: 'Capítulo', hidden: true },
			'páginas':     { type: 'text', label: 'Páginas', hidden: true }
		},
		'Cita enciclopedia': {
			'título':       { type: 'text', label: 'Título', required: true },
			'enciclopedia': { type: 'text', label: 'Enciclopedia', required: true },
			'apellido':     { type: 'text', label: 'Apellido', alias: [ 'apellidos', 'last' ] },
			'nombre':       { type: 'text', label: 'Nombre', alias: [ 'nombres', 'first' ] },
			'cita':         { type: 'text', label: 'Cita' },
			'autor':        { type: 'text', label: 'Autor', hidden: true },
			'enlaceautor':  { type: 'text', label: 'Artículo del autor', hidden: true },
			'coautores':    { type: 'text', label: 'Coautores', hidden: true },
			'editor':       { type: 'text', label: 'Editor', hidden: true },
			'idioma':       { type: 'text', label: 'Idioma', hidden: true },
			'url':          { type: 'url',  label: 'URL', hidden: true },
			'fechaacceso':  { type: 'date', label: 'Fecha de acceso', hidden: true },
			'edición':      { type: 'text', label: 'Edición', hidden: true },
			'fecha':        { type: 'date', label: 'Fecha', hidden: true },
			'editorial':    { type: 'text', label: 'Editorial', hidden: true },
			'volumen':      { type: 'text', label: 'Volumen', hidden: true },
			'ubicación':    { type: 'text', label: 'Ubicación', hidden: true },
			'isbn':         { type: 'text', label: 'ISBN', hidden: true },
			'páginas':      { type: 'text', label: 'Páginas', hidden: true },
			'isbn':         { type: 'text', label: 'ISBN', hidden: true },
			'oclc':         { type: 'text', label: 'OCLC', hidden: true },
			'doi':          { type: 'text', label: 'DOI', hidden: true }
		},
		'Cita noticia': {
			'título':      { type: 'text', label: 'Título', required: true },
			'periódico':   { type: 'text', label: 'Periódico', required: true },
			'fecha':       { type: 'date', label: 'Fecha' },
			'apellido':    { type: 'text', label: 'Apellido', alias: [ 'apellidos', 'last' ] },
			'nombre':      { type: 'text', label: 'Nombre', alias: [ 'nombres', 'first' ] },
			'enlaceautor': { type: 'text', label: 'Artículo del autor', hidden: true },
			'autor':       { type: 'text', label: 'Autor', hidden: true },
			'coautores':   { type: 'text', label: 'Coautor', hidden: true },
			'url':         { type: 'url',  label: 'URL', hidden: true },
			'formato':     { type: 'text', label: 'Formato', hidden: true },
			'agencia':     { type: 'text', label: 'Agencia', hidden: true },
			'editorial':   { type: 'text', label: 'Editorial', hidden: true },
			'id':          { type: 'text', label: 'Identificador', hidden: true },
			'páginas':     { type: 'text', label: 'Páginas', hidden: true },
			'página':      { type: 'text', label: 'Página', hidden: true },
			'fechaacceso': { type: 'date', label: 'Fecha de acceso', hidden: true },
			'idioma':      { type: 'text', label: 'Idioma', hidden: true },
			'ubicación':   { type: 'text', label: 'Ubicación', hidden: true },
			'cita':        { type: 'text', label: 'Cita', hidden: true }
		},
		'Cita conferencia': {
			'título':         { type: 'text', label: 'Título', required: true },
			'apellido':       { type: 'text', label: 'Apellido', alias: [ 'apellidos', 'last' ], required: true },
			'nombre':         { type: 'text', label: 'Nombre', alias: [ 'nombres', 'first' ] },
			'fecha':          { type: 'text', label: 'Fecha' },
			'conferencia':    { type: 'date', label: 'Conferencia' },
			'enlaceautor':    { type: 'text', label: 'Artículo del autor' },
			'coaturoes':      { type: 'text', label: 'Coautores', hidden: true },
			'año':            { type: 'text', label: 'Año', hidden: true },
			'mes':            { type: 'text', label: 'Mes', hidden: true },
			'urlconferencia': { type: 'url',  label: 'URL de la conferencia', hidden: true },
			'títulolibro':    { type: 'text', label: 'Título del libro', hidden: true },
			'editor':         { type: 'text', label: 'Editor', hidden: true },
			'otros':          { type: 'text', label: 'Otros créditos', hidden: true },
			'volumen':        { type: 'text', label: 'Volumen', hidden: true },
			'edición':        { type: 'text', label: 'Edición', hidden: true },
			'publicación':    { type: 'text', label: 'Publicación', hidden: true },
			'ubicación':      { type: 'text', label: 'Ubicación', hidden: true },
			'páginas':        { type: 'text', label: 'Páginas', hidden: true },
			'url':            { type: 'url',  label: 'URL', hidden: true },
			'fechaacceso':    { type: 'date', label: 'Fecha de acceso', hidden: true },
			'formato':        { type: 'text', label: 'Formato', hidden: true },
			'doi':            { type: 'text', label: 'DOI', hidden: true },
			'id':             { type: 'text', label: 'Identificador', hidden: true },
			'isbn':           { type: 'text', label: 'ISBN', hidden: true },
			'co-isbn':        { type: 'text', label: 'Co-ISBN', hidden: true }
		},
		'Cita vídeo': {
			'título':       { type: 'text', label: 'Título', required: true },
			'año':          { type: 'text', label: 'Año' },
			'url':          { type: 'url',  label: 'URL' },
			'fechaacceso':  { type: 'date', label: 'Fecha de acceso' },
			'persona':      { type: 'text', label: 'Persona', hidden: true },
			'medio':        { type: 'text', label: 'Medio', hidden: true },
			'editorial':    { type: 'text', label: 'Editorial', hidden: true },
			'localización': { type: 'text', label: 'Localización', hidden: true },
			'tiempo':       { type: 'text', label: 'Tiempo', hidden: true },
			'cita':         { type: 'text', label: 'Cita', hidden: true },
			'id':           { type: 'text', label: 'Identificador', hidden: true }
		},
		'Cita episodio': {
			'título':           { type: 'text', label: 'Título', required: true },
			'serie':            { type: 'text', label: 'Serie', required: true },
			'fecha':            { type: 'date', label: 'Fecha' },
			'minutos':          { type: 'text', label: 'Minutos' },
			'créditos':         { type: 'text', label: 'Créditos', hidden: true },
			'cadena':           { type: 'text', label: 'Cadena', hidden: true },
			'cadenalocal':      { type: 'text', label: 'Cadena local', hidden: true },
			'ubicación':        { type: 'text', label: 'Ubicación', hidden: true },
			'serial':           { type: 'text', label: 'Serial', hidden: true },
			'inicio':           { type: 'text', label: 'Fecha de inicio', hidden: true },
			'fin':              { type: 'text', label: 'Fecha de finalización', hidden: true },
			'temporada':        { type: 'text', label: 'Temporada', hidden: true },
			'número':           { type: 'text', label: 'Número', hidden: true },
			'transcripción':    { type: 'text', label: 'Transcripción', hidden: true },
			'transcripciónurl': { type: 'url',  label: 'Transcripción URL', hidden: true }
		},
		'Cita publicación': {
			'título':      { type: 'text',   label: 'Título', required: true },
			'apellido':    { type: 'text',   label: 'Apellido', alias: [ 'apellidos', 'last' ], required: true },
			'nombre':      { type: 'text',   label: 'Nombre', alias: [ 'nombres', 'first' ] },
			'publicación': { type: 'text',   label: 'Publicación' },
			'año':         { type: 'text',   label: 'Año' },
			'url':         { type: 'url',    label: 'URL' },
			'enlaceautor': { type: 'text',   label: 'Artículo del autor', hidden: true },
			'volumen':     { type: 'number', label: 'Volumen', hidden: true },
			'número':      { type: 'number', label: 'Número', hidden: true },
			'páginas':     { type: 'text',   label: 'Páginas', hidden: true },
			'página':      { type: 'text',   label: 'Página', hidden: true },
			'ubicación':   { type: 'text',   label: 'Ubicación', hidden: true },
			'editorial':   { type: 'text',   label: 'Editorial', hidden: true },
			'issn':        { type: 'text',   label: 'ISSN', hidden: true },
			'fechaacceso': { type: 'date',   label: 'Fecha de acceso', hidden: true },
			'doi':         { type: 'text',   label: 'DOI', hidden: true }
		}
	},
	
	messages: {
		edit_tab: 'Editar',
		add_tab: 'Agregar',
		template_label: 'Plantilla',
		ref_name_label: 'Nombre de la referencia',
		required: 'Requerido',
		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',

	/**
	 * Initializes ProveIt
	 */
	start: function() {

		//Initialize only when editing
		if ( wgAction != 'edit' && wgAction != 'submit' ) {
			return false;
		}

		var dependencies = [
			'jquery.ui.tabs',
			'jquery.textSelection',
			'jquery.effects.highlight'
		];
		mw.loader.using( dependencies, function() {

			//Replace the references button for the ProveIt button
			var textbox = proveit.getTextbox();
			textbox.bind( 'wikiEditor-toolbar-buildSection-main', function( event, section ) {
				delete section.groups.insert.tools.reference;
				section.groups.insert.tools.proveit = {
					label: 'ProveIt',
					type: 'button',
					icon: proveit.ICON,
					action: {
						type: 'callback',
						execute: function() {
							jQuery( '#proveit' ).toggle();
						}
					}
				};
			});

			proveit.createGUI();
			proveit.scanForReferences();

			//Only start visible in the mainspace and user pages
			if ( wgCanonicalNamespace != '' && wgCanonicalNamespace != 'User' ) {
				jQuery( '#proveit' ).hide();
			}
		});
	},

	/**
	 * Convenience functions
	 */
	getMessage: function( key ) {
		return this.messages[ key ];
	},

	getRegisteredTemplates: function() {
		return this.templates;
	},

	getTextbox: function() {
		return jQuery( '#wpTextbox1' );
	},

	/**
	 * Highlights a given length of text, starting at a particular index
	 * @param {Number} startIndex 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 text = textbox.value;

		//Scroll to the selection
		textbox.value = text.substring( 0, startIndex );
		textbox.focus();
		textbox.scrollTop = 99999999; //Larger than any real textarea (hopefully)
		var currentScrollTop = textbox.scrollTop;
		textbox.value += text.substring( startIndex );
		if ( currentScrollTop > 0 ) {
			textbox.scrollTop = currentScrollTop + 300;
		}

		//Highlight the selection
		jQuery( textbox ).focus().textSelection( 'setSelection', {
			start: startIndex,
			end: startIndex + length
		});

		//Move the form to optimal focus
		var editformTop = $( '#editform' ).offset().top;
		window.scroll( 0, editformTop );
	},

	/**
	 * 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 text = this.getTextbox().val();
		var startIndex = text.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 ) {
			return false; //If the user has already added a summary, don't screw it up
		}
		summary = proveit.getMessage( 'summary' );
		jQuery( '#wpSummary' ).val( summary );
		return true;
	},

	/**
	 * 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 = proveit.getTextbox().val();
		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 matches = text.match( /<\s*ref.*?<\s*\/\s*ref\s*>/gi );

		if ( !matches ) {
			var noReferencesMessage = jQuery( '<div/>', { 'id': 'proveit-no-references-message', 'text': proveit.getMessage( 'no_references' ) } );
			jQuery( '#proveit-reference-list' ).append( noReferencesMessage );
			return;
		}

		for ( var i = 0; i < matches.length; i++ ) {
			var reference = proveit.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 = proveit.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 );
		if ( match ) {
			var reference = new this.TemplateReference( { 'string': referenceString } );

			//Extract the name of the template
			var templateString = match[0];
			var regExp = new RegExp( registeredTemplatesDisjunction, 'i' );
			var template = templateString.match( regExp )[0];

			//Normalize it
			for ( var registeredTemplate in registeredTemplates ) {
				if ( template.toLowerCase() == registeredTemplate.toLowerCase() ) {
					template = registeredTemplate;
				}
			}
			reference.template = template;

			//Extract the parameters	
			var paramsString = templateString.substring( templateString.indexOf( '|' ) + 1, templateString.length - 2 ); //From after the first pipe to before the closing "}}"
			var paramsArray = paramsString.split( '|' );
			for ( var paramString in paramsArray ) {

				var nameAndValue = paramsArray[ paramString ].split( '=' );
				var paramName = jQuery.trim( nameAndValue[0] );
				var paramValue = jQuery.trim( nameAndValue[1] );

				if ( !paramName || !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;
				}
			}
		} else {
			var reference = new this.RawReference( { 'string': referenceString } );
		}

		//Extract the name of the reference, if any
		//There are three possible sintaxes: <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];
		}

		return reference;
	},

	/**
	 * Takes a reference object and returns 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 labels = jQuery( '#proveit-add-tab label' );
		var label, input, paramName, paramValue;
		for ( var i = 0; i < labels.length; i++ ) {
			label =  labels[ i ];
			input = jQuery( 'input', label );
			paramName = input.attr( 'name' );
			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
		});

		proveit.addSummary();
		proveit.scanForReferences();
	},

	/**
	 * 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' );

		//Insert the <ref> name field
		var refNameLabel = jQuery( '<label/>', { 'text': proveit.getMessage( 'ref_name_label' ) } );
		var refNameInput = jQuery( '<input/>', { 'type': 'text', id: 'edit-ref-name', 'value': reference.name } );
		refNameLabel.append( refNameInput );
		editFields.append( refNameLabel );

		//Insert the dropdown menu
		var templateLabel = jQuery( '<label/>', { 'text': proveit.getMessage( 'template_label' ) } );
		var templateSelect = jQuery( '<select/>', { id: 'edit-template' } );
		var templates = proveit.getRegisteredTemplates();
		for ( var templateName in templates ) {
			var templateOption = jQuery( '<option/>', { 'value': templateName, 'text': templateName } );
			if ( reference.template == templateName ) {
				templateOption.attr( 'selected', 'selected' );
			}
			templateSelect.append( templateOption );
		}
		templateLabel.append( templateSelect );
		editFields.append( templateLabel );

		//The insert all the other fields
		var params = reference.params;
		var registeredParams = reference.getRegisteredParams();
		var hiddenParams = reference.getHiddenParams();
		var requiredParams = reference.getRequiredParams();

		for ( var paramName in registeredParams ) {

			var registeredParam = registeredParams[ paramName ];

			//Defaults
			var paramLabel = paramName;
			var paramType = 'text';
			var paramPlaceholder = '';
			var paramValue = '';

			//Override with content
			if ( 'label' in registeredParam ) {
				paramLabel = registeredParam['label'];
			}
			if ( 'type' in registeredParam ) {
				paramType = registeredParam['type'];
			}
			if ( paramName in requiredParams ) {
				paramPlaceholder = proveit.getMessage( 'required' );
			}
			if ( paramName in reference.params ) {
				paramValue = reference.params[ paramName ];
			}

			var label = jQuery( '<label/>', { 'text': paramLabel } );
			var input = jQuery( '<input/>', { 'type': paramType, 'name': paramName, 'value': paramValue, 'placeholder': paramPlaceholder } );

			//Hide the hidden parameters, unless they are filled
			if ( ( paramName in hiddenParams ) && !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();

		//Bind events
		templateSelect.change( proveit.changeTemplate );

		jQuery( '#proveit-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' });
		});
	},

	/**
	 * Changes the params in the form according to the template selected
	 */
	changeTemplate: function( event ) {

		var templateSelect = $( event.currentTarget );
		var fields = templateSelect.closest( '.fields' );

		//Save the previous parameters and then remove them
		var previousParams = {};
		var labels = fields.children( 'label' ).slice(2); //All except the <ref> name and the dropdown menu
		var label, input, paramName, paramValue;
		for ( var i = 0; i < labels.length; i++ ) {
			label = labels[ i ];
			input = jQuery( 'input', label );
			paramName = input.attr( 'name' );
			paramValue = input.val();
			if ( paramName != '' && paramValue != '' ) {
				previousParams[ paramName ] = paramValue;
			}
		}
		labels.remove();

		var template = templateSelect.val();
		var reference = new proveit.TemplateReference( { 'template': template } );
		var registeredParams = reference.getRegisteredParams();
		var hiddenParams = reference.getHiddenParams();
		var requiredParams = reference.getRequiredParams();

		for ( var paramName in registeredParams ) {

			var registeredParam = registeredParams[ paramName ];

			//Defaults
			var paramLabel = paramName;
			var paramType = 'text';
			var paramPlaceholder = '';
			var paramValue = '';

			//Override with content
			if ( 'label' in registeredParam ) {
				paramLabel = registeredParam['label'];
			}
			if ( 'type' in registeredParam ) {
				paramType = registeredParam['type'];
			}
			if ( paramName in requiredParams ) {
				paramPlaceholder = proveit.getMessage( 'required' );
			}
			if ( paramName in previousParams ) {
				paramValue = previousParams[ paramName ];
			}

			var label = jQuery( '<label/>', { 'text': paramLabel } );
			var input = jQuery( '<input/>', { 'type': paramType, 'name': paramName, 'placeholder': paramPlaceholder, 'value': paramValue } );

			if ( ( paramName in hiddenParams ) && !paramValue ) {
				label.addClass( 'hidden' );
			}

			label.append( input );
			fields.append( label );
		}

		//Make sure this button is visible
		jQuery( '.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, alt: 'ProveIt' } );
		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: proveit.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: proveit.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', 'class': 'fields' } );
		var editButtons = jQuery( '<div/>', { id: 'proveit-edit-buttons' } );
		var updateButton = jQuery( '<button/>', { id: 'proveit-update-button', text: proveit.getMessage( 'update_button' ) } );
		var showAllParamsButton = jQuery( '<button/>', { 'class': 'show-all-params-button', text: proveit.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', 'class': 'fields' } );

		//First insert the <ref> name
		var refNameLabel = jQuery( '<label/>', { 'text': proveit.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': proveit.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/>', { id: 'proveit-insert-button', 'text': proveit.getMessage( 'insert_button' ) } );
		var showAllParamsButton = jQuery( '<button/>', { 'class': 'show-all-params-button', 'text': proveit.getMessage( 'show_all_params_button' ) } );
		addButtons.append( showAllParamsButton );
		addButtons.append( insertButton );
		addTab.append( addButtons );

		//And add the tab to the rest
		tabsWrapper.append( addTab );

		//Add everything to the DOM
		tabs.append( tabsWrapper );
		gui.append( tabs );
		jQuery( document.body ).prepend( gui );

		//Initialise the jQuery UI tabs
		jQuery( '#proveit-tabs' ).tabs();

		//Bind events
		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' } );
		});

		jQuery( '#proveit-add-buttons .show-all-params-button' ).click( function() {
			jQuery( this ).hide();
			jQuery( '#proveit-add-fields label' ).css({ 'display': 'block' });
		});

		proveit.getTextbox().change( proveit.scanForReferences );

		templateSelect.change( proveit.changeTemplate );

		//Finally, trigger tha change event to fill the rest of the add form
		templateSelect.change();
	},


	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 text = textbox.val();
			var oldString = this.string;
			var newString = this.toString();

			text = text.replace( oldString, newString );

			textbox.val( text );

			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() {

			this.name = jQuery( '#edit-ref-name' ).val();

			this.template = jQuery( '#edit-template' ).val();

			//Reset the parameters
			this.params = {};
			var labels = jQuery( '#proveit-edit-form label' ).slice(2); //All except the <ref> name and the template dropdown
			var label, input, paramName, paramValue;
			for ( var i = 0; i < labels.length; i++ ) {
				label = labels[ i ];
				input = jQuery( 'input', label );
				paramName = input.attr( 'name' );
				paramValue = input.val();
				if ( paramName != '' && paramValue != '' ) {
					this.params[ paramName ] = paramValue;
				}
			}

			this.updateInTextbox();
			proveit.addSummary();
			proveit.scanForReferences();
			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.getHiddenParams = function() {
			var hiddenParams = {};
			var registeredParams = this.getRegisteredParams();
			for ( var registeredParam in registeredParams ) {
				if ( registeredParams[ registeredParam ][ 'hidden' ] === true ) {
					hiddenParams[ registeredParam ] = registeredParams[ registeredParam ];
				}
			}
			return hiddenParams;
		};
	}
}

jQuery( proveit.start );