/* global module */
/**
* @class MessageFormat
* @classdesc see [messageformat.js](https://github.com/SlexAxton/messageformat.js)
*/
function MessageFormat(locale, pluralFunc) {
var fallbackLocale;
if (locale && pluralFunc) {
MessageFormat.locale[locale] = pluralFunc;
}
// Defaults
fallbackLocale = locale = locale || "en";
pluralFunc = pluralFunc || MessageFormat.locale[fallbackLocale = MessageFormat.Utils.getFallbackLocale(locale)];
if (!pluralFunc) {
throw new Error("Plural Function not found for locale: " + locale);
}
// Own Properties
this.pluralFunc = pluralFunc;
this.locale = locale;
this.fallbackLocale = fallbackLocale;
}
// Set up the locales object. Add in english by default
MessageFormat.locale = {
"en": function(n) {
if (n === 1) {
return "one";
}
return "other";
}
};
// Build out our basic SafeString type
// more or less stolen from Handlebars by @wycats
MessageFormat.SafeString = function(string) {
this.string = string;
};
MessageFormat.SafeString.prototype.toString = function() {
return this.string.toString();
};
MessageFormat.Utils = {
numSub: function(string, key, depth) {
// make sure that it's not an escaped octothorpe
return string.replace(/^#|[^\\]#/g, function(m) {
var prefix = m && m.length === 2 ? m.charAt(0) : '';
return prefix + '" + (function(){ var x = ' +
key + ';\nif( isNaN(x) ){\nthrow new Error("MessageFormat: `"+lastkey_' + depth + '+"` isnt a number.");\n}\nreturn x;\n})() + "';
});
},
escapeExpression: function(string) {
var escape = {
"\n": "\\n",
"\"": '\\"'
},
badChars = /[\n"]/g,
possible = /[\n"]/,
escapeChar = function(chr) {
return escape[chr] || "&";
};
// Don't escape SafeStrings, since they're already safe
if (string instanceof MessageFormat.SafeString) {
return string.toString();
} else if (string === null || string === false) {
return "";
}
if (!possible.test(string)) {
return string;
}
return string.replace(badChars, escapeChar);
},
getFallbackLocale: function(locale) {
var tagSeparator = locale.indexOf("-") >= 0 ? "-" : "_";
// Lets just be friends, fallback through the language tags
while (!MessageFormat.locale.hasOwnProperty(locale)) {
locale = locale.substring(0, locale.lastIndexOf(tagSeparator));
if (locale.length === 0) {
return null;
}
}
return locale;
}
};
// This is generated and pulled in for browsers.
var mparser = (function() {
/* Generated by PEG.js 0.6.2 (http://pegjs.majda.cz/). */
var result = {
/*
* Parses the input with a generated parser. If the parsing is successfull,
* returns a value explicitly or implicitly specified by the grammar from
* which the parser was generated (see |PEG.buildParser|). If the parsing is
* unsuccessful, throws |PEG.parser.SyntaxError| describing the error.
*/
parse: function(input, startRule) {
var pos = 0;
var reportMatchFailures = true;
var rightmostMatchFailuresPos = 0;
var rightmostMatchFailuresExpected = [];
var cache = {};
var parse_elementFormat;
function padLeft(input, padding, length) {
var result = input;
var padLength = length - input.length;
for (var i = 0; i < padLength; i++) {
result = padding + result;
}
return result;
}
function escape(ch) {
var length, escapeChar,
charCode = ch.charCodeAt(0);
if (charCode <= 0xFF) {
escapeChar = 'x';
length = 2;
} else {
escapeChar = 'u';
length = 4;
}
return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
}
function quote(s) {
/*
* ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a
* string literal except for the closing quote character, backslash,
* carriage return, line separator, paragraph separator, and line feed.
* Any character may appear in the form of an escape sequence.
*/
return '"' + s
.replace(/\\/g, '\\\\') // backslash
.replace(/"/g, '\\"') // closing quote character
.replace(/\r/g, '\\r') // carriage return
.replace(/\n/g, '\\n') // line feed
.replace(/[\x80-\uFFFF]/g, escape) + // non-ASCII characters
'"';
}
function matchFailed(failure) {
if (pos < rightmostMatchFailuresPos) {
return;
}
if (pos > rightmostMatchFailuresPos) {
rightmostMatchFailuresPos = pos;
rightmostMatchFailuresExpected = [];
}
rightmostMatchFailuresExpected.push(failure);
}
function parse_whitespace() {
var cacheKey = 'whitespace@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var result0;
if (input.substr(pos).match(/^[ \n\r]/) !== null) {
result0 = input.charAt(pos);
pos++;
} else {
result0 = null;
if (reportMatchFailures) {
matchFailed("[ \\n\\r]");
}
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse__() {
var cacheKey = '_@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedReportMatchFailures = reportMatchFailures;
reportMatchFailures = false;
var savedPos0 = pos;
var result1 = [];
var result3 = parse_whitespace();
while (result3 !== null) {
result1.push(result3);
result3 = parse_whitespace();
}
var result2 = result1 !== null ?
(function(w) {
return w.join('');
})(result1) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
reportMatchFailures = savedReportMatchFailures;
if (reportMatchFailures && result0 === null) {
matchFailed("whitespace");
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_digits() {
var cacheKey = 'digits@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var result3;
if (input.substr(pos).match(/^[0-9]/) !== null) {
result3 = input.charAt(pos);
pos++;
} else {
result3 = null;
if (reportMatchFailures) {
matchFailed("[0-9]");
}
}
var result1;
if (result3 !== null) {
result1 = [];
while (result3 !== null) {
result1.push(result3);
if (input.substr(pos).match(/^[0-9]/) !== null) {
result3 = input.charAt(pos);
pos++;
} else {
result3 = null;
if (reportMatchFailures) {
matchFailed("[0-9]");
}
}
}
} else {
result1 = null;
}
var result2 = result1 !== null ?
(function(ds) {
return parseInt((ds.join('')), 10);
})(result1) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_offsetPattern() {
var cacheKey = 'offsetPattern@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result3 = parse__();
var result1;
if (result3 !== null) {
var result4;
if (input.substr(pos, 6) === "offset") {
result4 = "offset";
pos += 6;
} else {
result4 = null;
if (reportMatchFailures) {
matchFailed("\"offset\"");
}
}
if (result4 !== null) {
var result5 = parse__();
if (result5 !== null) {
var result6;
if (input.substr(pos, 1) === ":") {
result6 = ":";
pos += 1;
} else {
result6 = null;
if (reportMatchFailures) {
matchFailed("\":\"");
}
}
if (result6 !== null) {
var result7 = parse__();
if (result7 !== null) {
var result8 = parse_digits();
if (result8 !== null) {
var result9 = parse__();
if (result9 !== null) {
result1 = [result3, result4, result5, result6, result7, result8, result9];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result2 = result1 !== null ?
(function(d) {
return d;
})(result1[5]) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_id() {
var cacheKey = 'id@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result3 = parse__();
var result1;
if (result3 !== null) {
var result4;
if (input.substr(pos).match(/^[a-zA-Z$_]/) !== null) {
result4 = input.charAt(pos);
pos++;
} else {
result4 = null;
if (reportMatchFailures) {
matchFailed("[a-zA-Z$_]");
}
}
if (result4 !== null) {
var result5 = [];
var result7;
if (input.substr(pos).match(/^[^ \n\r,.+={}]/) !== null) {
result7 = input.charAt(pos);
pos++;
} else {
result7 = null;
if (reportMatchFailures) {
matchFailed("[^ \\n\\r,.+={}]");
}
}
while (result7 !== null) {
result5.push(result7);
if (input.substr(pos).match(/^[^ \n\r,.+={}]/) !== null) {
result7 = input.charAt(pos);
pos++;
} else {
result7 = null;
if (reportMatchFailures) {
matchFailed("[^ \\n\\r,.+={}]");
}
}
}
if (result5 !== null) {
var result6 = parse__();
if (result6 !== null) {
result1 = [result3, result4, result5, result6];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result2 = result1 !== null ?
(function(s1, s2) {
return s1 + (s2 ? s2.join('') : '');
})(result1[1], result1[2]) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_stringKey() {
var cacheKey = 'stringKey@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos2 = pos;
var result7 = parse_id();
var result8 = result7 !== null ?
(function(i) {
return i;
})(result7) :
null;
var result6;
if (result8 !== null) {
result6 = result8;
} else {
result6 = null;
pos = savedPos2;
}
var result0;
if (result6 !== null) {
result0 = result6;
} else {
var savedPos0 = pos;
var savedPos1 = pos;
var result4;
if (input.substr(pos, 1) === "=") {
result4 = "=";
pos += 1;
} else {
result4 = null;
if (reportMatchFailures) {
matchFailed("\"=\"");
}
}
var result2;
if (result4 !== null) {
var result5 = parse_digits();
if (result5 !== null) {
result2 = [result4, result5];
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
var result3 = result2 !== null ?
(function(d) {
return d;
})(result2[1]) :
null;
var result1;
if (result3 !== null) {
result1 = result3;
} else {
result1 = null;
pos = savedPos0;
}
if (result1 !== null) {
result0 = result1;
} else {
result0 = null;
}
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_hexDigit() {
var cacheKey = 'hexDigit@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var result0;
if (input.substr(pos).match(/^[0-9a-fA-F]/) !== null) {
result0 = input.charAt(pos);
pos++;
} else {
result0 = null;
if (reportMatchFailures) {
matchFailed("[0-9a-fA-F]");
}
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
var charPattern = "^[^{}\\\0- \n\r]", // jshint ignore:line
charPatternMatch = new RegExp("/" + charPattern + "/");
function parse_char() {
var cacheKey = 'char@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos5 = pos;
var result19;
if (input.substr(pos).match(charPattern) !== null) {
result19 = input.charAt(pos);
pos++;
} else {
result19 = null;
if (reportMatchFailures) {
matchFailed(charPattern);
}
}
var result20 = result19 !== null ?
(function(x) {
return x;
})(result19) :
null;
var result18;
if (result20 !== null) {
result18 = result20;
} else {
result18 = null;
pos = savedPos5;
}
var result0;
if (result18 !== null) {
result0 = result18;
} else {
var savedPos4 = pos;
var result16;
if (input.substr(pos, 2) === "\\#") {
result16 = "\\#";
pos += 2;
} else {
result16 = null;
if (reportMatchFailures) {
matchFailed("\"\\\\#\"");
}
}
var result17 = result16 !== null ?
(function() {
return "\\#";
})() :
null;
var result15;
if (result17 !== null) {
result15 = result17;
} else {
result15 = null;
pos = savedPos4;
}
if (result15 !== null) {
result0 = result15;
} else {
var savedPos3 = pos;
var result13;
if (input.substr(pos, 2) === "\\{") {
result13 = "\\{";
pos += 2;
} else {
result13 = null;
if (reportMatchFailures) {
matchFailed("\"\\\\{\"");
}
}
var result14 = result13 !== null ?
(function() {
return "\u007B";
})() :
null;
var result12;
if (result14 !== null) {
result12 = result14;
} else {
result12 = null;
pos = savedPos3;
}
if (result12 !== null) {
result0 = result12;
} else {
var savedPos2 = pos;
var result10;
if (input.substr(pos, 2) === "\\}") {
result10 = "\\}";
pos += 2;
} else {
result10 = null;
if (reportMatchFailures) {
matchFailed("\"\\\\}\"");
}
}
var result11 = result10 !== null ?
(function() {
return "\u007D";
})() :
null;
var result9;
if (result11 !== null) {
result9 = result11;
} else {
result9 = null;
pos = savedPos2;
}
if (result9 !== null) {
result0 = result9;
} else {
var savedPos0 = pos;
var savedPos1 = pos;
var result4;
if (input.substr(pos, 2) === "\\u") {
result4 = "\\u";
pos += 2;
} else {
result4 = null;
if (reportMatchFailures) {
matchFailed("\"\\\\u\"");
}
}
var result2;
if (result4 !== null) {
var result5 = parse_hexDigit();
if (result5 !== null) {
var result6 = parse_hexDigit();
if (result6 !== null) {
var result7 = parse_hexDigit();
if (result7 !== null) {
var result8 = parse_hexDigit();
if (result8 !== null) {
result2 = [result4, result5, result6, result7, result8];
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
var result3 = result2 !== null ?
(function(h1, h2, h3, h4) {
return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4));
})(result2[1], result2[2], result2[3], result2[4]) :
null;
var result1;
if (result3 !== null) {
result1 = result3;
} else {
result1 = null;
pos = savedPos0;
}
if (result1 !== null) {
result0 = result1;
} else {
result0 = null;
}
}
}
}
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_chars() {
var cacheKey = 'chars@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var result3 = parse_char();
var result1;
if (result3 !== null) {
result1 = [];
while (result3 !== null) {
result1.push(result3);
result3 = parse_char();
}
} else {
result1 = null;
}
var result2 = result1 !== null ?
(function(chars) {
return chars.join('');
})(result1) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_string() {
var cacheKey = 'string@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result3 = parse__();
var result1;
if (result3 !== null) {
var result4 = [];
var savedPos2 = pos;
var result6 = parse__();
var result8;
var result5;
var result7;
if (result6 !== null) {
result7 = parse_chars();
if (result7 !== null) {
result8 = parse__();
if (result8 !== null) {
result5 = [result6, result7, result8];
} else {
result5 = null;
pos = savedPos2;
}
} else {
result5 = null;
pos = savedPos2;
}
} else {
result5 = null;
pos = savedPos2;
}
while (result5 !== null) {
result4.push(result5);
savedPos2 = pos;
result6 = parse__();
if (result6 !== null) {
result7 = parse_chars();
if (result7 !== null) {
result8 = parse__();
if (result8 !== null) {
result5 = [result6, result7, result8];
} else {
result5 = null;
pos = savedPos2;
}
} else {
result5 = null;
pos = savedPos2;
}
} else {
result5 = null;
pos = savedPos2;
}
}
if (result4 !== null) {
result1 = [result3, result4];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result2 = result1 !== null ?
(function(ws, s) {
var tmp = [];
for (var i = 0; i < s.length; ++i) {
for (var j = 0; j < s[i].length; ++j) {
tmp.push(s[i][j]);
}
}
return {
type: "string",
val: ws + tmp.join('')
};
})(result1[0], result1[1]) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_messageFormatElement() {
var cacheKey = 'messageFormatElement@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result3 = parse_id();
var result1;
if (result3 !== null) {
var savedPos2 = pos;
var result6;
if (input.substr(pos, 1) === ",") {
result6 = ",";
pos += 1;
} else {
result6 = null;
if (reportMatchFailures) {
matchFailed("\",\"");
}
}
var result5;
if (result6 !== null) {
var result7 = parse_elementFormat();
if (result7 !== null) {
result5 = [result6, result7];
} else {
result5 = null;
pos = savedPos2;
}
} else {
result5 = null;
pos = savedPos2;
}
var result4 = result5 !== null ? result5 : '';
if (result4 !== null) {
result1 = [result3, result4];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result2 = result1 !== null ?
(function(argIdx, efmt) {
var res = {
type: "messageFormatElement",
argumentIndex: argIdx
};
if (efmt && efmt.length) {
res.elementFormat = efmt[1];
} else {
res.output = true;
}
return res;
})(result1[0], result1[1]) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_messageFormatPatternRight() {
var cacheKey = 'messageFormatPatternRight@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result3;
if (input.substr(pos, 1) === "{") {
result3 = "{";
pos += 1;
} else {
result3 = null;
if (reportMatchFailures) {
matchFailed("\"{\"");
}
}
var result1;
if (result3 !== null) {
var result4 = parse__();
if (result4 !== null) {
var result5 = parse_messageFormatElement();
if (result5 !== null) {
var result6 = parse__();
if (result6 !== null) {
var result7;
if (input.substr(pos, 1) === "}") {
result7 = "}";
pos += 1;
} else {
result7 = null;
if (reportMatchFailures) {
matchFailed("\"}\"");
}
}
if (result7 !== null) {
var result8 = parse_string();
if (result8 !== null) {
result1 = [result3, result4, result5, result6, result7, result8];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result2 = result1 !== null ?
(function(mfe, s1) {
var res = [];
if (mfe) {
res.push(mfe);
}
if (s1 && s1.val) {
res.push(s1);
}
return {
type: "messageFormatPatternRight",
statements: res
};
})(result1[2], result1[5]) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_messageFormatPattern() {
var cacheKey = 'messageFormatPattern@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result1;
var result3 = parse_string();
if (result3 !== null) {
var result4 = [];
var result5 = parse_messageFormatPatternRight();
while (result5 !== null) {
result4.push(result5);
result5 = parse_messageFormatPatternRight();
}
if (result4 !== null) {
result1 = [result3, result4];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result0;
var result2 = result1 !== null ?
(function(s1, inner) {
var st = [];
if (s1 && s1.val) {
st.push(s1);
}
for (var i in inner) {
if (inner.hasOwnProperty(i)) {
st.push(inner[i]);
}
}
return {
type: 'messageFormatPattern',
statements: st
};
})(result1[0], result1[1]) :
null;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_pluralForms() {
var cacheKey = 'pluralForms@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result1;
var result3 = parse__();
if (result3 !== null) {
var result4 = parse_stringKey();
if (result4 !== null) {
var result5 = parse__();
if (result5 !== null) {
var result6;
if (input.substr(pos, 1) === "{") {
result6 = "{";
pos += 1;
} else {
result6 = null;
if (reportMatchFailures) {
matchFailed("\"{\"");
}
}
if (result6 !== null) {
var result7 = parse__();
if (result7 !== null) {
var result8 = parse_messageFormatPattern();
if (result8 !== null) {
var result9 = parse__();
if (result9 !== null) {
var result10;
if (input.substr(pos, 1) === "}") {
result10 = "}";
pos += 1;
} else {
result10 = null;
if (reportMatchFailures) {
matchFailed("\"}\"");
}
}
if (result10 !== null) {
result1 = [result3, result4, result5, result6, result7, result8, result9, result10];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result2 = result1 !== null ?
(function(k, mfp) {
return {
type: "pluralForms",
key: k,
val: mfp
};
})(result1[1], result1[5]) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_selectFormatPattern() {
var cacheKey = 'selectFormatPattern@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var result1 = [];
var result3 = parse_pluralForms();
while (result3 !== null) {
result1.push(result3);
result3 = parse_pluralForms();
}
var result2 = result1 !== null ?
(function(pf) {
return {
type: "selectFormatPattern",
pluralForms: pf
};
})(result1) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_selectStyle() {
var cacheKey = 'selectStyle@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var result1 = parse_selectFormatPattern();
var result2 = result1 !== null ?
(function(sfp) {
return {
type: "selectStyle",
val: sfp
};
})(result1) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_pluralFormatPattern() {
var cacheKey = 'pluralFormatPattern@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var savedPos1 = pos;
var result6 = parse_offsetPattern();
var result3 = result6 !== null ? result6 : '';
var result1;
if (result3 !== null) {
var result4 = [];
var result5 = parse_pluralForms();
while (result5 !== null) {
result4.push(result5);
result5 = parse_pluralForms();
}
if (result4 !== null) {
result1 = [result3, result4];
} else {
result1 = null;
pos = savedPos1;
}
} else {
result1 = null;
pos = savedPos1;
}
var result2 = result1 !== null ?
(function(op, pf) {
var res = {
type: "pluralFormatPattern",
pluralForms: pf
};
if (op) {
res.offset = op;
} else {
res.offset = 0;
}
return res;
})(result1[0], result1[1]) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function parse_pluralStyle() {
var cacheKey = 'pluralStyle@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var result1 = parse_pluralFormatPattern();
var result2 = result1 !== null ?
(function(pfp) {
return {
type: "pluralStyle",
val: pfp
};
})(result1) :
null;
var result0;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
parse_elementFormat = function () {
var cacheKey = 'elementFormat@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos2 = pos;
var savedPos3 = pos;
var result14 = parse__();
var result12;
if (result14 !== null) {
var result15;
if (input.substr(pos, 6) === "plural") {
result15 = "plural";
pos += 6;
} else {
result15 = null;
if (reportMatchFailures) {
matchFailed("\"plural\"");
}
}
if (result15 !== null) {
var result16 = parse__();
if (result16 !== null) {
var result17;
if (input.substr(pos, 1) === ",") {
result17 = ",";
pos += 1;
} else {
result17 = null;
if (reportMatchFailures) {
matchFailed("\",\"");
}
}
if (result17 !== null) {
var result18 = parse__();
if (result18 !== null) {
var result19 = parse_pluralStyle();
if (result19 !== null) {
var result20 = parse__();
if (result20 !== null) {
result12 = [result14, result15, result16, result17, result18, result19, result20];
} else {
result12 = null;
pos = savedPos3;
}
} else {
result12 = null;
pos = savedPos3;
}
} else {
result12 = null;
pos = savedPos3;
}
} else {
result12 = null;
pos = savedPos3;
}
} else {
result12 = null;
pos = savedPos3;
}
} else {
result12 = null;
pos = savedPos3;
}
} else {
result12 = null;
pos = savedPos3;
}
var result13 = result12 !== null ?
(function(t, s) {
return {
type: "elementFormat",
key: t,
val: s.val
};
})(result12[1], result12[5]) :
null;
var result11;
if (result13 !== null) {
result11 = result13;
} else {
result11 = null;
pos = savedPos2;
}
var result0;
var result2;
if (result11 !== null) {
result0 = result11;
} else {
var savedPos0 = pos;
var savedPos1 = pos;
var result4 = parse__();
if (result4 !== null) {
var result5;
if (input.substr(pos, 6) === "select") {
result5 = "select";
pos += 6;
} else {
result5 = null;
if (reportMatchFailures) {
matchFailed("\"select\"");
}
}
if (result5 !== null) {
var result6 = parse__();
if (result6 !== null) {
var result7;
if (input.substr(pos, 1) === ",") {
result7 = ",";
pos += 1;
} else {
result7 = null;
if (reportMatchFailures) {
matchFailed("\",\"");
}
}
if (result7 !== null) {
var result8 = parse__();
if (result8 !== null) {
var result9 = parse_selectStyle();
if (result9 !== null) {
var result10 = parse__();
if (result10 !== null) {
result2 = [result4, result5, result6, result7, result8, result9, result10];
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
} else {
result2 = null;
pos = savedPos1;
}
var result3 = result2 !== null ?
(function(t, s) {
return {
type: "elementFormat",
key: t,
val: s.val
};
})(result2[1], result2[5]) :
null;
var result1;
if (result3 !== null) {
result1 = result3;
} else {
result1 = null;
pos = savedPos0;
}
if (result1 !== null) {
result0 = result1;
} else {
result0 = null;
}
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
};
function parse_start() {
var cacheKey = 'start@' + pos;
var cachedResult = cache[cacheKey];
if (cachedResult) {
pos = cachedResult.nextPos;
return cachedResult.result;
}
var savedPos0 = pos;
var result0;
var result1 = parse_messageFormatPattern();
var result2 = result1 !== null ?
(function(messageFormatPattern) {
return {
type: "program",
program: messageFormatPattern
};
})(result1) :
null;
if (result2 !== null) {
result0 = result2;
} else {
result0 = null;
pos = savedPos0;
}
cache[cacheKey] = {
nextPos: pos,
result: result0
};
return result0;
}
function buildErrorMessage() {
function buildExpected(failuresExpected) {
failuresExpected.sort();
var lastFailure = null;
var failuresExpectedUnique = [];
for (var i = 0; i < failuresExpected.length; i++) {
if (failuresExpected[i] !== lastFailure) {
failuresExpectedUnique.push(failuresExpected[i]);
lastFailure = failuresExpected[i];
}
}
switch (failuresExpectedUnique.length) {
case 0:
return 'end of input';
case 1:
return failuresExpectedUnique[0];
default:
return failuresExpectedUnique.slice(0, failuresExpectedUnique.length - 1).join(', ') +
' or ' +
failuresExpectedUnique[failuresExpectedUnique.length - 1];
}
}
var expected = buildExpected(rightmostMatchFailuresExpected);
var actualPos = Math.max(pos, rightmostMatchFailuresPos);
var actual = actualPos < input.length ?
quote(input.charAt(actualPos)) :
'end of input';
return 'Expected ' + expected + ' but ' + actual + ' found.';
}
function computeErrorPosition() {
/*
* The first idea was to use |String.split| to break the input up to the
* error position along newlines and derive the line and column from
* there. However IE's |split| implementation is so broken that it was
* enough to prevent it.
*/
var line = 1;
var column = 1;
var seenCR = false;
for (var i = 0; i < rightmostMatchFailuresPos; i++) {
var ch = input.charAt(i);
if (ch === '\n') {
if (!seenCR) {
line++;
}
column = 1;
seenCR = false;
} else if (ch === '\r' | ch === '\u2028' || ch === '\u2029') {
line++;
column = 1;
seenCR = true;
} else {
column++;
seenCR = false;
}
}
return {
line: line,
column: column
};
}
var parseFunctions = {
"_": parse__,
"char": parse_char,
"chars": parse_chars,
"digits": parse_digits,
"elementFormat": parse_elementFormat,
"hexDigit": parse_hexDigit,
"id": parse_id,
"messageFormatElement": parse_messageFormatElement,
"messageFormatPattern": parse_messageFormatPattern,
"messageFormatPatternRight": parse_messageFormatPatternRight,
"offsetPattern": parse_offsetPattern,
"pluralFormatPattern": parse_pluralFormatPattern,
"pluralForms": parse_pluralForms,
"pluralStyle": parse_pluralStyle,
"selectFormatPattern": parse_selectFormatPattern,
"selectStyle": parse_selectStyle,
"start": parse_start,
"string": parse_string,
"stringKey": parse_stringKey,
"whitespace": parse_whitespace
};
if (startRule !== undefined) {
if (parseFunctions[startRule] === undefined) {
throw new Error("Invalid rule name: " + quote(startRule) + ".");
}
} else {
startRule = "start";
}
var result = parseFunctions[startRule]();
/*
* The parser is now in one of the following three states:
*
* 1. The parser successfully parsed the whole input.
*
* - |result !== null|
* - |pos === input.length|
* - |rightmostMatchFailuresExpected| may or may not contain something
*
* 2. The parser successfully parsed only a part of the input.
*
* - |result !== null|
* - |pos < input.length|
* - |rightmostMatchFailuresExpected| may or may not contain something
*
* 3. The parser did not successfully parse any part of the input.
*
* - |result === null|
* - |pos === 0|
* - |rightmostMatchFailuresExpected| contains at least one failure
*
* All code following this comment (including called functions) must
* handle these states.
*/
if (result === null || pos !== input.length) {
var errorPosition = computeErrorPosition();
throw new this.SyntaxError(
buildErrorMessage(),
errorPosition.line,
errorPosition.column
);
}
return result;
},
/* Returns the parser source code. */
toSource: function() {
return this._source;
}
};
/* Thrown when a parser encounters a syntax error. */
result.SyntaxError = function(message, line, column) {
this.name = 'SyntaxError';
this.message = message;
this.line = line;
this.column = column;
};
result.SyntaxError.prototype = Error.prototype;
return result;
})();
MessageFormat.prototype.parse = function() {
// Bind to itself so error handling works
return mparser.parse.apply(mparser, arguments);
};
MessageFormat.prototype.precompile = function(ast) {
var self = this,
needOther = false,
fp = {
begin: 'function(d){\nvar r = "";\n',
end: "return r;\n}"
};
function interpMFP(ast, data) {
// Set some default data
data = data || {};
var s = '',
res, i, tmp, lastkeyname;
switch (ast.type) {
case 'program':
return interpMFP(ast.program);
case 'messageFormatPattern':
for (i = 0; i < ast.statements.length; ++i) {
s += interpMFP(ast.statements[i], data);
}
return fp.begin + s + fp.end;
case 'messageFormatPatternRight':
for (i = 0; i < ast.statements.length; ++i) {
s += interpMFP(ast.statements[i], data);
}
return s;
case 'messageFormatElement':
data.pf_count = data.pf_count || 0;
s += 'if(!d){\nthrow new Error("MessageFormat: No data passed to function.");\n}\n';
if (ast.output) {
s += 'r += d["' + ast.argumentIndex + '"];\n';
} else {
lastkeyname = 'lastkey_' + (data.pf_count + 1);
s += 'var ' + lastkeyname + ' = "' + ast.argumentIndex + '";\n';
s += 'var k_' + (data.pf_count + 1) + '=d[' + lastkeyname + '];\n';
s += interpMFP(ast.elementFormat, data);
}
return s;
case 'elementFormat':
if (ast.key === 'select') {
s += interpMFP(ast.val, data);
s += 'r += (pf_' +
data.pf_count +
'[ k_' + (data.pf_count + 1) + ' ] || pf_' + data.pf_count + '[ "other" ])( d );\n';
} else if (ast.key === 'plural') {
s += interpMFP(ast.val, data);
s += 'if ( pf_' + (data.pf_count) + '[ k_' + (data.pf_count + 1) + ' + "" ] ) {\n';
s += 'r += pf_' + data.pf_count + '[ k_' + (data.pf_count + 1) + ' + "" ]( d ); \n';
s += '}\nelse {\n';
s += 'r += (pf_' +
data.pf_count +
'[ MessageFormat.locale["' +
self.fallbackLocale +
'"]( k_' + (data.pf_count + 1) + ' - off_' + (data.pf_count) + ' ) ] || pf_' + data.pf_count + '[ "other" ] )( d );\n';
s += '}\n';
}
return s;
/* // Unreachable cases.
case 'pluralStyle':
case 'selectStyle':*/
case 'pluralFormatPattern':
data.pf_count = data.pf_count || 0;
s += 'var off_' + data.pf_count + ' = ' + ast.offset + ';\n';
s += 'var pf_' + data.pf_count + ' = { \n';
needOther = true;
// We're going to simultaneously check to make sure we hit the required 'other' option.
for (i = 0; i < ast.pluralForms.length; ++i) {
if (ast.pluralForms[i].key === 'other') {
needOther = false;
}
if (tmp) {
s += ',\n';
} else {
tmp = 1;
}
res = JSON.parse(JSON.stringify(data));
res.pf_count++;
s += '"' + ast.pluralForms[i].key + '" : ' + interpMFP(ast.pluralForms[i].val, res);
}
s += '\n};\n';
if (needOther) {
throw new Error("No 'other' form found in pluralFormatPattern " + data.pf_count);
}
return s;
case 'selectFormatPattern':
data.pf_count = data.pf_count || 0;
s += 'var off_' + data.pf_count + ' = 0;\n';
s += 'var pf_' + data.pf_count + ' = { \n';
needOther = true;
for (i = 0; i < ast.pluralForms.length; ++i) {
if (ast.pluralForms[i].key === 'other') {
needOther = false;
}
if (tmp) {
s += ',\n';
} else {
tmp = 1;
}
res = JSON.parse(JSON.stringify(data));
res.pf_count++;
s += '"' + ast.pluralForms[i].key + '" : ' + interpMFP(ast.pluralForms[i].val, res);
}
s += '\n};\n';
if (needOther) {
throw new Error("No 'other' form found in selectFormatPattern " + data.pf_count);
}
return s;
/* // Unreachable
case 'pluralForms':
*/
case 'string':
return 'r += "' + MessageFormat.Utils.numSub(
MessageFormat.Utils.escapeExpression(ast.val),
'k_' + data.pf_count + ' - off_' + (data.pf_count - 1),
data.pf_count
) + '";\n';
default:
throw new Error('Bad AST type: ' + ast.type);
}
}
return interpMFP(ast);
};
MessageFormat.prototype.compile = function(message) {
/* jshint evil: true */
return new Function('MessageFormat',
'return ' +
this.precompile(
this.parse(message)
)
)(MessageFormat);
/* jshint evil: false */
};
module.exports = MessageFormat;