User:KokoroSenshi/common.js

From Zelda Wiki, the Zelda encyclopedia
Revision as of 09:46, 6 May 2018 by KokoroSenshi (talk | contribs) (Experimental (non-functional): Trying completely different approach)
Jump to navigation Jump to search

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)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * Autocomplete/Dropdown list
 * Uses https://yuku-t.com/textcomplete/
 *
 * Notes:
 * - Won't work with CodeEditor since it doesn't use a textarea but that's fine
 *   since CodeEditor won't be used for wikitext
 * - $.getScript() is used since mw.loader.load() doesn't wait for the code to load
 *   mw.loader.load('https://unpkg.com/textcomplete@0.13.1/dist/textcomplete.min.js');
 * Bugs:
 * 
 */

var registerStrategiesFunctionDefault = function() {
	var self = this;///
	var params = self.params;
	var strategies = self.strategies;
	var templateName = self.templateName;
	/** Validate parameters */
	console.log("params: ");
	console.log(params);

	/**
		 * Turn each parameter into a strategy; assume no named parameters for now
		 */
	for (var paramName in params) {
		if (params.hasOwnProperty(paramName)) {
			console.log(paramName + " -> " + params[paramName]);
			// Assume paramName is a number (as a String)
			var precedingParamRegex = "";
			var paramNum = parseInt(paramName);
			for (var i = 1; i < paramNum; i++) {
				precedingParamRegex += "\\|[^\\|\\}]*";
			}
			var strategy = self.createStrategy(
				templateName,
				paramName,
				new RegExp("({{" + templateName + precedingParamRegex + "\\|)([^\\|\\}]*)$",""),
				params[paramName],
				function(name) { return name; },
				function(name) { return "$1" + name; } // Removed the '}}' for generality for now; maybe will make exceptions for single or no parameter templates?
			);
			strategies.push(strategy);
		}
	}


	var editor = new Textarea(document.getElementById('wpTextbox1'))
	, options = {
		dropdown: {
			maxCount: 500,
			style: { 'margin-top': (-parseFloat($('body').css('margin-top')))+'px' }
		}
	}
	, textcomplete = new Textcomplete(editor, options);

	textcomplete.register(strategies);

};

var registerStrategiesFunctionInfinite = function() {
	var self = this;///
	var params = self.params;
	var strategies = self.strategies;
	var templateName = self.templateName;
	
	/** Validate parameters */
	console.log("params: ");
	console.log(params);

	/**
	 * Turn "1" in params into a strategy for all parameters; assume only one paramset for all params, which are all numbered
	 */
	var strategy = self.createStrategy(
		templateName,
		"",
		new RegExp("({{" + templateName + "[^\\}]*\\|)([^\\|\\}]*)$",""),
		params["1"],
		function(name) { return name; },
		function(name) { return "$1" + name; } // Removed the '}}' for generality for now; maybe will make exceptions for single or no parameter templates?
	);
	strategies.push(strategy);

	var editor = new Textarea(document.getElementById('wpTextbox1'))
	, options = {
		dropdown: {
			maxCount: 500,
			style: { 'margin-top': (-parseFloat($('body').css('margin-top')))+'px' }
		}
	}
	, textcomplete = new Textcomplete(editor, options);

	textcomplete.register(strategies);

};

