kopia lustrzana https://gitlab.com/gridtracker.org/gridtracker
355 wiersze
8.9 KiB
JavaScript
355 wiersze
8.9 KiB
JavaScript
/* eslint-disable no-tabs */
|
|
/*!
|
|
* jQuery Internationalization library
|
|
*
|
|
* Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar
|
|
*
|
|
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
|
|
* anything special to choose one license or the other and you don't have to
|
|
* notify anyone which license you are using. You are free to use
|
|
* UniversalLanguageSelector in commercial projects as long as the copyright
|
|
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
|
|
*
|
|
* @licence GNU General Public Licence 2.0 or later
|
|
* @licence MIT License
|
|
*/
|
|
|
|
(function ($)
|
|
{
|
|
"use strict";
|
|
|
|
var MessageParser = function (options)
|
|
{
|
|
this.options = $.extend({}, $.i18n.parser.defaults, options);
|
|
this.language = $.i18n.languages[String.locale] || $.i18n.languages.default;
|
|
this.emitter = $.i18n.parser.emitter;
|
|
};
|
|
|
|
MessageParser.prototype = {
|
|
|
|
constructor: MessageParser,
|
|
|
|
simpleParse: function (message, parameters)
|
|
{
|
|
return message.replace(/\$(\d+)/g, function (str, match)
|
|
{
|
|
var index = parseInt(match, 10) - 1;
|
|
|
|
return parameters[index] !== undefined ? parameters[index] : "$" + match;
|
|
});
|
|
},
|
|
|
|
parse: function (message, replacements)
|
|
{
|
|
if (message.indexOf("{{") < 0)
|
|
{
|
|
return this.simpleParse(message, replacements);
|
|
}
|
|
|
|
this.emitter.language = $.i18n.languages[$.i18n().locale] ||
|
|
$.i18n.languages.default;
|
|
|
|
return this.emitter.emit(this.ast(message), replacements);
|
|
},
|
|
|
|
ast: function (message)
|
|
{
|
|
var pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral,
|
|
regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar,
|
|
escapedOrRegularLiteral, templateContents, templateName, openTemplate,
|
|
closeTemplate, expression, paramExpression, result,
|
|
pos = 0;
|
|
|
|
// Try parsers until one works, if none work return null
|
|
function choice(parserSyntax)
|
|
{
|
|
return function ()
|
|
{
|
|
var i, result;
|
|
|
|
for (i = 0; i < parserSyntax.length; i++)
|
|
{
|
|
result = parserSyntax[i]();
|
|
|
|
if (result !== null)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
}
|
|
|
|
// Try several parserSyntax-es in a row.
|
|
// All must succeed; otherwise, return null.
|
|
// This is the only eager one.
|
|
function sequence(parserSyntax)
|
|
{
|
|
var i, res,
|
|
originalPos = pos,
|
|
result = [];
|
|
|
|
for (i = 0; i < parserSyntax.length; i++)
|
|
{
|
|
res = parserSyntax[i]();
|
|
|
|
if (res === null)
|
|
{
|
|
pos = originalPos;
|
|
|
|
return null;
|
|
}
|
|
|
|
result.push(res);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Run the same parser over and over until it fails.
|
|
// Must succeed a minimum of n times; otherwise, return null.
|
|
function nOrMore(n, p)
|
|
{
|
|
return function ()
|
|
{
|
|
var originalPos = pos,
|
|
result = [],
|
|
parsed = p();
|
|
|
|
while (parsed !== null)
|
|
{
|
|
result.push(parsed);
|
|
parsed = p();
|
|
}
|
|
|
|
if (result.length < n)
|
|
{
|
|
pos = originalPos;
|
|
|
|
return null;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
}
|
|
|
|
// Helpers -- just make parserSyntax out of simpler JS builtin types
|
|
|
|
function makeStringParser(s)
|
|
{
|
|
var len = s.length;
|
|
|
|
return function ()
|
|
{
|
|
var result = null;
|
|
|
|
if (message.slice(pos, pos + len) === s)
|
|
{
|
|
result = s;
|
|
pos += len;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
}
|
|
|
|
function makeRegexParser(regex)
|
|
{
|
|
return function ()
|
|
{
|
|
var matches = message.slice(pos).match(regex);
|
|
|
|
if (matches === null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
pos += matches[0].length;
|
|
|
|
return matches[0];
|
|
};
|
|
}
|
|
|
|
pipe = makeStringParser("|");
|
|
colon = makeStringParser(":");
|
|
backslash = makeStringParser("\\");
|
|
anyCharacter = makeRegexParser(/^./);
|
|
dollar = makeStringParser("$");
|
|
digits = makeRegexParser(/^\d+/);
|
|
regularLiteral = makeRegexParser(/^[^{}[\]$\\]/);
|
|
regularLiteralWithoutBar = makeRegexParser(/^[^{}[\]$\\|]/);
|
|
regularLiteralWithoutSpace = makeRegexParser(/^[^{}[\]$\s]/);
|
|
|
|
// There is a general pattern:
|
|
// parse a thing;
|
|
// if it worked, apply transform,
|
|
// otherwise return null.
|
|
// But using this as a combinator seems to cause problems
|
|
// when combined with nOrMore().
|
|
// May be some scoping issue.
|
|
function transform(p, fn)
|
|
{
|
|
return function ()
|
|
{
|
|
var result = p();
|
|
|
|
return result === null ? null : fn(result);
|
|
};
|
|
}
|
|
|
|
// Used to define "literals" within template parameters. The pipe
|
|
// character is the parameter delimeter, so by default
|
|
// it is not a literal in the parameter
|
|
function literalWithoutBar()
|
|
{
|
|
var result = nOrMore(1, escapedOrLiteralWithoutBar)();
|
|
|
|
return result === null ? null : result.join("");
|
|
}
|
|
|
|
function literal()
|
|
{
|
|
var result = nOrMore(1, escapedOrRegularLiteral)();
|
|
|
|
return result === null ? null : result.join("");
|
|
}
|
|
|
|
function escapedLiteral()
|
|
{
|
|
var result = sequence([backslash, anyCharacter]);
|
|
|
|
return result === null ? null : result[1];
|
|
}
|
|
|
|
choice([escapedLiteral, regularLiteralWithoutSpace]);
|
|
escapedOrLiteralWithoutBar = choice([escapedLiteral, regularLiteralWithoutBar]);
|
|
escapedOrRegularLiteral = choice([escapedLiteral, regularLiteral]);
|
|
|
|
function replacement()
|
|
{
|
|
var result = sequence([dollar, digits]);
|
|
|
|
if (result === null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return ["REPLACE", parseInt(result[1], 10) - 1];
|
|
}
|
|
|
|
templateName = transform(
|
|
// see $wgLegalTitleChars
|
|
// not allowing : due to the need to catch "PLURAL:$1"
|
|
makeRegexParser(/^[ !"$&'()*,./0-9;=?@A-Z^_`a-z~\x80-\xFF+-]+/),
|
|
|
|
function (result)
|
|
{
|
|
return result.toString();
|
|
}
|
|
);
|
|
|
|
function templateParam()
|
|
{
|
|
var expr,
|
|
result = sequence([pipe, nOrMore(0, paramExpression)]);
|
|
|
|
if (result === null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
expr = result[1];
|
|
|
|
// use a "CONCAT" operator if there are multiple nodes,
|
|
// otherwise return the first node, raw.
|
|
return expr.length > 1 ? ["CONCAT"].concat(expr) : expr[0];
|
|
}
|
|
|
|
function templateWithReplacement()
|
|
{
|
|
var result = sequence([templateName, colon, replacement]);
|
|
|
|
return result === null ? null : [result[0], result[2]];
|
|
}
|
|
|
|
function templateWithOutReplacement()
|
|
{
|
|
var result = sequence([templateName, colon, paramExpression]);
|
|
|
|
return result === null ? null : [result[0], result[2]];
|
|
}
|
|
|
|
templateContents = choice([
|
|
function ()
|
|
{
|
|
var res = sequence([
|
|
// templates can have placeholders for dynamic
|
|
// replacement eg: {{PLURAL:$1|one car|$1 cars}}
|
|
// or no placeholders eg:
|
|
// {{GRAMMAR:genitive|{{SITENAME}}}
|
|
choice([templateWithReplacement, templateWithOutReplacement]),
|
|
nOrMore(0, templateParam)
|
|
]);
|
|
|
|
return res === null ? null : res[0].concat(res[1]);
|
|
},
|
|
function ()
|
|
{
|
|
var res = sequence([templateName, nOrMore(0, templateParam)]);
|
|
|
|
if (res === null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return [res[0]].concat(res[1]);
|
|
}
|
|
]);
|
|
|
|
openTemplate = makeStringParser("{{");
|
|
closeTemplate = makeStringParser("}}");
|
|
|
|
function template()
|
|
{
|
|
var result = sequence([openTemplate, templateContents, closeTemplate]);
|
|
|
|
return result === null ? null : result[1];
|
|
}
|
|
|
|
expression = choice([template, replacement, literal]);
|
|
paramExpression = choice([template, replacement, literalWithoutBar]);
|
|
|
|
function start()
|
|
{
|
|
var result = nOrMore(0, expression)();
|
|
|
|
if (result === null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return ["CONCAT"].concat(result);
|
|
}
|
|
|
|
result = start();
|
|
|
|
/*
|
|
* For success, the pos must have gotten to the end of the input
|
|
* and returned a non-null.
|
|
* n.b. This is part of language infrastructure, so we do not throw an
|
|
* internationalizable message.
|
|
*/
|
|
if (result === null || pos !== message.length)
|
|
{
|
|
throw new Error("Parse error at position " + pos.toString() + " in input: " + message);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
};
|
|
|
|
$.extend($.i18n.parser, new MessageParser());
|
|
}(jQuery));
|