User:Erutuon/scripts/LanguageSearcher.js
< User:Erutuon | scripts
Note: You may have to bypass your browser’s cache to see the changes. In addition, after saving a sitewide CSS file such as MediaWiki:Common.css, it will take 5-10 minutes before the changes take effect, even if you clear your cache.
- Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
- Konqueror and Chrome: click Reload or press F5;
- Opera: clear the cache in Tools → Preferences;
- Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.
- This script lacks a documentation subpage. Please create it.
- Useful links: root page • root page’s subpages • links • redirects • your own
// have to load apiWrapper before loading this!
// $.getScript("//en.wiktionary.org/w/index.php?title=User:Erutuon/scripts/apiWrapper.js&action=raw")
/* jshint boss: true, esversion: 6, undef: true */
/* globals apiWrapper, mw */
// <nowiki>
(function () {
"use strict";
function makeWikiLink(pageName, text) {
const link = document.createElement("a");
link.href = mw.util.getUrl(pageName);
link.textContent = text || pageName;
return link;
}
function LanguageSearchResult(name, code, index, words) {
this.name = name;
this.code = code;
this.index = index;
this.words = words;
}
LanguageSearchResult.prototype = {
get categoryLink() {
// Based on getCategoryName in [[Module:languages]].
const categoryName = this.name.includes("Language")
? this.name : this.name + " language";
return makeWikiLink("Category:" + categoryName, this.name);
},
get codeElement() {
const codeElement = document.createElement("code");
codeElement.textContent = this.code;
return codeElement;
},
};
LanguageSearchResult.prototype.print = function () {
const listItem = document.createElement("li");
for (const element of [
this.categoryLink,
document.createTextNode(" ("),
this.codeElement,
document.createTextNode(")")
])
listItem.appendChild(element);
return listItem;
};
function LanguageSearcher(storageKey, handleResults, resultSorter) {
this.storageKey = storageKey;
this.handleResults = handleResults;
this.resultSorter = resultSorter;
}
LanguageSearcher.prototype = {
get storage () {
return mw.storage.get(this.storageKey);
},
set storage (JSON) {
mw.storage.set(this.storageKey, JSON);
},
};
LanguageSearcher.prototype.getResults = function (query) {
const results = [];
query = query.toLowerCase();
for (const [ name, code ] of Object.entries(window[this.storageKey])) {
const lowerName = name.toLowerCase();
let index = lowerName.indexOf(query);
if (index === -1)
index = code.indexOf(query);
if (index !== -1) {
const words = lowerName.split(/[- ()]+/).concat(code.split("-"));
results.push(new LanguageSearchResult(name, code, index, words));
}
}
return results;
};
LanguageSearcher.defaultResultSorter = query => (result1, result2) => {
// put results in the following groups:
// 1. exact code matches
if (result1.code === query || result2.code === query)
return Number(result2.code === query);
// 2. exact matches when "-pro" is added
if (result1.code === query + "-pro" || result2.code === query + "-pro")
return Number(result2.code === query + "-pro");
// 3. exact name matches
if (result1.name === query || result2.name === query)
return Number(result2.name === query);
// 4. prefix matches
if ((result1.index === 0) !== (result2.index === 0))
return result1.index - result2.index;
// 5. whole word matches
if (result1.words.includes(query) !== result2.words.includes(query))
return Number(result2.words.includes(query));
// within each group, alphabetize
return result1.name > result2.name ? 1
: result1.name < result2.name ? -1
: 0;
};
LanguageSearcher.prototype.handleQuery = function (query) {
if (typeof query !== "string")
throw new TypeError("Expected string");
const results = query !== ""
? this.getResults(query)
: [];
if (query !== "")
results.sort(this.resultSorter
? this.resultSorter(query)
: LanguageSearcher.defaultResultSorter(query));
this.handleResults(results);
};
// return true if data was successfully converted from JSON
LanguageSearcher.prototype.loadData = function (nameToCodeJSON) {
if (nameToCodeJSON) {
if (typeof nameToCodeJSON !== "string")
throw new TypeError("Expected string");
try {
const data = JSON.parse(nameToCodeJSON);
if (typeof data !== "object")
throw new TypeError("JSON should decode to an object");
window[this.storageKey] = data;
return true;
} catch (error) {
console.log(error);
}
}
return false;
};
// name-to-code map process:
// get JSON from Lua => save JSON in localStorage => save object as global variable
LanguageSearcher.prototype.doSearch = function (query) {
if (query === "" || window[this.storageKey] || this.loadData(this.storage)) {
this.handleQuery(query);
return;
}
apiWrapper.expandTemplates(
"{{#invoke:languages/javascript-interface|AllCanonicalToCode}}",
JSON => {
this.storage = JSON;
this.loadData(JSON);
this.handleQuery(query);
});
};
window.LanguageSearcher = LanguageSearcher;
})();
// </nowiki>