if (mw.config.get("wgAction") == 'edit') {
	
	/* Make the strategy/ies to add */
	var Strategies = {};
	Strategies.Templates = [];
	Strategies.Templates.push(new TemplateStrategy(
		"Color",
		function() {
			var self = this;
			
			$.get( "https://zelda.gamepedia.com/Template:Color?action=raw", function( data ) {
				
				/** Store parameter options in instance variable */
				self.params["1"] = readArrayFromString(data);
				
				/** Callback */
				self.registerStrategies();
			});
			
			var readArrayFromString = function(text) {
				var array = [];
				text.split("</includeonly>")[0]
					.split("#switch:{{{1\|}}}")[1]
					.split("\|#default")[0]
					.match(/\|[a-zA-Z0-9 ]*/g)
					.forEach(function(value, index){
					var name = value.slice(1).trim();
					array.push(name);
				});
				return array;
			};
			
		},
		registerStrategiesFunctionDefault
	));
	Strategies.Templates.push(new TemplateStrategy(
		"KSTest",
		function() { // Need to have self/this for params and registerStrategies()
			var self = this;
			
			/** Store parameter options in instance variable */
			self.params["1"] = ["testparam1value1","testparam1value2","testparam1value3"];
			self.params["2"] = ["testparam2value1","testparam2value2","testparam2value3"];
			self.params["3"] = ["testparam3value1","testparam3value2","testparam3value3"];

			/** Callback */
			self.registerStrategies();
			
		},
		registerStrategiesFunctionDefault
	));
	Strategies.Templates.push(new TemplateStrategy(
		"Icon",
		function() {
			var self = this;
			
			$.get( "https://zelda.gamepedia.com/Template:Icon?action=raw", function( data ) {
				
				/** Store parameter options in instance variable */
				self.params["1"] = readArrayFromString(data);
				
				/** Callback */
				self.registerStrategies();
			});
			
			var readArrayFromString = function(text) {
				var array = [];
				text.split("</includeonly>")[0]
					.split("\|#default")[0]
					.match(/\n\|[a-zA-Z0-9-+ ]*/g)
					.forEach(function(value, index){
					var name = value.slice(2).trim();
					array.push(name);
				});
				return array;
			};
			
		},
		registerStrategiesFunctionDefault
	));
	var readInitialismsArrayFromString = function(callback) {
		$.get( "https://zelda.gamepedia.com/Template:Zelda?action=raw", function( data ) {
			var initialismsArray = [];
			data.split("<noinclude>")[1]
				.match(/\|\|[a-zA-Z0-9-+& ]*\n/g)
				.forEach(function(value, index){
				var name = value.slice(2).trim();
				initialismsArray.push(name);
			});
			callback(initialismsArray);
		});
	};
	readInitialismsArrayFromString(function(initialismsArray) {
		
		Strategies.Templates.push(new TemplateStrategy(
			"Exp Game",
			function() {
				var self = this;

				/** Store parameter options in instance variable */
				self.params["1"] = initialismsArray;

				/** Callback */
				self.registerStrategies();

			},
			registerStrategiesFunctionInfinite
		));

	});
	
	// Getting a list of every template in the wiki is non-trivial...
	
	/* Load Textcomplete then register the strategy/ies */
	$(document).ready(function() {
		getTextcompleteScript(
			Strategies.Templates // Array of TemplateStrategy objects to register each of
		);
	} );
	
}

function getTextcompleteScript(templateStrategyArray) {
	console.log( "Loading textcomplete..." );
	$.getScript( "https://unpkg.com/textcomplete/dist/textcomplete.min.js", function( data, textStatus, jqxhr ) {
		console.log( [ data, textStatus, jqxhr.status ] ); // Data returned, Success, 200
		console.log( "Loaded textcomplete. (Warning: May not be executed yet)" );
		// (Global) Textarea object: https://github.com/yuku-t/textcomplete/issues/114#issuecomment-318352383
		Textarea = Textcomplete.editors.Textarea;
		templateStrategyArray.forEach(function(templateStrategy){ templateStrategy.register(); });
	});
}

// Been going over OOP in JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Example

/**
 * Strategy Object
 */

/**
 * Constructor (and non-prototype definitions)
 * (instance variables can't be accessed without "this." etc.)
 */
function TemplateStrategy(templateName, getParametersFunction, registerStrategiesFunction) {
	
	var self = this;
	
	/*********************
	** Public variables **
	**********************/
	this.templateName = templateName;	
	this.params = {}; // params.<name_of_param> = array of options // should each param have a 'description' variable too? applicable to both preset options and open-ended
	this.strategies = [];
	
	/********************
	** Public methods **
	********************/
	
	/**
	 * register method
	 */
	this.register = function() {
		self.getParameters(); // Must have callback to registerStrategies()
	};
	
	this.getParameters = getParametersFunction;
	
	this.registerStrategies = registerStrategiesFunction; 
	
	this.createStrategy = function(name, param, match, paramArray, template, replace) {
		var strategy =
			{
				id: "Template:" + name + "_" + param,
				match: /(\|)([^\|\}]*)$/,
				search: function (term, callback) {
					// term should be the stuff after the latest "|"
					var text = getBeforeCursor(el); //the text before the cursor
					var templateStartPos = getTemplateStartPos(text);
					var nameArray = paramArray.filter(function(currentValue) { return currentValue.startsWith(term); });
					callback(nameArray); // List of possible completions ('names')
				},
				template: function (name) {
					var displayName = template(name);
					return displayName; // What to display in the list
				},
				replace: function (name) {
					var replacementString = replace(name);
					return replacementString; // What to replace the matched typed text with
				}
			};
		return strategy;
	};
	
	

}

function getBeforeCursor(el) { // Borrowed from https://github.com/yuku-t/textcomplete/blob/6f07195c47ace5e787cf7b46604b37a8bd5c6d30/src/textarea.js#L82
	if (el == null) el = document.getElementById('wpTextbox1');
	return el.selectionStart !== el.selectionEnd ? null : el.value.substring(0, el.selectionEnd));
}

function getTemplateStartPos(text) {
	var count = 0;
	var index = text.length-2;
	while (i >= 0) {
		var char1 = text.charAt(i);
		var char2 = text.charAt(i+1);
		if (char1 == "}" && char2 == "}") {
			count++;
			index -= 2;
		} else if (char1 == "{" && char2 == "{") {
			count--;
			index -= 2;
		} else {
			index -= 1;
		}
		if (count < 0) return index;
	}
	return -1;
}