MediaWiki:Gadget-Hotkeys.js

(function {	var editBoxId = 'wpTextbox1';	var openCurly = '{{'; // prevents MediaWiki from doing its unwated black magic where JS strings surrounded by –  are counted as transclusions	var cm = document.getElementsByClassName("CodeMirror")[0];	if (!cm) {		console.warn("Gadget-Hotkeys: Unable to find CodeMirror in DOM");		return;	}	cm = cm.CodeMirror;	cm.setOption("extraKeys", { "Ctrl-Alt-T": function(cm) { insertText(cm, openCurly+'Term|'+termContext+'|', '}}') }, "Ctrl-Alt-P": function(cm) { insertText(cm, openCurly+'Plural|'+termContext+'|', '}}') }, "Ctrl-Alt-I": function(cm) { var termContext = getTermContext; var currentPage = mw.config.get('wgTitle'); insertText(cm, openCurly+'Term|'+termContext+'|'+currentPage+'}}'); }	});	function insertText(cm, textBeforeCursor, textAfterCursor) {		textBeforeCursor = textBeforeCursor || ;		textAfterCursor = textAfterCursor || ;		var selection = cm.getSelection;		var replacement = textBeforeCursor + selection + textAfterCursor;		cm.replaceSelection(replacement);	}	function getTermContext {		var game = getCurrentGameContext;		if (game === null) { // if context not loaded yet, don't infer any game			return ;		}		else if (game === ) { //if context loaded but no current context exists, use "Series"			return 'Series';		}		else {			return getLatestVersion(game);		}	}	/** CONTEXT **/	var gameContextRegex = null;	var gameToLatestVersion = {};	(function loadGames { new mw.Api.get({			action: 'cargoquery',			format: 'json',			limit: 'max',			tables: 'Games',			fields: 'code,supersededBy',			order_by: 'canonOrder'		}).then(function(result) {			gameContextRegex = '';			var gameTokens = result.cargoquery.map(function(queryResult) { var game = queryResult.title; gameToLatestVersion[game.code] = game.supersededBy || game.code; var gameCapture = '(' + escapeRegExp(game.code) + ')'; // Each sub-expression represents a game-based template which can be used to infer the current game context. return [ openCurly + gameCapture + '}}', openCurly + gameCapture + '\\|-}}', openCurly + 'Term\\|' + gameCapture + '\\|', openCurly + 'Plural\\|' + gameCapture + '\\|', ].join('|'); });			gameContextRegex = new RegExp(gameTokens.join('|'), 'g');		}); });	function escapeRegExp(string) {	 return string.replace(/[{}.*+\-?^$|[\]\\]/g, '\\$&');	}	/**	 * Attemps to infer the current game that should be inserted into text by looking at the templates used before it (,, OoT:, etc.)	 * @returns a game code (e.g. OoT, MM, BotW), or an empty string if none found. Returns nil if games haven't been loaded yet.	**/ 	function getCurrentGameContext {		if (!gameContextRegex) { // list of games hasn't loaded yet			return null;		}		var gameMatch = getLastMatchBeforeCursor(gameContextRegex);		if (!gameMatch) {			return ;		}		var headingMatch = getLastMatchBeforeCursor(/[^=]={2,3}[^=]+={2,3}/g); // match L2 or L3 heading		if (headingMatch && headingMatch.index > gameMatch.index) { //Effectively: L2 and L3 headings clear the game context			return ;		}		var captures = gameMatch.filter(Boolean); // remove empty capture groups		return captures[1]; }	function getLastMatchBeforeCursor(regex) { var editBox = document.getElementById(editBoxId); var text = editBox.value.substring(0, editBox.selectionStart); var it = text.matchAll(regex); var lastMatch; var result = it.next; while(!result.done) { lastMatch = result.value; result = it.next; }		return lastMatch; }	function getLatestVersion(game) { return gameToLatestVersion && gameToLatestVersion[game] || game; } });