User:KokoroSenshi/common.js: Difference between revisions
Jump to navigation
Jump to search
KokoroSenshi (talk | contribs) m (Attempt to enable a parameter dependent header (or later, footer) on the dropdown list) |
KokoroSenshi (talk | contribs) m (Tidied somewhat) |
||
Line 1: | Line 1: | ||
/** | /** | ||
* Autocomplete | * WikitextAutocompleter | ||
* Provides Autocomplete (a dropdown list) for wiki editing such as for | |||
* completing templates | |||
* Uses https://yuku-t.com/textcomplete/ | * Uses https://yuku-t.com/textcomplete/ | ||
* | * | ||
Line 6: | Line 8: | ||
* - Won't work with CodeEditor since it doesn't use a textarea but that's fine | * - Won't work with CodeEditor since it doesn't use a textarea but that's fine | ||
* since CodeEditor won't be used for wikitext | * since CodeEditor won't be used for wikitext | ||
* Bugs: | * Bugs: | ||
* | * - | ||
*/ | */ | ||
Line 19: | Line 19: | ||
autoComplete = {}; // Contains supporting variables, e.g. initialismsArray, createTemplateStrategy() | autoComplete = {}; // Contains supporting variables, e.g. initialismsArray, createTemplateStrategy() | ||
/** | /** Supporting definitions */ | ||
/* Array of Standard Initialisms */ | |||
autoComplete.initialismsArray = []; | |||
var readInitialisms = function(callback) { | |||
$.get( "https://zelda.gamepedia.com/Template:Zelda?action=raw", function( data ) { | |||
var initialisms = []; | |||
data.split("<noinclude>")[1] | |||
.match(/\|\|[a-zA-Z0-9-+& ]*\n/g) | |||
.forEach(function(value, index){ | |||
var name = value.slice(2).trim(); | |||
initialisms.push(name); | |||
}); | |||
autoComplete.initialismsArray = initialisms; // Leave autoComplete.initialismsArray empty until fully filled | |||
if (typeof callback === "function") callback(); | |||
}); | |||
}; | |||
/* Call it, optionally with a callback if needed */ | |||
readInitialisms(); // No specific need for the callback yet? | |||
/** Template parameter definitions */ | |||
/* Template:Color */ | |||
templates.Color = {}; | templates.Color = {}; | ||
$.get( "https://zelda.gamepedia.com/Template:Color?action=raw", function( data ) { | $.get( "https://zelda.gamepedia.com/Template:Color?action=raw", function( data ) { | ||
Line 36: | Line 58: | ||
}); | }); | ||
/* Template:KSTest */ | |||
templates.KSTest = { | templates.KSTest = { | ||
"1": ["testparam1value1","testparam1value2","testparam1value3"], | "1": ["testparam1value1","testparam1value2","testparam1value3"], | ||
Line 42: | Line 65: | ||
}; | }; | ||
/* Template:Icon */ | |||
templates.Icon = {}; | templates.Icon = {}; | ||
$.get( "https://zelda.gamepedia.com/Template:Icon?action=raw", function( data ) { | $.get( "https://zelda.gamepedia.com/Template:Icon?action=raw", function( data ) { | ||
Line 57: | Line 81: | ||
}); | }); | ||
/* Template:Exp Game */ | |||
templates["Exp Game"] = {}; | templates["Exp Game"] = {}; | ||
templates["Exp Game"].getParams = function(paramNum) { | templates["Exp Game"].getParams = function(paramNum) { | ||
return autoComplete.initialismsArray; // Assumes an integer paramNum > 0 | |||
}; | }; | ||
// Getting a list of every template in the wiki is non-trivial... | // Getting a list of every template in the wiki is non-trivial... too many for a single api call | ||
/** Registering strategies */ | |||
autoComplete.currentHeader = ""; // An attempt to generate a dropdown menu header to describe the parameter in some way, to accompany choices, or when there are no preset choices. | autoComplete.currentHeader = ""; // An attempt to generate a dropdown menu header to describe the parameter in some way, to accompany choices, or when there are no preset choices. | ||
Line 94: | Line 96: | ||
console.log( "Loading textcomplete..." ); | console.log( "Loading textcomplete..." ); | ||
$.getScript( "https://unpkg.com/textcomplete/dist/textcomplete.min.js", function( data, textStatus, jqxhr ) { | $.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( [ data, textStatus, jqxhr.status ] ); // Data returned, Success, 200 | ||
console.log( "Loaded textcomplete. (Warning: May not be executed yet)" ); | console.log( "Loaded textcomplete. (Warning: May not be executed yet)" ); | ||
// Textarea object: https://github.com/yuku-t/textcomplete/issues/114#issuecomment-318352383 | // Textarea object: https://github.com/yuku-t/textcomplete/issues/114#issuecomment-318352383 | ||
Textarea = Textcomplete.editors.Textarea; // Global | Textarea = Textcomplete.editors.Textarea; // Global Variable | ||
autoComplete.registerStrategies(); | autoComplete.registerStrategies(); | ||
}); | }); | ||
}; | }; | ||
/* Note: The param arrays need | |||
/* Note: The param arrays need not exist before strategy is registered */ | |||
autoComplete.registerStrategies = function() { | autoComplete.registerStrategies = function() { | ||
var editor = new Textarea(document.getElementById("wpTextbox1")) | var editor = new Textarea(document.getElementById("wpTextbox1")) | ||
Line 107: | Line 110: | ||
dropdown: { | dropdown: { | ||
maxCount: 500, | maxCount: 500, | ||
header: function() { return autoComplete.currentHeader }, | header: function() { return autoComplete.currentHeader; }, | ||
style: { "margin-top": (-parseFloat($("body").css("margin-top")))+"px" } | style: { "margin-top": (-parseFloat($("body").css("margin-top")))+"px" } | ||
} | } | ||
Line 115: | Line 118: | ||
/** Register strategies */ | /** Register strategies */ | ||
strategies.push(autoComplete.createTemplateStrategy()); | strategies.push(autoComplete.createTemplateStrategy()); | ||
textcomplete.register(strategies); | textcomplete.register(strategies); | ||
Line 121: | Line 123: | ||
autoComplete.createTemplateStrategy = function() { | autoComplete.createTemplateStrategy = function() { | ||
console.log("createTemplateStrategy start"); | console.log("/* createTemplateStrategy - start */"); | ||
// Get text preceding text cursor, | |||
// like https://github.com/yuku-t/textcomplete/blob/6f07195c47ace5e787cf7b46604b37a8bd5c6d30/src/textarea.js#L82 | |||
var getBeforeCursor = function(/**textarea*/) { | |||
textarea = document.getElementById("wpTextbox1"); | textarea = document.getElementById("wpTextbox1"); | ||
return textarea.selectionStart !== textarea.selectionEnd ? null : textarea.value.substring(0, textarea.selectionEnd); | return textarea.selectionStart !== textarea.selectionEnd ? null : textarea.value.substring(0, textarea.selectionEnd); | ||
Line 129: | Line 133: | ||
var getLastTemplateOpenBracketPos = function(text) { // Assumes no "{{{" and "}}}" (perhaps ok even with these, anyway?), nor <nowiki> tags that would render "{{" or "}}" escaped/inactive, | var getLastTemplateOpenBracketPos = function(text) { // Assumes no "{{{" and "}}}" (perhaps ok even with these, anyway?), nor <nowiki> tags that would render "{{" or "}}" escaped/inactive, | ||
var count = 0 | var count = 0 | ||
, index = text.length - 2 | |||
, foundBracketPair | |||
, chars; | |||
while (index >= 0) { | while (index >= 0) { | ||
chars = [text.charAt(index), text.charAt(index+1)]; | |||
console.log(chars); | ///console.log(chars); | ||
foundBracketPair = false; | |||
if (chars[0] === '}' && chars[1] === '}') { | if (chars[0] === '}' && chars[1] === '}') { | ||
count++; | count++; | ||
Line 143: | Line 149: | ||
} | } | ||
if (count < 0) return index; | if (count < 0) return index; | ||
foundBracketPair | if (foundBracketPair) index -= 2; else index -= 1; | ||
} | } | ||
return -1; | return -1; | ||
Line 154: | Line 159: | ||
match: /(\|)([^\|]*)$/, | match: /(\|)([^\|]*)$/, | ||
search: function (term, callback) { | search: function (term, callback) { | ||
console.log(" | console.log("/* Template parameter choice search - start */"); | ||
var text = getBeforeCursor(); | var text = getBeforeCursor(); | ||
console.log("Text before cursor | console.log(" * Text before cursor: " + text); | ||
text = text.slice(0, text.length - term.length); | text = text.slice(0, text.length - term.length); | ||
console.log("Text before term | console.log(" * Text before term: " + text); | ||
var templateStartPos = getLastTemplateOpenBracketPos(text); | var templateStartPos = getLastTemplateOpenBracketPos(text); | ||
var templateBody = text.slice(templateStartPos + "{{".length); | var templateBody = text.slice(templateStartPos + "{{".length); | ||
console.log("templateBody | console.log(" * templateBody: " + templateBody); | ||
var templateName = templateBody.slice(0,templateBody.indexOf("|")); // Assumes stuff like {{Test|, not {{Test{{aTemplate}}| | var templateName = templateBody.slice(0,templateBody.indexOf("|")); // Assumes stuff like {{Test|, not {{Test{{aTemplate}}| | ||
console.log("templateName | console.log(" * templateName: " + templateName); | ||
/ | /* | ||
* Remove the templates [TODO: and tables] (there are no unmatched template brackets within this string?) | |||
* - Loop a regex removal of {{..}} pairs, inside out | |||
* - There should be no unmatched pairs due to having called | |||
* getLastTemplateOpenBracketPos(text) | |||
* - Use "?" in the regex to "lazy" match, or else it matches | |||
* the biggest {{...}}, which renders the following method | |||
* non-functional | |||
* - The match will only have one "}}" at the end, due to ltr | |||
* lazy regex search? | |||
* - Then remove the rightmost/innermost {{...}} that includes | |||
* this "}}", by reversing so the pair is on the left side of | |||
* the string, lazy matching, then reversing back | |||
*/ | |||
var templateBodyTrimmed = templateBody; | var templateBodyTrimmed = templateBody; | ||
while (templateBodyTrimmed != (templateBodyTrimmed = templateBodyTrimmed.replace(/\{\{[^]*?\}\}/g, function (match) { | while (templateBodyTrimmed != (templateBodyTrimmed = templateBodyTrimmed.replace(/\{\{[^]*?\}\}/g, function (match) { | ||
var matchInner = match.slice(2, match.length-2); | var matchInner = match.slice(2, match.length - 2); // Remove outer braces | ||
if (matchInner.includes("{{") | if (matchInner.includes("{{")) { | ||
// Remove the last pair of "{{" and "}}" | // Remove the last (inner) pair of "{{" and "}}" | ||
return match.split("").reverse().join("") | return match.split("").reverse().join("") // Reverse | ||
.replace(/\}\}[^]*?\{\{/, "") // Pairs become "}}...{{" when the string is reversed | .replace(/\}\}[^]*?\{\{/, "") // Pairs become "}}...{{" when the string is reversed | ||
.split("").reverse().join(""); | .split("").reverse().join(""); //Reverse back | ||
} else { | } else { | ||
return ""; | return ""; | ||
Line 180: | Line 197: | ||
)); | )); | ||
// Count the number of "|" in text to determine which parameter ( | // Count the number of "|" in text to determine which number | ||
// parameter (e.g. "{{templateName|param1|param2|currentparam" ) | |||
var paramNum = 0; | var paramNum = 0; | ||
for (var i=0; i<templateBodyTrimmed.length; i++) if (templateBodyTrimmed.charAt(i) == '|') paramNum++; | for (var i=0; i<templateBodyTrimmed.length; i++) if (templateBodyTrimmed.charAt(i) == '|') paramNum++; | ||
console.log(paramNum); | console.log(" * paramNum: " + paramNum); | ||
var paramArray = []; | var paramArray = []; | ||
if (templates[templateName] !== undefined) { | if (templates[templateName] !== undefined) { | ||
Line 192: | Line 210: | ||
} | } | ||
} | } | ||
console.log(paramArray); | console.log(" * paramArray: " + paramArray); | ||
autoComplete.currentHeader = templateName + " " + paramNum; // testing out header | autoComplete.currentHeader = templateName + " " + paramNum; // testing out header | ||
var nameArray = paramArray.filter(function(currentValue) { return currentValue.startsWith(term); }); | var nameArray = paramArray.filter(function(currentValue) { return currentValue.startsWith(term); }); | ||
console.log(" * nameArray: " + paramArray); | |||
console.log("/* Template parameter choice search - end */"); | |||
callback(nameArray); // List of possible completions ("names") | callback(nameArray); // List of possible completions ("names") | ||
}, | }, | ||
Line 207: | Line 227: | ||
}; | }; | ||
console.log("createTemplateStrategy end"); | console.log("/* createTemplateStrategy - end */"); | ||
return templateStrategy; | return templateStrategy; |
Revision as of 02:55, 7 May 2018
/**
* WikitextAutocompleter
* Provides Autocomplete (a dropdown list) for wiki editing such as for
* completing templates
* 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
* Bugs:
* -
*/
if (mw.config.get("wgAction") == "edit") {
/** Global Variables */
strategies = []; // Array of strategies
templates = {}; // Contains all the parameter arrays/definitions
autoComplete = {}; // Contains supporting variables, e.g. initialismsArray, createTemplateStrategy()
/** Supporting definitions */
/* Array of Standard Initialisms */
autoComplete.initialismsArray = [];
var readInitialisms = function(callback) {
$.get( "https://zelda.gamepedia.com/Template:Zelda?action=raw", function( data ) {
var initialisms = [];
data.split("<noinclude>")[1]
.match(/\|\|[a-zA-Z0-9-+& ]*\n/g)
.forEach(function(value, index){
var name = value.slice(2).trim();
initialisms.push(name);
});
autoComplete.initialismsArray = initialisms; // Leave autoComplete.initialismsArray empty until fully filled
if (typeof callback === "function") callback();
});
};
/* Call it, optionally with a callback if needed */
readInitialisms(); // No specific need for the callback yet?
/** Template parameter definitions */
/* Template:Color */
templates.Color = {};
$.get( "https://zelda.gamepedia.com/Template:Color?action=raw", function( data ) {
var colorsArray = [];
data.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();
colorsArray.push(name);
});
templates.Color = {
"1": colorsArray
};
});
/* Template:KSTest */
templates.KSTest = {
"1": ["testparam1value1","testparam1value2","testparam1value3"],
"2": ["testparam2value1","testparam2value2","testparam2value3"],
"3": ["testparam3value1","testparam3value2","testparam3value3"]
};
/* Template:Icon */
templates.Icon = {};
$.get( "https://zelda.gamepedia.com/Template:Icon?action=raw", function( data ) {
var iconsArray = [];
data.split("</includeonly>")[0]
.split("\|#default")[0]
.match(/\n\|[a-zA-Z0-9-+ ]*/g)
.forEach(function(value, index){
var name = value.slice(2).trim();
iconsArray.push(name);
});
templates.Icon = {
"1": iconsArray
};
});
/* Template:Exp Game */
templates["Exp Game"] = {};
templates["Exp Game"].getParams = function(paramNum) {
return autoComplete.initialismsArray; // Assumes an integer paramNum > 0
};
// Getting a list of every template in the wiki is non-trivial... too many for a single api call
/** Registering strategies */
autoComplete.currentHeader = ""; // An attempt to generate a dropdown menu header to describe the parameter in some way, to accompany choices, or when there are no preset choices.
autoComplete.getTextcompleteScript = function() {
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)" );
// Textarea object: https://github.com/yuku-t/textcomplete/issues/114#issuecomment-318352383
Textarea = Textcomplete.editors.Textarea; // Global Variable
autoComplete.registerStrategies();
});
};
/* Note: The param arrays need not exist before strategy is registered */
autoComplete.registerStrategies = function() {
var editor = new Textarea(document.getElementById("wpTextbox1"))
, options = {
dropdown: {
maxCount: 500,
header: function() { return autoComplete.currentHeader; },
style: { "margin-top": (-parseFloat($("body").css("margin-top")))+"px" }
}
}
, textcomplete = new Textcomplete(editor, options);
/** Register strategies */
strategies.push(autoComplete.createTemplateStrategy());
textcomplete.register(strategies);
};
autoComplete.createTemplateStrategy = function() {
console.log("/* createTemplateStrategy - start */");
// Get text preceding text cursor,
// like https://github.com/yuku-t/textcomplete/blob/6f07195c47ace5e787cf7b46604b37a8bd5c6d30/src/textarea.js#L82
var getBeforeCursor = function(/**textarea*/) {
textarea = document.getElementById("wpTextbox1");
return textarea.selectionStart !== textarea.selectionEnd ? null : textarea.value.substring(0, textarea.selectionEnd);
};
var getLastTemplateOpenBracketPos = function(text) { // Assumes no "{{{" and "}}}" (perhaps ok even with these, anyway?), nor <nowiki> tags that would render "{{" or "}}" escaped/inactive,
var count = 0
, index = text.length - 2
, foundBracketPair
, chars;
while (index >= 0) {
chars = [text.charAt(index), text.charAt(index+1)];
///console.log(chars);
foundBracketPair = false;
if (chars[0] === '}' && chars[1] === '}') {
count++;
foundBracketPair = true;
} else if (chars[0] === '{' && chars[1] === '{') {
count--;
foundBracketPair = true;
}
if (count < 0) return index;
if (foundBracketPair) index -= 2; else index -= 1;
}
return -1;
};
var templateStrategy =
{
id: "Templates",
match: /(\|)([^\|]*)$/,
search: function (term, callback) {
console.log("/* Template parameter choice search - start */");
var text = getBeforeCursor();
console.log(" * Text before cursor: " + text);
text = text.slice(0, text.length - term.length);
console.log(" * Text before term: " + text);
var templateStartPos = getLastTemplateOpenBracketPos(text);
var templateBody = text.slice(templateStartPos + "{{".length);
console.log(" * templateBody: " + templateBody);
var templateName = templateBody.slice(0,templateBody.indexOf("|")); // Assumes stuff like {{Test|, not {{Test{{aTemplate}}|
console.log(" * templateName: " + templateName);
/*
* Remove the templates [TODO: and tables] (there are no unmatched template brackets within this string?)
* - Loop a regex removal of {{..}} pairs, inside out
* - There should be no unmatched pairs due to having called
* getLastTemplateOpenBracketPos(text)
* - Use "?" in the regex to "lazy" match, or else it matches
* the biggest {{...}}, which renders the following method
* non-functional
* - The match will only have one "}}" at the end, due to ltr
* lazy regex search?
* - Then remove the rightmost/innermost {{...}} that includes
* this "}}", by reversing so the pair is on the left side of
* the string, lazy matching, then reversing back
*/
var templateBodyTrimmed = templateBody;
while (templateBodyTrimmed != (templateBodyTrimmed = templateBodyTrimmed.replace(/\{\{[^]*?\}\}/g, function (match) {
var matchInner = match.slice(2, match.length - 2); // Remove outer braces
if (matchInner.includes("{{")) {
// Remove the last (inner) pair of "{{" and "}}"
return match.split("").reverse().join("") // Reverse
.replace(/\}\}[^]*?\{\{/, "") // Pairs become "}}...{{" when the string is reversed
.split("").reverse().join(""); //Reverse back
} else {
return "";
}
})
));
// Count the number of "|" in text to determine which number
// parameter (e.g. "{{templateName|param1|param2|currentparam" )
var paramNum = 0;
for (var i=0; i<templateBodyTrimmed.length; i++) if (templateBodyTrimmed.charAt(i) == '|') paramNum++;
console.log(" * paramNum: " + paramNum);
var paramArray = [];
if (templates[templateName] !== undefined) {
if (typeof templates[templateName].getParams === "function") {
paramArray = templates[templateName].getParams(paramNum);
} else {
paramArray = templates[templateName][paramNum];
}
}
console.log(" * paramArray: " + paramArray);
autoComplete.currentHeader = templateName + " " + paramNum; // testing out header
var nameArray = paramArray.filter(function(currentValue) { return currentValue.startsWith(term); });
console.log(" * nameArray: " + paramArray);
console.log("/* Template parameter choice search - end */");
callback(nameArray); // List of possible completions ("names")
},
template: function (name) {
var displayName = name;
return displayName; // What to display in the list
},
replace: function (name) {
var replacementString = "$1" + name;
return replacementString; // What to replace the matched typed text with
}
};
console.log("/* createTemplateStrategy - end */");
return templateStrategy;
};
/* Load Textcomplete then register the strategies */
$(document).ready(autoComplete.getTextcompleteScript);
}