User:Nux/veAutocorrect.js

/** * Autocorrection features in Visual Editor. * * Polska instrukcja: *		https://pl.wikipedia.org/wiki/WP:NAC * * Version history and technical docs: *		https://github.com/Eccenux/veAutocorrect * * Authors: Maciej Nux Jaros, Schnark. * *  */ /*global mediaWiki, OO, ve*/ (function (mw) {	"use strict";

var version = '2.1.4';

/**	 * Helpers for defining replacements. */	class Helpers { /**		 * p-starter * 		 * Note! The paragraph is not closed, so that it can be used in `from`. * E.g.: `from: p('=z+'),` * 		 * Normally paragraphs should be closed (see h2). */		p(text) { var textArray = text.split(''); return [{type: 'paragraph'}].concat(textArray); }		/**		 * Standard header. * 		 * E.g.: `to: h2('See also'),` */		h2(text, skipParagraph) { var head = [ {type: 'heading', attributes: {level: 2}}, ...text.split(''), {type: '/heading'}, ];			var p = [ {type: 'paragraph'}, {type: '/paragraph'}, ];			return skipParagraph ? head : head.concat(p); }		/**		 * Inline or block template. * 		 * Note! Inline templates should be inside a paragraph. * E.g.: * ```		 * tpl({				target: {					href: 'Szablon:Przypisy',					wt: 'Przypisy'				},				//params: {}			}) * ```			*/		tpl(template, block) { var tplType = block ? 'mwTransclusionBlock' : 'mwTransclusionInline'; return [ {					type: tplType, attributes: { mw: { parts: [ { template: template } ] }					}				},				{ type: '/' + tplType }, ];		}	}

/**	 * Autocorrect class (export). */	var veNuxAutocorrect = { version: version, helpers: new Helpers, _ready: false, _configs: [], /**		 * Add replacemnt rule. *		 * Examples in documentation. * See also: `autoCorrectFromTo`. */		addReplacements: function(config) { if (this._ready) { this._run(config); } else { this._configs.push(config); }		},		/**		 * Alias `addReplacements`. */		add: function(config) { this.addReplacements(config); },		_run: function(config) { autoCorrectFromTo(config.from, config.to); },		_onReady: function { for (var i = 0; i < this._configs.length; i++) { this._run(this._configs[i]); }			this._configs = []; this._ready = true; mw.hook('userjs.veNuxAutocorrect.ready').fire(veNuxAutocorrect, veNuxAutocorrect.helpers); },	};	mw.hook('userjs.veNuxAutocorrect').fire(veNuxAutocorrect, veNuxAutocorrect.helpers);

// shorthand for helpers var h = veNuxAutocorrect.helpers;

// Usage info helper // This is only for quick death, expected to be re-checked on page reload e.g. from wiki-code editor. var usageInfoDone = false; /**	 * Append gadget usage info. * 	 * Adds documentation page shortcut in summary. */	function appendUsageInfo { // quick death if (usageInfoDone) { //console.log('[NAC] appendUsageInfo: quick death'); return; }

if (!(ve.init && typeof ve.init.target === 'object')) { //console.log('[NAC] appendUsageInfo: no target'); return; }

var target = ve.init.target; var myInfo = "WP:NAC"; // append if not already if (typeof target.initialEditSummary === 'string' && target.initialEditSummary.length) { //console.log('[NAC] appendUsageInfo: append?'); if (target.initialEditSummary.indexOf(myInfo) < 0) { //console.log('[NAC] appendUsageInfo: append'); target.initialEditSummary += ", " + myInfo; }		// create when empty } else { //console.log('[NAC] appendUsageInfo: create info'); target.initialEditSummary = myInfo; }		usageInfoDone = true; }

/**	 * AutoCorrectCommand. * 	 * Command to replace selected content and place the cursor after it. * 	 * inherit from ve.ui.Command, and override execute */	function AutoCorrectCommand (name, content) { AutoCorrectCommand.parent.call(this, name); this.content = content; }

/**	 * ReSequence. * 	 * like ve.ui.Sequence, with the difference that for regular expressions * of the form /foo(bar)/ only the parentheses is used as Range, not the whole expression */	function ReSequence { ReSequence.parent.apply(this, arguments); }

var customVeClassesReady = false;

/**	 * Init classes when ready ve.ui is ready. * 	 * @returns true if already there. */	function initCustomVeClasses { // avoid re-run when VE re-opened without reloading page if (customVeClassesReady) { return true; }		customVeClassesReady = true; OO.inheritClass(AutoCorrectCommand, ve.ui.Command); AutoCorrectCommand.prototype.execute = function (surface) { surface.getModel.getFragment.insertContent(this.content).collapseToEnd.select; appendUsageInfo; return true; };		OO.inheritClass(ReSequence, ve.ui.Sequence); ReSequence.prototype.match = function (data, offset, plaintext) { var execResult; if (this.data instanceof RegExp) { execResult = this.data.exec(plaintext); return execResult && new ve.Range(offset - execResult[1].length, offset); }			return ReSequence.parent.prototype.match.apply(this, arguments); };	}

/**	 * autoCorrectFromTo. * 	 * when the user enters "from" change it to "to" * @param from can be a string, a regular expression of the form /foo(bar)/ or an array of data * @param to can be a string or an array of data */	function autoCorrectFromTo (from, to) { //get a unique name, we use it for both the command and the sequnce var name = 'nuxAutoCorrectCommand-' + (autoCorrectCommandCount++); //create and register the command ve.ui.commandRegistry.register(			new AutoCorrectCommand(name, to)		); //let the surface know that there is a new command that can be executed ve.init.target.getSurface.commands.push(name); //create and register the sequence ve.ui.sequenceRegistry.register(			new ReSequence(/*sequence*/ name, /*command*/ name, from, 0, true)		); }	var autoCorrectCommandCount = 0;

/**	 * Init commands. */	function initAutoCorrect (lang, wiki) { //define what should be autocorrected

//for all languages and projects autoCorrectFromTo('--', '–'); autoCorrectFromTo('–-', '—'); autoCorrectFromTo('...', '…'); autoCorrectFromTo('<<', '«'); autoCorrectFromTo('>>', '»'); autoCorrectFromTo('->', '→'); autoCorrectFromTo(/(?:^|[^\d])(1\/2 )$/, '½ '); autoCorrectFromTo(/(?:^|[^\d])(1\/4 )$/, '¼ '); autoCorrectFromTo(/(?:^|[^\d])(3\/4 )$/, '¾ '); autoCorrectFromTo('+-', '±'); /*		autoCorrectFromTo(/\d(')/, '′'); autoCorrectFromTo(/\D(')/, '’'); autoCorrectFromTo(/\d(")/, '″');		*/		//depending on the content language		switch (lang) {			case 'de':				autoCorrectFromTo(/(?:^|[( \n])(")$/, '„');				autoCorrectFromTo(/[^\d( \n](")$/, '“');			break;			// disabled per en.wiki policies en:MOS:PUNCT			/*			case 'en':				autoCorrectFromTo(/(?:^|[( \n])(")$/, '“');				autoCorrectFromTo(/[^\d( \n](")$/, '”');			break;			*/			case 'pl':				autoCorrectFromTo(/(?:^|[( \n])(")$/, '„');				autoCorrectFromTo(/[^\d( \n](")$/, '”');			break;		}		//depending on the wiki		/*jshint onecase: true*/		switch (wiki) {			case 'dewiki':				autoCorrectFromTo([{type: 'paragraph'}, '=', 'w'], [					{type: 'heading', attributes: {level: 2}},					'W', 'e', 'b', 'l', 'i', 'n', 'k', 's',					{type: '/heading'},					{type: 'paragraph'}				]);			break;			case 'plwiki':				var iso = (new Date).toISOString;				var ym = iso.substr(0,7);				autoCorrectFromTo('{fd', h.tpl({						target: {							href: 'Szablon:Fakt',							wt: 'fakt'						},						params: {							'data': {								wt: ym							}						}					}) );			break;		}		// run custom commands		veNuxAutocorrect._onReady;	}

//we just need to run once the editor is ready //don't care about dependencies, they should be fine when activation is complete mw.hook('ve.activationComplete').add(function {		var alreadyDone = initCustomVeClasses;		if (!alreadyDone) {			initAutoCorrect(mw.config.get('wgContentLanguage'), mw.config.get('wgDBname'));		}	});

})(mediaWiki); //