Commit 778e3994 authored by µKöff's avatar µKöff

Changes to syntax - now using PEG.js with a formal grammar;

Code style improvements
parent 1e8b4b80
Pipeline #96391789 passed with stages
in 2 minutes and 19 seconds
svg.railroad-diagram path {
stroke-width: 2;
stroke: black;
fill: none;
}
svg.railroad-diagram text {
font: sans-serif 14px monospace;
text-anchor: middle;
cursor: pointer;
}
svg.railroad-diagram text.label {
text-anchor: start;
}
svg.railroad-diagram text.comment {
font: italic 12px monospace;
}
svg.railroad-diagram rect {
stroke-width: 2;
stroke: black;
fill: #eee;
}
textarea {
width: 100%;
}
......
{
class BinaryCondition {
constructor(operator, leftOperand, rightOperand) {
this._type = "BinaryCondition";
this.operator = operator;
this.leftOperand = leftOperand;
this.rightOperand = rightOperand;
}
}
class UnaryCondition {
constructor(operator, operand) {
this._type = "UnaryCondition";
this.operator = operator;
this.operand = operand;
}
}
class Literal {
constructor(literal) {
this._type = "Literal";
this.literal = literal;
}
}
class RequestTuple {
constructor(term, conditionList, propertyList) {
this._type = "RequestTuple";
this.term = term;
this.conditionList = conditionList;
this.propertyList = propertyList;
}
}
}
CommandList
= cmdHead:CommandLine cmdTail:("\n" _cmdTail:CommandList {return _cmdTail})?
{
var cmdList = (cmdTail === null ? [] : cmdTail);
cmdList.unshift(cmdHead);
return cmdList;
}
CommandLine
= cmdHead:CommandLineItem cmdTail:("\t" _cmdTail:CommandLine {return _cmdTail;})?
{
var cmdList = (cmdTail === null ? [] : cmdTail);
cmdList.unshift(cmdHead);
return cmdList;
}
CommandLineItem = Literal / RequestTuple
Literal
= "$" literal:([^$\t\n]+)
{
return new options.Literal(literal);
}
RequestTuple
= searchTerm:SearchTerm
condProp:("|"
_conditions:ConditionList?
properties:("|" _properties:PropertyList {return _properties;})?
{return [_conditions, properties]}
)?
{
return new options.RequestTuple(searchTerm, condProp === null ? null : condProp[0], condProp === null ? null : condProp[1]);
}
ConditionList
= condHead:Condition condTail:("," _condTail:ConditionList {return _condTail;})?
{
var condList = (condTail === null ? [] : condTail);
condList.unshift(condHead);
return condList;
}
Condition
= unaryCondition:UnaryCondition / binaryCondition:BinaryCondition
BinaryCondition
= leftOperand:SearchTerm operator:BinaryConditionOperator rightOperand:SearchTerm
{
return new options.BinaryCondition(operator, leftOperand, rightOperand);
}
BinaryConditionOperator = "=" / "*=" / "~"
UnaryCondition
= operator:UnaryConditionOperator operand:SearchTerm
{
return new options.UnaryCondition(operator, operand);
}
UnaryConditionOperator = "^"
PropertyList
= propHead:PropertyAssignment propTail:("," _propTail:PropertyList {return _propTail;})?
{
if(propTail === null) {
return propHead;
} else {
return {...propHead, ...propTail};
}
}
PropertyAssignment
= key:PropertyKey "=" value:PropertyValue
{
var dict = ({});
dict[key] = value;
return dict;
}
PropertyKey
= chars:[a-zA-Z0-9_]+
{
return chars.join("").toLowerCase();
}
PropertyValue
= chars:[a-zA-Z0-9_ ]+
{
return chars.join("");
}
SearchTerm
= chars:([^\t\n\r\f\|\\=~^] / "\\\\" / "\\=" / "\\~" / "\\~" / "\\^")+
{
return chars.join("");
}
[
[
"Berlin|P17=Q30|type=Q",
"Bremen|P17=Q30",
"Hamburg|P17=Q30",
"Manheim|P17=Q30",
"Stuttgart|P17=Q30"
], [
"Saransk|^dewikivoyage|type=Q",
"Sekretaj Sonetoj|^eowikisource",
"Slavery|^commonswiki",
"Sphaleron|^dewiki",
"Supernatural|^frwikiquote"
]
]
\ No newline at end of file
["aa","ab","ace","ady","ady-cyrl","aeb","aeb-arab","aeb-latn","af","ak","aln","als","am","an","ang","anp","ar","arc","arn","arq","ary","arz","as","ase","ast","atj","av","avk","awa","ay","az","azb","ba","ban","bar","bat-smg","bbc","bbc-latn","bcc","bcl","be","be-tarask","be-x-old","bg","bgn","bh","bho","bi","bjn","bm","bn","bo","bpy","bqi","br","brh","bs","bto","bug","bxr","ca","cbk-zam","cdo","ce","ceb","ch","cho","chr","chy","ckb","co","cps","cr","crh","crh-cyrl","crh-latn","cs","csb","cu","cv","cy","da","de","de-at","de-ch","de-formal","din","diq","dsb","dtp","dty","dv","dz","ee","egl","el","eml","en","en-ca","en-gb","eo","es","es-formal","et","eu","ext","fa","ff","fi","fit","fiu-vro","fj","fo","fr","frc","frp","frr","fur","fy","ga","gag","gan","gan-hans","gan-hant","gcr","gd","gl","glk","gn","gom","gom-deva","gom-latn","gor","got","grc","gsw","gu","gv","ha","hak","haw","he","hi","hif","hif-latn","hil","ho","hr","hrx","hsb","ht","hu","hu-formal","hy","hz","ia","id","ie","ig","ii","ik","ike-cans","ike-latn","ilo","inh","io","is","it","iu","ja","jam","jbo","jut","jv","ka","kaa","kab","kbd","kbd-cyrl","kbp","kea","kg","khw","ki","kiu","kj","kk","kk-arab","kk-cn","kk-cyrl","kk-kz","kk-latn","kk-tr","kl","km","kn","ko","ko-kp","koi","kr","krc","kri","krj","krl","ks","ks-arab","ks-deva","ksh","ku","ku-arab","ku-latn","kum","kv","kw","ky","la","lad","lb","lbe","lez","lfn","lg","li","lij","liv","lki","lmo","ln","lo","loz","lrc","lt","ltg","lus","luz","lv","lzh","lzz","mai","map-bms","mdf","mg","mh","mhr","mi","min","mk","ml","mn","mo","mr","mrj","ms","mt","mus","mwl","my","myv","mzn","na","nah","nan","nap","nb","nds","nds-nl","ne","new","ng","niu","nl","nl-informal","nn","no","nod","nov","nrm","nso","nv","ny","nys","oc","olo","om","or","os","ota","pa","pag","pam","pap","pcd","pdc","pdt","pfl","pi","pih","pl","pms","pnb","pnt","prg","ps","pt","pt-br","qu","qug","rgn","rif","rm","rmy","rn","ro","roa-rup","roa-tara","ru","rue","rup","ruq","ruq-cyrl","ruq-latn","rw","rwr","sa","sah","sat","sc","scn","sco","sd","sdc","sdh","se","sei","ses","sg","sgs","sh","shi","shi-latn","shi-tfng","shn","si","simple","sje","sk","skr","skr-arab","sl","sli","sm","sma","smj","sn","so","sq","sr","sr-ec","sr-el","srn","srq","ss","st","stq","sty","su","sv","sw","szl","ta","tay","tcy","te","tet","tg","tg-cyrl","tg-latn","th","ti","tk","tl","tly","tn","to","tpi","tr","tru","ts","tt","tt-cyrl","tt-latn","tum","tw","ty","tyv","tzm","udm","ug","ug-arab","ug-latn","uk","ur","uz","uz-cyrl","uz-latn","ve","vec","vep","vi","vls","vmf","vo","vot","vro","wa","war","wo","wuu","xal","xh","xmf","yi","yo","yue","za","zea","zh","zh-classical","zh-cn","zh-hans","zh-hant","zh-hk","zh-min-nan","zh-mo","zh-my","zh-sg","zh-tw","zh-yue","zu"]
\ No newline at end of file
This diff is collapsed.
import Condition from "./condition";
export default class BinaryCondition extends Condition {
leftOperand: string;
rightOperand: string;
constructor(operator: string, leftOperand: string, rightOperand: string) {
super(operator);
this.leftOperand = leftOperand;
this.rightOperand = rightOperand;
}
generateCondition(): string {
switch(this.operator) {
case "=":
return `?item wdt:${this.leftOperand} wd:${this.rightOperand} .`;
case "*=":
return `?item wdt:${this.leftOperand}* wd:${this.rightOperand} .`;
case "~":
return `?item ${this.leftOperand} ${this.rightOperand} .`;
default:
return null;
}
}
}
\ No newline at end of file
import Tuple from './tuple'
import RequestTuple from "./requestTuple";
export default class Condition {
mode : string
property : string
value : string
export default abstract class Condition {
operator: string;
constructor(mode : string, property : string, value : string) {
this.mode = mode;
this.property = property;
this.value = value;
protected constructor(operator: string) {
this.operator = operator;
}
static extendData(oldItem : any, newItem : any, langCode : string) {
static extendData(oldItem: any, newItem: any, langCode: string) {
if(typeof newItem.descriptions[langCode] != "undefined") oldItem.description = newItem.descriptions[langCode].value;
if(typeof newItem.labels[langCode] != "undefined") oldItem.label = newItem.labels[langCode].value;
}
static filterItem(tuple : Tuple, elements : any[], results : any, langCode : string, callback : (filteredItems : any) => void) {
static filterItem(tuple: RequestTuple, elements: any[], results: any, langCode: string, callback: (filteredItems: any) => void) {
var elementsNew = elements.slice(0);
$.ajax({
......@@ -44,37 +41,35 @@ export default class Condition {
callback(elements); // Don't filter
});
function generateConditions(tuple : Tuple) {
var rules : string[] = [];
$.each(<Condition[]>tuple.conditions, function(_, b) {
switch(b.mode) {
case "=":
rules.push(`?item wdt:${b.property} wd:${b.value} .`);
break;
case "~":
rules.push(`?item ${b.property} ${b.value} .`);
break;
function generateConditions(tuple: RequestTuple) {
var rules: string[] = [];
$.each(<Condition[]>tuple.conditionList, function(_, condition) {
if(condition.generateCondition() != null) {
rules.push(condition.generateCondition());
}
});
return rules;
}
function sparqlResultToIdList(results : any) {
var idList : string[] = [];
function sparqlResultToIdList(results: any) {
var idList: string[] = [];
$.each(results.results.bindings, function(a, b) {
idList.push(b.item.value.replace("http://www.wikidata.org/entity/", ""));
});
return idList;
}
function resultsToIdList(results : any) {
var idList : string[] = [];
function resultsToIdList(results: any) {
var idList: string[] = [];
$.each(results.entities, function(index, candidate) {
idList.push(`wd:${candidate.id}`);
});
return idList;
}
}
static filterItems(tuple : Tuple, elements : any[], langCode : string, callback: (filteredElements: any[]) => void) {
var ids : string[] = [];
static filterItems(tuple: RequestTuple, elements: any[], langCode: string, callback: (filteredElements: any[]) => void) {
var ids: string[] = [];
$.each(elements, function(index, item) {
ids.push(item.id);
});
......@@ -89,7 +84,7 @@ export default class Condition {
},
url: "https://www.wikidata.org/w/api.php"
}).done(function(e) {
if(tuple.conditions.length > 0) {
if(tuple.conditionList.length > 0) {
Condition.filterItem(tuple, elements, e, langCode, callback);
} else {
callback(elements);
......@@ -99,21 +94,6 @@ export default class Condition {
callback(elements);
});
}
static parse(conditions : string) {
var rules = [];
if(conditions.startsWith("^")) {
rules.push(new Condition("^", "", conditions.substr(1)));
} else {
$.each(conditions.split(","), function(ruleIndex, ruleTerm) {
if(ruleTerm.split("=").length == 2) {
var pair = ruleTerm.split("=");
rules.push(new Condition("=", pair[0], pair[1]));
} else if(ruleTerm.split("~").length == 2) {
var pair = ruleTerm.split("~");
rules.push(new Condition("~", pair[0], pair[1]));
}
});
}
return rules;
}
abstract generateCondition(): string;
}
\ No newline at end of file
export default class Literal {
literal: string;
constructor(literal: string) {
this.literal = literal;
}
toString() {
return `$${this.literal}`;
}
}
\ No newline at end of file
import BinaryCondition from "./binaryCondition";
import Literal from "./literal";
import Peg from "pegjs";
import RequestTuple from "./requestTuple";
import UnaryCondition from "./unaryCondition";
export default class Parser {
private static _grammar: string = require("../data/grammar.pegjs.txt").default;
static get grammar(): string {
return Parser._grammar;
}
private parser: Peg.Parser;
constructor() {
this.parser = Peg.generate(Parser._grammar);
}
parse(input : string) {
return this.parser.parse(input, {
BinaryCondition: BinaryCondition,
Literal: Literal,
RequestTuple: RequestTuple,
UnaryCondition: UnaryCondition
});
}
}
\ No newline at end of file
export default class QuerySamples {
private static samples: string[][] = require("../data/querySamples.json");
static getRandomSample() {
return QuerySamples.samples[Math.floor(Math.random() * QuerySamples.samples.length)].join("\n");
}
}
\ No newline at end of file
declare module "*.txt" {
const content: any;
export default content;
}
\ No newline at end of file
import Condition from './condition'
import Settings from './settings'
import TaskQueue from './taskQueue'
import Tuple from './tuple'
import Util from './util'
import WikiUtils from './wikiUtils'
import Condition from './condition';
import RequestTuple from './requestTuple';
import Settings from './settings';
import TaskQueue from './taskQueue';
import Util from './util';
import WikiUtils from './wikiUtils';
import UnaryCondition from './unaryCondition';
export default class RequestManager {
requestQueue : TaskQueue<{tuple: Tuple, rowIndex: number, tupleIndex: number}>
requestQueue: TaskQueue<{tuple: RequestTuple, rowIndex: number, tupleIndex: number}>;
constructor() {
this.requestQueue = new TaskQueue();
}
enqueueRequest(tuple : Tuple, rowIndex : number, tupleIndex : number) {
enqueueRequest(tuple: RequestTuple, rowIndex: number, tupleIndex: number) {
this.requestQueue.enqueueTask(this._performRequest, {
tuple: tuple,
rowIndex: rowIndex,
......@@ -20,12 +21,11 @@ export default class RequestManager {
});
}
_performRequest(requestQueue : TaskQueue<{tuple: Tuple, rowIndex: number, tupleIndex: number}>, data : {tuple: Tuple, rowIndex: number, tupleIndex: number}) {
data.tuple.ui.status = "working";
data.tuple.ui.update();
_performRequest(requestQueue: TaskQueue<{tuple: RequestTuple, rowIndex: number, tupleIndex: number}>, data: {tuple: RequestTuple, rowIndex: number, tupleIndex: number}) {
data.tuple.uiRow.updateStatus("working");
if(data.tuple.conditions.length == 1 && (<Condition[]>data.tuple.conditions)[0].mode == "^") {
var host = WikiUtils.getHost((<Condition[]>data.tuple.conditions)[0].value);
if(data.tuple.conditionList.length == 1 && (<Condition[]>data.tuple.conditionList)[0].operator == "^") {
var host = WikiUtils.getHost((<UnaryCondition>(<Condition[]>data.tuple.conditionList)[0]).operand);
if(host != null) {
$.ajax({
data: {
......@@ -34,7 +34,7 @@ export default class RequestManager {
"prop": "pageprops",
"ppprop": "wikibase_item",
"redirects": "1",
"titles": data.tuple.searchquery,
"titles": data.tuple.term,
"origin": "*"
},
url: `https://${host}/w/api.php`
......@@ -43,21 +43,17 @@ export default class RequestManager {
}).done(function(e) {
console.debug(e);
if(Object.keys(e.query.pages)[0] == "-1") {
data.tuple.ui.status = "no results";
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("no results");
} else {
data.tuple.ui.status = "success";
data.tuple.result = e.query.pages[Object.keys(e.query.pages)[0]].pageprops.wikibase_item;
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("success");
data.tuple.uiRow.updateResult(e.query.pages[Object.keys(e.query.pages)[0]].pageprops.wikibase_item)
}
}).fail(function(e) {
data.tuple.ui.status = "error";
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("error");
console.error(e);
});
} else {
data.tuple.ui.status = "error";
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("error");
}
} else {
$.ajax({
......@@ -67,8 +63,8 @@ export default class RequestManager {
"language": $("#setting-language-search").val(),
"limit": Util.validateNumber(Settings.getDisambiguationCandidatesnumber(), 7),
"origin": "*",
"search": data.tuple.searchquery,
"type": data.tuple.type.toLowerCase() == "q" ? "item" : "property",
"search": data.tuple.term,
"type": data.tuple.type,
"uselang": $("#setting-language-search").val()
},
url: `https://www.wikidata.org/w/api.php`
......@@ -76,28 +72,23 @@ export default class RequestManager {
requestQueue.finishTask();
}).done(function(e) {
if(e.search.length == 0) {
data.tuple.ui.status = "no results";
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("no results");
} else {
Condition.filterItems(data.tuple, e.search, Settings.getLanguageItem(), finalize);
}
}).fail(function(e) {
data.tuple.ui.status = "error";
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("error");
console.error(e);
});
}
function finalize(items : any[]) {
function finalize(items: any[]) {
if(items.length == 1) {
data.tuple.ui.status = "success";
data.tuple.result = items[0].id;
data.tuple.ui.generateDisambiguation(items);
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("success");
data.tuple.uiRow.updateResult(items[0].id);
} else {
data.tuple.ui.status = "disambiguation";
data.tuple.ui.generateDisambiguation(items);
data.tuple.ui.update();
data.tuple.uiRow.updateStatus("disambiguation");
data.tuple.uiRow.generateDisambiguation(items);
}
}
}
......
import Condition from "./condition";
import UiRow from "./uiRow";
import RequestManager from "./requestManager";
type PropertyDictionary = { [key: string]: string };
export default class RequestTuple {
private _conditionList: Condition[];
get conditionList(): Condition[] {
return this._conditionList;
}
private note: string;
private _term: string;
get term(): string {
return this._term;
}
private propertyDict: PropertyDictionary;
private _result: string;
get result(): string {
return this._result;
}
set result(val: string) {
this._result = val;
}
private _type: string;
get type(): string {
return this._type;
}
private _uiRow: UiRow;
get uiRow(): UiRow {
return this._uiRow;
}
set uiRow(val: UiRow) {
this._uiRow = val;
}
constructor(term: string, conditionList: Condition[], propertyDict: PropertyDictionary) {
this._term = term;
this._conditionList = conditionList || [];
this.propertyDict = propertyDict || {};
this._type = this.normalizeAndGetProperty("type", propertyDict, function(val: string) {
switch(val.toLowerCase()) {
case "f":
return "form";
case "l":
return "lexeme";
case "p":
return "property";
case "q":
return "item";
case "s":
return "sense";
default:
return val;
}
}, "item")
}
getProperty(key: string, propertyDict: PropertyDictionary, defaultTo: string = null) {
return this.hasProperty(key, propertyDict) ? propertyDict[key] : defaultTo;
}
hasProperty(key: string, propertyDict: PropertyDictionary) {
return propertyDict != null && typeof propertyDict[key] == "string";
}
normalizeProperty(key: string, propertyDict: PropertyDictionary, normalizeFunction: ((value: string) => string)): string {
if(this.hasProperty(key, propertyDict)) {
var oldValue = propertyDict[key];
propertyDict[key] = normalizeFunction(propertyDict[key]);
return oldValue;
} else {
return null;
}
}
normalizeAndGetProperty(key: string, propertyDict: PropertyDictionary, normalizeFunction: ((value: string) => string), defaultTo: string = null): string {
this.normalizeProperty(key, propertyDict, normalizeFunction);
return this.getProperty(key, propertyDict, defaultTo);
}
request(requestManager: RequestManager, rowIndex: number, tupleIndex: number) {
requestManager.enqueueRequest(this, rowIndex, tupleIndex);
}
}
\ No newline at end of file
import Util from './util'
require("tooltipster")
require("tooltipster/dist/css/tooltipster.bundle.css")
require("tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-light.min.css")
import Util from './util';
require("tooltipster");
require("tooltipster/dist/css/tooltipster.bundle.css");
require("tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-light.min.css");
export default class Settings {
static attachLiveHandler() {
......@@ -27,13 +27,16 @@ export default class Settings {
$("#setting-candidateremoval").change();
$("#setting-disambiguation-matchhighlight").change();
}
static disableConditionalStyle(name : string) {
static disableConditionalStyle(name: string) {
var element = $(`style[data-setting='${name}']`);
if(element.length != 0) element.remove();
}
static enableConditionalStyle(name : string, style : string) {
static enableConditionalStyle(name: string, style: string) {
if($(`style[data-setting='${name}']`).length == 0) $("head").append(`<style data-setting="${name}">${style}</style>`);
}
static export() {
var settings = {
"candidateremoval": $("#setting-candidateremoval").is(":checked"),
......@@ -44,12 +47,15 @@ export default class Settings {
};
return settings;
}
static getDisambiguationCandidatesnumber() {
return Util.validateNumber(<number>$("#setting-disambiguation-candidatesnumber").val(), 7);
}
static getLanguageItem() {
return <string>$("#setting-language-item").val();
}
static initialize() {
$("#button-settings-geturl").click(function(e) {
e.preventDefault();
......@@ -87,6 +93,7 @@ export default class Settings {
Settings.load();
Settings.attachLiveHandler();
}
static load() {
var localstorageSettings = localStorage.getItem("wikiRsolve.settings");
if(localstorageSettings != null) {
......@@ -106,7 +113,7 @@ export default class Settings {
}
}
function apply(settings : any) {
function apply(settings: any) {
assign(settings, "candidateremoval", x => $("#setting-candidateremoval").prop("checked", x));
assign(settings, "disambiguation-candidatesnumber", x => $("#setting-disambiguation-candidatesnumber").val(x));
assign(settings, "disambiguation-matchhighlight", x => $("#setting-disambiguation-matchhighlight").prop("checked", x));
......@@ -114,15 +121,17 @@ export default class Settings {
assign(settings, "language-search", x => $("#setting-language-search").val(x));
}
function assign(settings : any, key : string, assignFunction : (value : string) => void) {
function assign(settings: any, key: string, assignFunction: (value: string) => void) {
if(typeof settings[key] != "undefined") assignFunction(settings[key]);
}
}
static loadLanguages() { // https://www.wikidata.org/w/api.php?action=help&modules=wbsearchentities
var lang = ["aa","ab","ace","ady","ady-cyrl","aeb","aeb-arab","aeb-latn","af","ak","aln","als","am","an","ang","anp","ar","arc","arn","arq","ary","arz","as","ase","ast","atj","av","avk","awa","ay","az","azb","ba","ban","bar","bat-smg","bbc","bbc-latn","bcc","bcl","be","be-tarask","be-x-old","bg","bgn","bh","bho","bi","bjn","bm","bn","bo","bpy","bqi","br","brh","bs","bto","bug","bxr","ca","cbk-zam","cdo","ce","ceb","ch","cho","chr","chy","ckb","co","cps","cr","crh","crh-cyrl","crh-latn","cs","csb","cu","cv","cy","da","de","de-at","de-ch","de-formal","din","diq","dsb","dtp","dty","dv","dz","ee","egl","el","eml","en","en-ca","en-gb","eo","es","es-formal","et","eu","ext","fa","ff","fi","fit","fiu-vro","fj","fo","fr","frc","frp","frr","fur","fy","ga","gag","gan","gan-hans","gan-hant","gcr","gd","gl","glk","gn","gom","gom-deva","gom-latn","gor","got","grc","gsw","gu","gv","ha","hak","haw","he","hi","hif","hif-latn","hil","ho","hr","hrx","hsb","ht","hu","hu-formal","hy","hz","ia","id","ie","ig","ii","ik","ike-cans","ike-latn","ilo","inh","io","is","it","iu","ja","jam","jbo","jut","jv","ka","kaa","kab","kbd","kbd-cyrl","kbp","kea","kg","khw","ki","kiu","kj","kk","kk-arab","kk-cn","kk-cyrl","kk-kz","kk-latn","kk-tr","kl","km","kn","ko","ko-kp","koi","kr","krc","kri","krj","krl","ks","ks-arab","ks-deva","ksh","ku","ku-arab","ku-latn","kum","kv","kw","ky","la","lad","lb","lbe","lez","lfn","lg","li","lij","liv","lki","lmo","ln","lo","loz","lrc","lt","ltg","lus","luz","lv","lzh","lzz","mai","map-bms","mdf","mg","mh","mhr","mi","min","mk","ml","mn","mo","mr","mrj","ms","mt","mus","mwl","my","myv","mzn","na","nah","nan","nap","nb","nds","nds-nl","ne","new","ng","niu","nl","nl-informal","nn","no","nod","nov","nrm","nso","nv","ny","nys","oc","olo","om","or","os","ota","pa","pag","pam","pap","pcd","pdc","pdt","pfl","pi","pih","pl","pms","pnb","pnt","prg","ps","pt","pt-br","qu","qug","rgn","rif","rm","rmy","rn","ro","roa-rup","roa-tara","ru","rue","rup","ruq","ruq-cyrl","ruq-latn","rw","rwr","sa","sah","sat","sc","scn","sco","sd","sdc","sdh","se","sei","ses","sg","sgs","sh","shi","shi-latn","shi-tfng","shn","si","simple","sje","sk","skr","skr-arab","sl","sli","sm","sma","smj","sn","so","sq","sr","sr-ec","sr-el","srn","srq","ss","st","stq","sty","su","sv","sw","szl","ta","tay","tcy","te","tet","tg","tg-cyrl","tg-latn","th","ti","tk","tl","tly","tn","to","tpi","tr","tru","ts","tt","tt-cyrl","tt-latn","tum","tw","ty","tyv","tzm","udm","ug","ug-arab","ug-latn","uk","ur","uz","uz-cyrl","uz-latn","ve","vec","vep","vi","vls","vmf","vo","vot","vro","wa","war","wo","wuu","xal","xh","xmf","yi","yo","yue","za","zea","zh","zh-classical","zh-cn","zh-hans","zh-hant","zh-hk","zh-min-nan","zh-mo","zh-my","zh-sg","zh-tw","zh-yue","zu"];
static loadLanguages() {
// https://www.wikidata.org/w/api.php?action=help&modules=wbsearchentities
var lang: string[] = require("../data/wikiLanguageCodes.json");
$("#languages").html("");