Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* =====================================================================
 * Bharatpedia Gadget: RefGenVe (hidden)
 * VisualEditor plugin for the Citation generator gadget.
 *
 * Adds a "Citation generator" tool to VE's Insert menu (visual mode
 * and the 2017 wikitext editor). The tool opens a dialog: paste a URL,
 * fetch metadata (via mw.libs.refGen from ext.gadget.RefGen), and
 * insert the result as a proper <ref> citation at the cursor.
 *
 * Loaded via mw.libs.ve.addPlugin( 'ext.gadget.RefGenVe' ).
 * All user-facing text is in English.
 * ===================================================================== */
( function () {
	'use strict';

	/* ---------------- Dialog ---------------- */

	function RefGenDialog( config ) {
		RefGenDialog.super.call( this, config );
		this.surface = config.surface || null;
		this.params = null;
	}
	OO.inheritClass( RefGenDialog, OO.ui.ProcessDialog );

	RefGenDialog.static.name = 'refgen';
	RefGenDialog.static.title = 'Citation generator';
	RefGenDialog.static.size = 'large';
	RefGenDialog.static.actions = [
		{ action: 'insert', label: 'Insert citation', flags: [ 'primary', 'progressive' ] },
		{ action: 'cancel', label: 'Cancel', flags: [ 'safe' ] }
	];

	RefGenDialog.prototype.initialize = function () {
		RefGenDialog.super.prototype.initialize.call( this );

		this.urlInput = new OO.ui.TextInputWidget( {
			placeholder: 'Paste a URL (e.g. https://www.bbc.com/news/...)'
		} );
		this.fetchButton = new OO.ui.ButtonWidget( { label: 'Fetch' } );
		this.statusLabel = new OO.ui.LabelWidget();
		this.typeLabel = new OO.ui.LabelWidget();
		this.pageInput = new OO.ui.TextInputWidget( { placeholder: 'e.g. 42' } );
		this.quoteInput = new OO.ui.MultilineTextInputWidget( {
			rows: 2, placeholder: 'Exact quote from the source (optional)'
		} );
		this.refPreview = new OO.ui.MultilineTextInputWidget( {
			rows: 4, readOnly: true, placeholder: 'The generated reference will appear here.'
		} );

		this.fetchButton.on( 'click', this.onFetch.bind( this ) );
		this.urlInput.on( 'enter', this.onFetch.bind( this ) );
		this.pageInput.on( 'change', this.refreshPreview.bind( this ) );
		this.quoteInput.on( 'change', this.refreshPreview.bind( this ) );

		var fieldset = new OO.ui.FieldsetLayout( {
			label: 'Citation generator',
			icon: 'reference'
		} );
		fieldset.addItems( [
			new OO.ui.FieldLayout( this.urlInput, { label: 'URL', align: 'top' } ),
			new OO.ui.FieldLayout( this.fetchButton, { align: 'top' } ),
			new OO.ui.FieldLayout( this.statusLabel, { align: 'top' } ),
			new OO.ui.FieldLayout( this.typeLabel, { align: 'top' } ),
			new OO.ui.FieldLayout( this.pageInput, { label: 'Page(s)', align: 'top' } ),
			new OO.ui.FieldLayout( this.quoteInput, { label: 'Quote', align: 'top' } ),
			new OO.ui.FieldLayout( this.refPreview, { label: 'Generated reference', align: 'top' } )
		] );
		this.$body.append( fieldset.$element );
	};

	RefGenDialog.prototype.getSetupProcess = function ( data ) {
		return RefGenDialog.super.prototype.getSetupProcess.call( this, data )
			.next( function () {
				this.surface = ( data && data.surface ) || this.surface;
				this.params = null;
				this.urlInput.setValue( '' );
				this.pageInput.setValue( '' );
				this.quoteInput.setValue( '' );
				this.refPreview.setValue( '' );
				this.typeLabel.setLabel( '' );
				this.statusLabel.setLabel( '' );
			}, this );
	};

	RefGenDialog.prototype.onFetch = function () {
		var self = this,
			RefGen = mw.libs.refGen;
		if ( !RefGen ) {
			this.statusLabel.setLabel( 'Citation generator library not loaded.' );
			return;
		}
		this.fetchButton.setDisabled( true ).setLabel( 'Loading…' );
		this.statusLabel.setLabel( '' );
		RefGen.fetchCitation( this.urlInput.getValue() ).done( function ( item ) {
			self.params = RefGen.mapToParams( item );
			self.typeLabel.setLabel( 'Detected type: {{' + self.params._template + '}}' );
			self.refreshPreview();
		} ).fail( function ( msg ) {
			self.statusLabel.setLabel( String( msg ) );
		} ).always( function () {
			self.fetchButton.setDisabled( false ).setLabel( 'Fetch' );
		} );
	};

	RefGenDialog.prototype.refreshPreview = function () {
		if ( !this.params || !mw.libs.refGen ) {
			return;
		}
		this.refPreview.setValue(
			mw.libs.refGen.buildRef(
				this.params,
				this.pageInput.getValue(),
				this.quoteInput.getValue()
			)
		);
	};

	RefGenDialog.prototype.getActionProcess = function ( action ) {
		if ( action === 'insert' ) {
			return new OO.ui.Process( function () {
				this.insertCitation();
				this.close( { action: action } );
			}, this );
		}
		return RefGenDialog.super.prototype.getActionProcess.call( this, action );
	};

	/**
	 * Insert the generated citation as a proper VE reference node
	 * at the current cursor position.
	 */
	RefGenDialog.prototype.insertCitation = function () {
		var params = this.params;
		if ( !params || !this.surface ) {
			return;
		}
		var page = this.pageInput.getValue().trim();
		var quote = this.quoteInput.getValue().trim();

		// Build the cite template as VE transclusion data
		// (same params/order/guards as mw.libs.refGen.buildRef).
		var mwParams = {};
		var dropWebsite = params._template === 'cite news' &&
			String( params.newspaper || '' ).trim() &&
			String( params.website || '' ).trim();
		[ 'url', 'title', 'author', 'date', 'website', 'newspaper', 'journal',
			'publisher', 'volume', 'issue', 'pages', 'isbn', 'doi', 'accessdate'
		].forEach( function ( k ) {
			if ( k === 'website' && dropWebsite ) {
				return;
			}
			var v = String( params[ k ] == null ? '' : params[ k ] ).trim();
			if ( !v ) {
				return;
			}
			mwParams[ k === 'accessdate' ? 'access-date' : k ] = { wt: v };
		} );
		if ( page ) {
			mwParams.page = { wt: page };
		}
		if ( quote ) {
			mwParams.quote = { wt: quote };
		}

		var names = {
			'cite web': 'Cite web',
			'cite book': 'Cite book',
			'cite journal': 'Cite journal',
			'cite news': 'Cite news',
			'cite magazine': 'Cite magazine'
		};
		var tName = names[ params._template ] || 'Cite web';
		var ns = mw.config.get( 'wgFormattedNamespaces' )[ '10' ];

		var surfaceModel = this.surface.getModel();
		var doc = surfaceModel.getDocument();
		var ref = ve.dm.MWReferenceModel.static.newEmptyRef( doc );
		ref.setDocument( doc.cloneWithData( [
			{ type: 'paragraph' },
			{
				type: 'mwTransclusionInline',
				attributes: {
					mw: {
						parts: [ {
							template: {
								target: { href: ns + ':' + tName, wt: tName },
								params: mwParams
							}
						} ]
					}
				}
			},
			{ type: '/mwTransclusionInline' },
			{ type: '/paragraph' },
			{ type: 'internalList' },
			{ type: '/internalList' }
		] ) );
		ref.setGroup( '' );
		ref.insertIntoFragment( surfaceModel.getFragment().collapseToEnd() );
	};

	/* ---------------- Toolbar tool ---------------- */

	function RefGenVeTool( toolGroup, config ) {
		OO.ui.Tool.call( this, toolGroup, config );
	}
	OO.inheritClass( RefGenVeTool, OO.ui.Tool );
	RefGenVeTool.static.name = 'refgen';
	RefGenVeTool.static.group = 'insert';
	RefGenVeTool.static.autoAddToGroup = 'insert';
	RefGenVeTool.static.title = 'Ref-Gen';
	RefGenVeTool.static.icon = 'code';

	RefGenVeTool.prototype.onSelect = function () {
		this.setActive( false );
		var surface = this.toolbar.getSurface();
		// Use the global window manager with a pre-created dialog instance:
		// surface.getDialogs() does not reliably open plain OOUI dialogs.
		ve.ui.windowManager.openWindow( refGenDialogInstance, { surface: surface } );
	};
	RefGenVeTool.prototype.onUpdateState = function () {
		this.setActive( false );
	};

	ve.ui.windowFactory.register( RefGenDialog );
	ve.ui.toolFactory.register( RefGenVeTool );
	// Pre-create the dialog and attach it to the global window manager.
	var refGenDialogInstance = new RefGenDialog();
	ve.ui.windowManager.addWindows( [ refGenDialogInstance ] );
}() );