gridtracker/package.nw/lib/jquery.i18n.parser.js

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));