Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2859 lines
No EOL
66 KiB
JavaScript
Executable file
2859 lines
No EOL
66 KiB
JavaScript
Executable file
'use strict';
|
||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
|
||
|
||
function L10nError(message, id, lang) {
|
||
this.name = 'L10nError';
|
||
this.message = message;
|
||
this.id = id;
|
||
this.lang = lang;
|
||
}
|
||
L10nError.prototype = Object.create(Error.prototype);
|
||
L10nError.prototype.constructor = L10nError;
|
||
|
||
function fetchResource(htmloptimizer, res, _ref) {
|
||
var code = _ref.code;
|
||
|
||
var url = decodeURI(res).replace('{locale}', code);
|
||
|
||
var _htmloptimizer$getFileByRelativePath = htmloptimizer.getFileByRelativePath(url);
|
||
|
||
var file = _htmloptimizer$getFileByRelativePath.file;
|
||
var content = _htmloptimizer$getFileByRelativePath.content;
|
||
|
||
if (!file) {
|
||
return Promise.reject(new L10nError('Not found: ' + url));
|
||
}
|
||
|
||
var parsed = res.endsWith('.json') ? JSON.parse(content) : content;
|
||
return Promise.resolve(parsed);
|
||
}
|
||
|
||
function walkEntry(entry, fn) {
|
||
if (typeof entry === 'string') {
|
||
return fn(entry);
|
||
}
|
||
|
||
var newEntry = Object.create(null);
|
||
|
||
if (entry.value) {
|
||
newEntry.value = walkValue(entry.value, fn);
|
||
}
|
||
|
||
if (entry.index) {
|
||
newEntry.index = entry.index;
|
||
}
|
||
|
||
if (entry.attrs) {
|
||
newEntry.attrs = Object.create(null);
|
||
for (var key in entry.attrs) {
|
||
newEntry.attrs[key] = walkEntry(entry.attrs[key], fn);
|
||
}
|
||
}
|
||
|
||
return newEntry;
|
||
}
|
||
|
||
function walkValue(value, fn) {
|
||
if (typeof value === 'string') {
|
||
return fn(value);
|
||
}
|
||
|
||
if (value.type) {
|
||
return value;
|
||
}
|
||
|
||
var newValue = Array.isArray(value) ? [] : Object.create(null);
|
||
var keys = Object.keys(value);
|
||
|
||
for (var i = 0, key = undefined; key = keys[i]; i++) {
|
||
newValue[key] = walkValue(value[key], fn);
|
||
}
|
||
|
||
return newValue;
|
||
}
|
||
|
||
function createGetter(id, name) {
|
||
var _pseudo = null;
|
||
|
||
return function getPseudo() {
|
||
if (_pseudo) {
|
||
return _pseudo;
|
||
}
|
||
|
||
var reAlphas = /[a-zA-Z]/g;
|
||
var reVowels = /[aeiouAEIOU]/g;
|
||
var reWords = /[^\W0-9_]+/g;
|
||
|
||
var reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;
|
||
|
||
var charMaps = {
|
||
'fr-x-psaccent': 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ',
|
||
'ar-x-psbidi': '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz'
|
||
};
|
||
|
||
var mods = {
|
||
'fr-x-psaccent': function (val) {
|
||
return val.replace(reVowels, function (match) {
|
||
return match + match.toLowerCase();
|
||
});
|
||
},
|
||
|
||
'ar-x-psbidi': function (val) {
|
||
return val.replace(reWords, function (match) {
|
||
return '' + match + '';
|
||
});
|
||
}
|
||
};
|
||
|
||
var ASCII_LETTER_A = 65;
|
||
var replaceChars = function (map, val) {
|
||
return val.replace(reAlphas, function (match) {
|
||
return map.charAt(match.charCodeAt(0) - ASCII_LETTER_A);
|
||
});
|
||
};
|
||
|
||
var transform = function (val) {
|
||
return replaceChars(charMaps[id], mods[id](val));
|
||
};
|
||
|
||
var apply = function (fn, val) {
|
||
if (!val) {
|
||
return val;
|
||
}
|
||
|
||
var parts = val.split(reExcluded);
|
||
var modified = parts.map(function (part) {
|
||
if (reExcluded.test(part)) {
|
||
return part;
|
||
}
|
||
return fn(part);
|
||
});
|
||
return modified.join('');
|
||
};
|
||
|
||
return _pseudo = {
|
||
name: transform(name),
|
||
process: function (str) {
|
||
return apply(transform, str);
|
||
}
|
||
};
|
||
};
|
||
}
|
||
|
||
var pseudo = Object.defineProperties(Object.create(null), {
|
||
'fr-x-psaccent': {
|
||
enumerable: true,
|
||
get: createGetter('fr-x-psaccent', 'Runtime Accented')
|
||
},
|
||
'ar-x-psbidi': {
|
||
enumerable: true,
|
||
get: createGetter('ar-x-psbidi', 'Runtime Bidi')
|
||
}
|
||
});
|
||
|
||
var KNOWN_MACROS$1 = ['plural'];
|
||
var MAX_PLACEABLE_LENGTH$1 = 2500;
|
||
|
||
var FSI$1 = '';
|
||
var PDI$1 = '';
|
||
|
||
var resolutionChain$1 = new WeakSet();
|
||
|
||
function format$1(ctx, lang, args, entity) {
|
||
if (typeof entity === 'string') {
|
||
return [{}, entity];
|
||
}
|
||
|
||
if (resolutionChain$1.has(entity)) {
|
||
throw new L10nError('Cyclic reference detected');
|
||
}
|
||
|
||
resolutionChain$1.add(entity);
|
||
|
||
var rv = undefined;
|
||
|
||
try {
|
||
rv = resolveValue$1({}, ctx, lang, args, entity.value, entity.index);
|
||
} finally {
|
||
resolutionChain$1.delete(entity);
|
||
}
|
||
return rv;
|
||
}
|
||
|
||
function resolveIdentifier$1(ctx, lang, args, id) {
|
||
if (KNOWN_MACROS$1.indexOf(id) > -1) {
|
||
return [{}, ctx._getMacro(lang, id)];
|
||
}
|
||
|
||
if (args && args.hasOwnProperty(id)) {
|
||
if (typeof args[id] === 'string' || typeof args[id] === 'number' && !isNaN(args[id])) {
|
||
return [{}, args[id]];
|
||
} else {
|
||
throw new L10nError('Arg must be a string or a number: ' + id);
|
||
}
|
||
}
|
||
|
||
if (id === '__proto__') {
|
||
throw new L10nError('Illegal id: ' + id);
|
||
}
|
||
|
||
var entity = ctx._getEntity(lang, id);
|
||
|
||
if (entity) {
|
||
return format$1(ctx, lang, args, entity);
|
||
}
|
||
|
||
throw new L10nError('Unknown reference: ' + id);
|
||
}
|
||
|
||
function subPlaceable$1(locals, ctx, lang, args, id) {
|
||
var newLocals = undefined,
|
||
value = undefined;
|
||
|
||
try {
|
||
var _resolveIdentifier$1 = resolveIdentifier$1(ctx, lang, args, id);
|
||
|
||
newLocals = _resolveIdentifier$1[0];
|
||
value = _resolveIdentifier$1[1];
|
||
} catch (err) {
|
||
return [{ error: err }, FSI$1 + '{{ ' + id + ' }}' + PDI$1];
|
||
}
|
||
|
||
if (typeof value === 'number') {
|
||
var formatter = ctx._getNumberFormatter(lang);
|
||
return [newLocals, formatter.format(value)];
|
||
}
|
||
|
||
if (typeof value === 'string') {
|
||
if (value.length >= MAX_PLACEABLE_LENGTH$1) {
|
||
throw new L10nError('Too many characters in placeable (' + value.length + ', max allowed is ' + MAX_PLACEABLE_LENGTH$1 + ')');
|
||
}
|
||
return [newLocals, FSI$1 + value + PDI$1];
|
||
}
|
||
|
||
return [{}, FSI$1 + '{{ ' + id + ' }}' + PDI$1];
|
||
}
|
||
|
||
function interpolate$1(locals, ctx, lang, args, arr) {
|
||
return arr.reduce(function (_ref2, cur) {
|
||
var localsSeq = _ref2[0];
|
||
var valueSeq = _ref2[1];
|
||
|
||
if (typeof cur === 'string') {
|
||
return [localsSeq, valueSeq + cur];
|
||
} else {
|
||
var _subPlaceable$1 = subPlaceable$1(locals, ctx, lang, args, cur.name);
|
||
|
||
var value = _subPlaceable$1[1];
|
||
|
||
return [localsSeq, valueSeq + value];
|
||
}
|
||
}, [locals, '']);
|
||
}
|
||
|
||
function resolveSelector$1(ctx, lang, args, expr, index) {
|
||
var selectorName = undefined;
|
||
if (index[0].type === 'call' && index[0].expr.type === 'prop' && index[0].expr.expr.name === 'cldr') {
|
||
selectorName = 'plural';
|
||
} else {
|
||
selectorName = index[0].name;
|
||
}
|
||
var selector = resolveIdentifier$1(ctx, lang, args, selectorName)[1];
|
||
|
||
if (typeof selector !== 'function') {
|
||
return selector;
|
||
}
|
||
|
||
var argValue = index[0].args ? resolveIdentifier$1(ctx, lang, args, index[0].args[0].name)[1] : undefined;
|
||
|
||
if (selectorName === 'plural') {
|
||
if (argValue === 0 && 'zero' in expr) {
|
||
return 'zero';
|
||
}
|
||
if (argValue === 1 && 'one' in expr) {
|
||
return 'one';
|
||
}
|
||
if (argValue === 2 && 'two' in expr) {
|
||
return 'two';
|
||
}
|
||
}
|
||
|
||
return selector(argValue);
|
||
}
|
||
|
||
function resolveValue$1(locals, ctx, lang, args, expr, index) {
|
||
if (!expr) {
|
||
return [locals, expr];
|
||
}
|
||
|
||
if (typeof expr === 'string' || typeof expr === 'boolean' || typeof expr === 'number') {
|
||
return [locals, expr];
|
||
}
|
||
|
||
if (Array.isArray(expr)) {
|
||
return interpolate$1(locals, ctx, lang, args, expr);
|
||
}
|
||
|
||
if (index) {
|
||
var selector = resolveSelector$1(ctx, lang, args, expr, index);
|
||
if (selector in expr) {
|
||
return resolveValue$1(locals, ctx, lang, args, expr[selector]);
|
||
}
|
||
}
|
||
|
||
var defaultKey = expr.__default || 'other';
|
||
if (defaultKey in expr) {
|
||
return resolveValue$1(locals, ctx, lang, args, expr[defaultKey]);
|
||
}
|
||
|
||
throw new L10nError('Unresolvable value');
|
||
}
|
||
|
||
var locales2rules = {
|
||
'af': 3,
|
||
'ak': 4,
|
||
'am': 4,
|
||
'ar': 1,
|
||
'asa': 3,
|
||
'az': 0,
|
||
'be': 11,
|
||
'bem': 3,
|
||
'bez': 3,
|
||
'bg': 3,
|
||
'bh': 4,
|
||
'bm': 0,
|
||
'bn': 3,
|
||
'bo': 0,
|
||
'br': 20,
|
||
'brx': 3,
|
||
'bs': 11,
|
||
'ca': 3,
|
||
'cgg': 3,
|
||
'chr': 3,
|
||
'cs': 12,
|
||
'cy': 17,
|
||
'da': 3,
|
||
'de': 3,
|
||
'dv': 3,
|
||
'dz': 0,
|
||
'ee': 3,
|
||
'el': 3,
|
||
'en': 3,
|
||
'eo': 3,
|
||
'es': 3,
|
||
'et': 3,
|
||
'eu': 3,
|
||
'fa': 0,
|
||
'ff': 5,
|
||
'fi': 3,
|
||
'fil': 4,
|
||
'fo': 3,
|
||
'fr': 5,
|
||
'fur': 3,
|
||
'fy': 3,
|
||
'ga': 8,
|
||
'gd': 24,
|
||
'gl': 3,
|
||
'gsw': 3,
|
||
'gu': 3,
|
||
'guw': 4,
|
||
'gv': 23,
|
||
'ha': 3,
|
||
'haw': 3,
|
||
'he': 2,
|
||
'hi': 4,
|
||
'hr': 11,
|
||
'hu': 0,
|
||
'id': 0,
|
||
'ig': 0,
|
||
'ii': 0,
|
||
'is': 3,
|
||
'it': 3,
|
||
'iu': 7,
|
||
'ja': 0,
|
||
'jmc': 3,
|
||
'jv': 0,
|
||
'ka': 0,
|
||
'kab': 5,
|
||
'kaj': 3,
|
||
'kcg': 3,
|
||
'kde': 0,
|
||
'kea': 0,
|
||
'kk': 3,
|
||
'kl': 3,
|
||
'km': 0,
|
||
'kn': 0,
|
||
'ko': 0,
|
||
'ksb': 3,
|
||
'ksh': 21,
|
||
'ku': 3,
|
||
'kw': 7,
|
||
'lag': 18,
|
||
'lb': 3,
|
||
'lg': 3,
|
||
'ln': 4,
|
||
'lo': 0,
|
||
'lt': 10,
|
||
'lv': 6,
|
||
'mas': 3,
|
||
'mg': 4,
|
||
'mk': 16,
|
||
'ml': 3,
|
||
'mn': 3,
|
||
'mo': 9,
|
||
'mr': 3,
|
||
'ms': 0,
|
||
'mt': 15,
|
||
'my': 0,
|
||
'nah': 3,
|
||
'naq': 7,
|
||
'nb': 3,
|
||
'nd': 3,
|
||
'ne': 3,
|
||
'nl': 3,
|
||
'nn': 3,
|
||
'no': 3,
|
||
'nr': 3,
|
||
'nso': 4,
|
||
'ny': 3,
|
||
'nyn': 3,
|
||
'om': 3,
|
||
'or': 3,
|
||
'pa': 3,
|
||
'pap': 3,
|
||
'pl': 13,
|
||
'ps': 3,
|
||
'pt': 3,
|
||
'rm': 3,
|
||
'ro': 9,
|
||
'rof': 3,
|
||
'ru': 11,
|
||
'rwk': 3,
|
||
'sah': 0,
|
||
'saq': 3,
|
||
'se': 7,
|
||
'seh': 3,
|
||
'ses': 0,
|
||
'sg': 0,
|
||
'sh': 11,
|
||
'shi': 19,
|
||
'sk': 12,
|
||
'sl': 14,
|
||
'sma': 7,
|
||
'smi': 7,
|
||
'smj': 7,
|
||
'smn': 7,
|
||
'sms': 7,
|
||
'sn': 3,
|
||
'so': 3,
|
||
'sq': 3,
|
||
'sr': 11,
|
||
'ss': 3,
|
||
'ssy': 3,
|
||
'st': 3,
|
||
'sv': 3,
|
||
'sw': 3,
|
||
'syr': 3,
|
||
'ta': 3,
|
||
'te': 3,
|
||
'teo': 3,
|
||
'th': 0,
|
||
'ti': 4,
|
||
'tig': 3,
|
||
'tk': 3,
|
||
'tl': 4,
|
||
'tn': 3,
|
||
'to': 0,
|
||
'tr': 0,
|
||
'ts': 3,
|
||
'tzm': 22,
|
||
'uk': 11,
|
||
'ur': 3,
|
||
've': 3,
|
||
'vi': 0,
|
||
'vun': 3,
|
||
'wa': 4,
|
||
'wae': 3,
|
||
'wo': 0,
|
||
'xh': 3,
|
||
'xog': 3,
|
||
'yo': 0,
|
||
'zh': 0,
|
||
'zu': 3
|
||
};
|
||
|
||
function isIn(n, list) {
|
||
return list.indexOf(n) !== -1;
|
||
}
|
||
function isBetween(n, start, end) {
|
||
return typeof n === typeof start && start <= n && n <= end;
|
||
}
|
||
|
||
var pluralRules = {
|
||
'0': function () {
|
||
return 'other';
|
||
},
|
||
'1': function (n) {
|
||
if (isBetween(n % 100, 3, 10)) {
|
||
return 'few';
|
||
}
|
||
if (n === 0) {
|
||
return 'zero';
|
||
}
|
||
if (isBetween(n % 100, 11, 99)) {
|
||
return 'many';
|
||
}
|
||
if (n === 2) {
|
||
return 'two';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'2': function (n) {
|
||
if (n !== 0 && n % 10 === 0) {
|
||
return 'many';
|
||
}
|
||
if (n === 2) {
|
||
return 'two';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'3': function (n) {
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'4': function (n) {
|
||
if (isBetween(n, 0, 1)) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'5': function (n) {
|
||
if (isBetween(n, 0, 2) && n !== 2) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'6': function (n) {
|
||
if (n === 0) {
|
||
return 'zero';
|
||
}
|
||
if (n % 10 === 1 && n % 100 !== 11) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'7': function (n) {
|
||
if (n === 2) {
|
||
return 'two';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'8': function (n) {
|
||
if (isBetween(n, 3, 6)) {
|
||
return 'few';
|
||
}
|
||
if (isBetween(n, 7, 10)) {
|
||
return 'many';
|
||
}
|
||
if (n === 2) {
|
||
return 'two';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'9': function (n) {
|
||
if (n === 0 || n !== 1 && isBetween(n % 100, 1, 19)) {
|
||
return 'few';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'10': function (n) {
|
||
if (isBetween(n % 10, 2, 9) && !isBetween(n % 100, 11, 19)) {
|
||
return 'few';
|
||
}
|
||
if (n % 10 === 1 && !isBetween(n % 100, 11, 19)) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'11': function (n) {
|
||
if (isBetween(n % 10, 2, 4) && !isBetween(n % 100, 12, 14)) {
|
||
return 'few';
|
||
}
|
||
if (n % 10 === 0 || isBetween(n % 10, 5, 9) || isBetween(n % 100, 11, 14)) {
|
||
return 'many';
|
||
}
|
||
if (n % 10 === 1 && n % 100 !== 11) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'12': function (n) {
|
||
if (isBetween(n, 2, 4)) {
|
||
return 'few';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'13': function (n) {
|
||
if (isBetween(n % 10, 2, 4) && !isBetween(n % 100, 12, 14)) {
|
||
return 'few';
|
||
}
|
||
if (n !== 1 && isBetween(n % 10, 0, 1) || isBetween(n % 10, 5, 9) || isBetween(n % 100, 12, 14)) {
|
||
return 'many';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'14': function (n) {
|
||
if (isBetween(n % 100, 3, 4)) {
|
||
return 'few';
|
||
}
|
||
if (n % 100 === 2) {
|
||
return 'two';
|
||
}
|
||
if (n % 100 === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'15': function (n) {
|
||
if (n === 0 || isBetween(n % 100, 2, 10)) {
|
||
return 'few';
|
||
}
|
||
if (isBetween(n % 100, 11, 19)) {
|
||
return 'many';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'16': function (n) {
|
||
if (n % 10 === 1 && n !== 11) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'17': function (n) {
|
||
if (n === 3) {
|
||
return 'few';
|
||
}
|
||
if (n === 0) {
|
||
return 'zero';
|
||
}
|
||
if (n === 6) {
|
||
return 'many';
|
||
}
|
||
if (n === 2) {
|
||
return 'two';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'18': function (n) {
|
||
if (n === 0) {
|
||
return 'zero';
|
||
}
|
||
if (isBetween(n, 0, 2) && n !== 0 && n !== 2) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'19': function (n) {
|
||
if (isBetween(n, 2, 10)) {
|
||
return 'few';
|
||
}
|
||
if (isBetween(n, 0, 1)) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'20': function (n) {
|
||
if ((isBetween(n % 10, 3, 4) || n % 10 === 9) && !(isBetween(n % 100, 10, 19) || isBetween(n % 100, 70, 79) || isBetween(n % 100, 90, 99))) {
|
||
return 'few';
|
||
}
|
||
if (n % 1000000 === 0 && n !== 0) {
|
||
return 'many';
|
||
}
|
||
if (n % 10 === 2 && !isIn(n % 100, [12, 72, 92])) {
|
||
return 'two';
|
||
}
|
||
if (n % 10 === 1 && !isIn(n % 100, [11, 71, 91])) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'21': function (n) {
|
||
if (n === 0) {
|
||
return 'zero';
|
||
}
|
||
if (n === 1) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'22': function (n) {
|
||
if (isBetween(n, 0, 1) || isBetween(n, 11, 99)) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'23': function (n) {
|
||
if (isBetween(n % 10, 1, 2) || n % 20 === 0) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
},
|
||
'24': function (n) {
|
||
if (isBetween(n, 3, 10) || isBetween(n, 13, 19)) {
|
||
return 'few';
|
||
}
|
||
if (isIn(n, [2, 12])) {
|
||
return 'two';
|
||
}
|
||
if (isIn(n, [1, 11])) {
|
||
return 'one';
|
||
}
|
||
return 'other';
|
||
}
|
||
};
|
||
|
||
function getPluralRule(code) {
|
||
var index = locales2rules[code.replace(/-.*$/, '')];
|
||
if (!(index in pluralRules)) {
|
||
return function () {
|
||
return 'other';
|
||
};
|
||
}
|
||
return pluralRules[index];
|
||
}
|
||
|
||
var L20nIntl = typeof Intl !== 'undefined' ? Intl : {
|
||
NumberFormat: function () {
|
||
return {
|
||
format: function (v) {
|
||
return v;
|
||
}
|
||
};
|
||
}
|
||
};
|
||
|
||
var Context = (function () {
|
||
function Context(env, langs, resIds) {
|
||
var _this = this;
|
||
|
||
_classCallCheck(this, Context);
|
||
|
||
this.langs = langs;
|
||
this.resIds = resIds;
|
||
this.env = env;
|
||
this.emit = function (type, evt) {
|
||
return env.emit(type, evt, _this);
|
||
};
|
||
}
|
||
|
||
Context.prototype._formatTuple = function _formatTuple(lang, args, entity, id, key) {
|
||
try {
|
||
return format$1(this, lang, args, entity);
|
||
} catch (err) {
|
||
err.id = key ? id + '::' + key : id;
|
||
err.lang = lang;
|
||
this.emit('resolveerror', err);
|
||
return [{ error: err }, err.id];
|
||
}
|
||
};
|
||
|
||
Context.prototype._formatEntity = function _formatEntity(lang, args, entity, id) {
|
||
var _formatTuple2 = this._formatTuple(lang, args, entity, id);
|
||
|
||
var value = _formatTuple2[1];
|
||
|
||
var formatted = {
|
||
value: value,
|
||
attrs: null
|
||
};
|
||
|
||
if (entity.attrs) {
|
||
formatted.attrs = Object.create(null);
|
||
for (var key in entity.attrs) {
|
||
var _formatTuple3 = this._formatTuple(lang, args, entity.attrs[key], id, key);
|
||
|
||
var attrValue = _formatTuple3[1];
|
||
|
||
formatted.attrs[key] = attrValue;
|
||
}
|
||
}
|
||
|
||
return formatted;
|
||
};
|
||
|
||
Context.prototype._formatValue = function _formatValue(lang, args, entity, id) {
|
||
return this._formatTuple(lang, args, entity, id)[1];
|
||
};
|
||
|
||
Context.prototype.fetch = function fetch() {
|
||
var _this2 = this;
|
||
|
||
var langs = arguments.length <= 0 || arguments[0] === undefined ? this.langs : arguments[0];
|
||
|
||
if (langs.length === 0) {
|
||
return Promise.resolve(langs);
|
||
}
|
||
|
||
return Promise.all(this.resIds.map(function (resId) {
|
||
return _this2.env._getResource(langs[0], resId);
|
||
})).then(function () {
|
||
return langs;
|
||
});
|
||
};
|
||
|
||
Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) {
|
||
var _this3 = this;
|
||
|
||
var lang = langs[0];
|
||
|
||
if (!lang) {
|
||
return reportMissing.call(this, keys, formatter, prevResolved);
|
||
}
|
||
|
||
var hasUnresolved = false;
|
||
|
||
var resolved = keys.map(function (key, i) {
|
||
if (prevResolved && prevResolved[i] !== undefined) {
|
||
return prevResolved[i];
|
||
}
|
||
|
||
var _ref3 = Array.isArray(key) ? key : [key, undefined];
|
||
|
||
var id = _ref3[0];
|
||
var args = _ref3[1];
|
||
|
||
var entity = _this3._getEntity(lang, id);
|
||
|
||
if (entity) {
|
||
return formatter.call(_this3, lang, args, entity, id);
|
||
}
|
||
|
||
_this3.emit('notfounderror', new L10nError('"' + id + '" not found in ' + lang.code, id, lang));
|
||
hasUnresolved = true;
|
||
});
|
||
|
||
if (!hasUnresolved) {
|
||
return resolved;
|
||
}
|
||
|
||
return this.fetch(langs.slice(1)).then(function (nextLangs) {
|
||
return _this3._resolve(nextLangs, keys, formatter, resolved);
|
||
});
|
||
};
|
||
|
||
Context.prototype.formatEntities = function formatEntities() {
|
||
var _this4 = this;
|
||
|
||
for (var _len = arguments.length, keys = Array(_len), _key = 0; _key < _len; _key++) {
|
||
keys[_key] = arguments[_key];
|
||
}
|
||
|
||
return this.fetch().then(function (langs) {
|
||
return _this4._resolve(langs, keys, _this4._formatEntity);
|
||
});
|
||
};
|
||
|
||
Context.prototype.formatValues = function formatValues() {
|
||
var _this5 = this;
|
||
|
||
for (var _len2 = arguments.length, keys = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||
keys[_key2] = arguments[_key2];
|
||
}
|
||
|
||
return this.fetch().then(function (langs) {
|
||
return _this5._resolve(langs, keys, _this5._formatValue);
|
||
});
|
||
};
|
||
|
||
Context.prototype._getEntity = function _getEntity(lang, id) {
|
||
var cache = this.env.resCache;
|
||
|
||
for (var i = 0, resId = undefined; resId = this.resIds[i]; i++) {
|
||
var resource = cache.get(resId + lang.code + lang.src);
|
||
if (resource instanceof L10nError) {
|
||
continue;
|
||
}
|
||
if (id in resource) {
|
||
return resource[id];
|
||
}
|
||
}
|
||
return undefined;
|
||
};
|
||
|
||
Context.prototype._getNumberFormatter = function _getNumberFormatter(lang) {
|
||
if (!this.env.numberFormatters) {
|
||
this.env.numberFormatters = new Map();
|
||
}
|
||
if (!this.env.numberFormatters.has(lang)) {
|
||
var formatter = L20nIntl.NumberFormat(lang);
|
||
this.env.numberFormatters.set(lang, formatter);
|
||
return formatter;
|
||
}
|
||
return this.env.numberFormatters.get(lang);
|
||
};
|
||
|
||
Context.prototype._getMacro = function _getMacro(lang, id) {
|
||
switch (id) {
|
||
case 'plural':
|
||
return getPluralRule(lang.code);
|
||
default:
|
||
return undefined;
|
||
}
|
||
};
|
||
|
||
return Context;
|
||
})();
|
||
|
||
function reportMissing(keys, formatter, resolved) {
|
||
var _this6 = this;
|
||
|
||
var missingIds = new Set();
|
||
|
||
keys.forEach(function (key, i) {
|
||
if (resolved && resolved[i] !== undefined) {
|
||
return;
|
||
}
|
||
var id = Array.isArray(key) ? key[0] : key;
|
||
missingIds.add(id);
|
||
resolved[i] = formatter === _this6._formatValue ? id : { value: id, attrs: null };
|
||
});
|
||
|
||
this.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds));
|
||
|
||
return resolved;
|
||
}
|
||
|
||
var MAX_PLACEABLES = 100;
|
||
|
||
var PropertiesParser = {
|
||
patterns: null,
|
||
entryIds: null,
|
||
emit: null,
|
||
|
||
init: function () {
|
||
this.patterns = {
|
||
comment: /^\s*#|^\s*$/,
|
||
entity: /^([^=\s]+)\s*=\s*(.*)$/,
|
||
multiline: /[^\\]\\$/,
|
||
index: /\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,
|
||
unicode: /\\u([0-9a-fA-F]{1,4})/g,
|
||
entries: /[^\r\n]+/g,
|
||
controlChars: /\\([\\\n\r\t\b\f\{\}\"\'])/g,
|
||
placeables: /\{\{\s*([^\s]*?)\s*\}\}/
|
||
};
|
||
},
|
||
|
||
parse: function (emit, source) {
|
||
if (!this.patterns) {
|
||
this.init();
|
||
}
|
||
this.emit = emit;
|
||
|
||
var entries = {};
|
||
|
||
var lines = source.match(this.patterns.entries);
|
||
if (!lines) {
|
||
return entries;
|
||
}
|
||
for (var i = 0; i < lines.length; i++) {
|
||
var line = lines[i];
|
||
|
||
if (this.patterns.comment.test(line)) {
|
||
continue;
|
||
}
|
||
|
||
while (this.patterns.multiline.test(line) && i < lines.length) {
|
||
line = line.slice(0, -1) + lines[++i].trim();
|
||
}
|
||
|
||
var entityMatch = line.match(this.patterns.entity);
|
||
if (entityMatch) {
|
||
try {
|
||
this.parseEntity(entityMatch[1], entityMatch[2], entries);
|
||
} catch (e) {
|
||
if (!this.emit) {
|
||
throw e;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return entries;
|
||
},
|
||
|
||
parseEntity: function (id, value, entries) {
|
||
var name = undefined,
|
||
key = undefined;
|
||
|
||
var pos = id.indexOf('[');
|
||
if (pos !== -1) {
|
||
name = id.substr(0, pos);
|
||
key = id.substring(pos + 1, id.length - 1);
|
||
} else {
|
||
name = id;
|
||
key = null;
|
||
}
|
||
|
||
var nameElements = name.split('.');
|
||
|
||
if (nameElements.length > 2) {
|
||
throw this.error('Error in ID: "' + name + '".' + ' Nested attributes are not supported.');
|
||
}
|
||
|
||
var attr = undefined;
|
||
if (nameElements.length > 1) {
|
||
name = nameElements[0];
|
||
attr = nameElements[1];
|
||
|
||
if (attr[0] === '$') {
|
||
throw this.error('Attribute can\'t start with "$"');
|
||
}
|
||
} else {
|
||
attr = null;
|
||
}
|
||
|
||
this.setEntityValue(name, attr, key, this.unescapeString(value), entries);
|
||
},
|
||
|
||
setEntityValue: function (id, attr, key, rawValue, entries) {
|
||
var value = rawValue.indexOf('{{') > -1 ? this.parseString(rawValue) : rawValue;
|
||
|
||
var isSimpleValue = typeof value === 'string';
|
||
var root = entries;
|
||
|
||
var isSimpleNode = typeof entries[id] === 'string';
|
||
|
||
if (!entries[id] && (attr || key || !isSimpleValue)) {
|
||
entries[id] = Object.create(null);
|
||
isSimpleNode = false;
|
||
}
|
||
|
||
if (attr) {
|
||
if (isSimpleNode) {
|
||
var val = entries[id];
|
||
entries[id] = Object.create(null);
|
||
entries[id].value = val;
|
||
}
|
||
if (!entries[id].attrs) {
|
||
entries[id].attrs = Object.create(null);
|
||
}
|
||
if (!entries[id].attrs && !isSimpleValue) {
|
||
entries[id].attrs[attr] = Object.create(null);
|
||
}
|
||
root = entries[id].attrs;
|
||
id = attr;
|
||
}
|
||
|
||
if (key) {
|
||
isSimpleNode = false;
|
||
if (typeof root[id] === 'string') {
|
||
var val = root[id];
|
||
root[id] = Object.create(null);
|
||
root[id].index = this.parseIndex(val);
|
||
root[id].value = Object.create(null);
|
||
}
|
||
root = root[id].value;
|
||
id = key;
|
||
isSimpleValue = true;
|
||
}
|
||
|
||
if (isSimpleValue) {
|
||
if (id in root) {
|
||
throw this.error('Duplicated id: ' + id);
|
||
}
|
||
root[id] = value;
|
||
} else {
|
||
if (!root[id]) {
|
||
root[id] = Object.create(null);
|
||
}
|
||
root[id].value = value;
|
||
}
|
||
},
|
||
|
||
parseString: function (str) {
|
||
var chunks = str.split(this.patterns.placeables);
|
||
var complexStr = [];
|
||
|
||
var len = chunks.length;
|
||
var placeablesCount = (len - 1) / 2;
|
||
|
||
if (placeablesCount >= MAX_PLACEABLES) {
|
||
throw this.error('Too many placeables (' + placeablesCount + ', max allowed is ' + MAX_PLACEABLES + ')');
|
||
}
|
||
|
||
for (var i = 0; i < chunks.length; i++) {
|
||
if (chunks[i].length === 0) {
|
||
continue;
|
||
}
|
||
if (i % 2 === 1) {
|
||
complexStr.push({ type: 'idOrVar', name: chunks[i] });
|
||
} else {
|
||
complexStr.push(chunks[i]);
|
||
}
|
||
}
|
||
return complexStr;
|
||
},
|
||
|
||
unescapeString: function (str) {
|
||
if (str.lastIndexOf('\\') !== -1) {
|
||
str = str.replace(this.patterns.controlChars, '$1');
|
||
}
|
||
return str.replace(this.patterns.unicode, function (match, token) {
|
||
return String.fromCodePoint(parseInt(token, 16));
|
||
});
|
||
},
|
||
|
||
parseIndex: function (str) {
|
||
var match = str.match(this.patterns.index);
|
||
if (!match) {
|
||
throw new L10nError('Malformed index');
|
||
}
|
||
if (match[2]) {
|
||
return [{
|
||
type: 'call',
|
||
expr: {
|
||
type: 'prop',
|
||
expr: {
|
||
type: 'glob',
|
||
name: 'cldr'
|
||
},
|
||
prop: 'plural',
|
||
cmpt: false
|
||
}, args: [{
|
||
type: 'idOrVar',
|
||
name: match[2]
|
||
}]
|
||
}];
|
||
} else {
|
||
return [{ type: 'idOrVar', name: match[1] }];
|
||
}
|
||
},
|
||
|
||
error: function (msg) {
|
||
var type = arguments.length <= 1 || arguments[1] === undefined ? 'parsererror' : arguments[1];
|
||
|
||
var err = new L10nError(msg);
|
||
if (this.emit) {
|
||
this.emit(type, err);
|
||
}
|
||
return err;
|
||
}
|
||
};
|
||
|
||
var MAX_PLACEABLES$1 = 100;
|
||
|
||
var L20nParser = {
|
||
parse: function (emit, string) {
|
||
this._source = string;
|
||
this._index = 0;
|
||
this._length = string.length;
|
||
this.entries = Object.create(null);
|
||
this.emit = emit;
|
||
|
||
return this.getResource();
|
||
},
|
||
|
||
getResource: function () {
|
||
this.getWS();
|
||
while (this._index < this._length) {
|
||
try {
|
||
this.getEntry();
|
||
} catch (e) {
|
||
if (e instanceof L10nError) {
|
||
this.getJunkEntry();
|
||
if (!this.emit) {
|
||
throw e;
|
||
}
|
||
} else {
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
if (this._index < this._length) {
|
||
this.getWS();
|
||
}
|
||
}
|
||
|
||
return this.entries;
|
||
},
|
||
|
||
getEntry: function () {
|
||
if (this._source[this._index] === '<') {
|
||
++this._index;
|
||
var id = this.getIdentifier();
|
||
if (this._source[this._index] === '[') {
|
||
++this._index;
|
||
return this.getEntity(id, this.getItemList(this.getExpression, ']'));
|
||
}
|
||
return this.getEntity(id);
|
||
}
|
||
|
||
if (this._source.startsWith('/*', this._index)) {
|
||
return this.getComment();
|
||
}
|
||
|
||
throw this.error('Invalid entry');
|
||
},
|
||
|
||
getEntity: function (id, index) {
|
||
if (!this.getRequiredWS()) {
|
||
throw this.error('Expected white space');
|
||
}
|
||
|
||
var ch = this._source[this._index];
|
||
var hasIndex = index !== undefined;
|
||
var value = this.getValue(ch, hasIndex, hasIndex);
|
||
var attrs = undefined;
|
||
|
||
if (value === undefined) {
|
||
if (ch === '>') {
|
||
throw this.error('Expected ">"');
|
||
}
|
||
attrs = this.getAttributes();
|
||
} else {
|
||
var ws1 = this.getRequiredWS();
|
||
if (this._source[this._index] !== '>') {
|
||
if (!ws1) {
|
||
throw this.error('Expected ">"');
|
||
}
|
||
attrs = this.getAttributes();
|
||
}
|
||
}
|
||
|
||
++this._index;
|
||
|
||
if (id in this.entries) {
|
||
throw this.error('Duplicate entry ID "' + id, 'duplicateerror');
|
||
}
|
||
if (!attrs && !index && typeof value === 'string') {
|
||
this.entries[id] = value;
|
||
} else {
|
||
this.entries[id] = {
|
||
value: value,
|
||
attrs: attrs,
|
||
index: index
|
||
};
|
||
}
|
||
},
|
||
|
||
getValue: function () {
|
||
var ch = arguments.length <= 0 || arguments[0] === undefined ? this._source[this._index] : arguments[0];
|
||
var index = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
|
||
var required = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2];
|
||
|
||
switch (ch) {
|
||
case '\'':
|
||
case '"':
|
||
return this.getString(ch, 1);
|
||
case '{':
|
||
return this.getHash(index);
|
||
}
|
||
|
||
if (required) {
|
||
throw this.error('Unknown value type');
|
||
}
|
||
|
||
return undefined;
|
||
},
|
||
|
||
getWS: function () {
|
||
var cc = this._source.charCodeAt(this._index);
|
||
|
||
while (cc === 32 || cc === 10 || cc === 9 || cc === 13) {
|
||
cc = this._source.charCodeAt(++this._index);
|
||
}
|
||
},
|
||
|
||
getRequiredWS: function () {
|
||
var pos = this._index;
|
||
var cc = this._source.charCodeAt(pos);
|
||
|
||
while (cc === 32 || cc === 10 || cc === 9 || cc === 13) {
|
||
cc = this._source.charCodeAt(++this._index);
|
||
}
|
||
return this._index !== pos;
|
||
},
|
||
|
||
getIdentifier: function () {
|
||
var start = this._index;
|
||
var cc = this._source.charCodeAt(this._index);
|
||
|
||
if (cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90 || cc === 95) {
|
||
cc = this._source.charCodeAt(++this._index);
|
||
} else {
|
||
throw this.error('Identifier has to start with [a-zA-Z_]');
|
||
}
|
||
|
||
while (cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90 || cc >= 48 && cc <= 57 || cc === 95) {
|
||
cc = this._source.charCodeAt(++this._index);
|
||
}
|
||
|
||
return this._source.slice(start, this._index);
|
||
},
|
||
|
||
getUnicodeChar: function () {
|
||
for (var i = 0; i < 4; i++) {
|
||
var cc = this._source.charCodeAt(++this._index);
|
||
if (cc > 96 && cc < 103 || cc > 64 && cc < 71 || cc > 47 && cc < 58) {
|
||
continue;
|
||
}
|
||
throw this.error('Illegal unicode escape sequence');
|
||
}
|
||
this._index++;
|
||
return String.fromCharCode(parseInt(this._source.slice(this._index - 4, this._index), 16));
|
||
},
|
||
|
||
stringRe: /"|'|{{|\\/g,
|
||
getString: function (opchar, opcharLen) {
|
||
var body = [];
|
||
var placeables = 0;
|
||
|
||
this._index += opcharLen;
|
||
var start = this._index;
|
||
|
||
var bufStart = start;
|
||
var buf = '';
|
||
|
||
while (true) {
|
||
this.stringRe.lastIndex = this._index;
|
||
var match = this.stringRe.exec(this._source);
|
||
|
||
if (!match) {
|
||
throw this.error('Unclosed string literal');
|
||
}
|
||
|
||
if (match[0] === '"' || match[0] === '\'') {
|
||
if (match[0] !== opchar) {
|
||
this._index += opcharLen;
|
||
continue;
|
||
}
|
||
this._index = match.index + opcharLen;
|
||
break;
|
||
}
|
||
|
||
if (match[0] === '{{') {
|
||
if (placeables > MAX_PLACEABLES$1 - 1) {
|
||
throw this.error('Too many placeables, maximum allowed is ' + MAX_PLACEABLES$1);
|
||
}
|
||
placeables++;
|
||
if (match.index > bufStart || buf.length > 0) {
|
||
body.push(buf + this._source.slice(bufStart, match.index));
|
||
buf = '';
|
||
}
|
||
this._index = match.index + 2;
|
||
this.getWS();
|
||
body.push(this.getExpression());
|
||
this.getWS();
|
||
this._index += 2;
|
||
bufStart = this._index;
|
||
continue;
|
||
}
|
||
|
||
if (match[0] === '\\') {
|
||
this._index = match.index + 1;
|
||
var ch2 = this._source[this._index];
|
||
if (ch2 === 'u') {
|
||
buf += this._source.slice(bufStart, match.index) + this.getUnicodeChar();
|
||
} else if (ch2 === opchar || ch2 === '\\') {
|
||
buf += this._source.slice(bufStart, match.index) + ch2;
|
||
this._index++;
|
||
} else if (this._source.startsWith('{{', this._index)) {
|
||
buf += this._source.slice(bufStart, match.index) + '{{';
|
||
this._index += 2;
|
||
} else {
|
||
throw this.error('Illegal escape sequence');
|
||
}
|
||
bufStart = this._index;
|
||
}
|
||
}
|
||
|
||
if (body.length === 0) {
|
||
return buf + this._source.slice(bufStart, this._index - opcharLen);
|
||
}
|
||
|
||
if (this._index - opcharLen > bufStart || buf.length > 0) {
|
||
body.push(buf + this._source.slice(bufStart, this._index - opcharLen));
|
||
}
|
||
|
||
return body;
|
||
},
|
||
|
||
getAttributes: function () {
|
||
var attrs = Object.create(null);
|
||
|
||
while (true) {
|
||
this.getAttribute(attrs);
|
||
var ws1 = this.getRequiredWS();
|
||
var ch = this._source.charAt(this._index);
|
||
if (ch === '>') {
|
||
break;
|
||
} else if (!ws1) {
|
||
throw this.error('Expected ">"');
|
||
}
|
||
}
|
||
return attrs;
|
||
},
|
||
|
||
getAttribute: function (attrs) {
|
||
var key = this.getIdentifier();
|
||
var index = undefined;
|
||
|
||
if (this._source[this._index] === '[') {
|
||
++this._index;
|
||
this.getWS();
|
||
index = this.getItemList(this.getExpression, ']');
|
||
}
|
||
this.getWS();
|
||
if (this._source[this._index] !== ':') {
|
||
throw this.error('Expected ":"');
|
||
}
|
||
++this._index;
|
||
this.getWS();
|
||
var hasIndex = index !== undefined;
|
||
var value = this.getValue(undefined, hasIndex);
|
||
|
||
if (key in attrs) {
|
||
throw this.error('Duplicate attribute "' + key, 'duplicateerror');
|
||
}
|
||
|
||
if (!index && typeof value === 'string') {
|
||
attrs[key] = value;
|
||
} else {
|
||
attrs[key] = {
|
||
value: value,
|
||
index: index
|
||
};
|
||
}
|
||
},
|
||
|
||
getHash: function (index) {
|
||
var items = Object.create(null);
|
||
|
||
++this._index;
|
||
this.getWS();
|
||
|
||
var defKey = undefined;
|
||
|
||
while (true) {
|
||
var _getHashItem = this.getHashItem();
|
||
|
||
var key = _getHashItem[0];
|
||
var value = _getHashItem[1];
|
||
var def = _getHashItem[2];
|
||
|
||
items[key] = value;
|
||
|
||
if (def) {
|
||
if (defKey) {
|
||
throw this.error('Default item redefinition forbidden');
|
||
}
|
||
defKey = key;
|
||
}
|
||
this.getWS();
|
||
|
||
var comma = this._source[this._index] === ',';
|
||
if (comma) {
|
||
++this._index;
|
||
this.getWS();
|
||
}
|
||
if (this._source[this._index] === '}') {
|
||
++this._index;
|
||
break;
|
||
}
|
||
if (!comma) {
|
||
throw this.error('Expected "}"');
|
||
}
|
||
}
|
||
|
||
if (defKey) {
|
||
items.__default = defKey;
|
||
} else if (!index) {
|
||
throw this.error('Unresolvable Hash Value');
|
||
}
|
||
|
||
return items;
|
||
},
|
||
|
||
getHashItem: function () {
|
||
var defItem = false;
|
||
if (this._source[this._index] === '*') {
|
||
++this._index;
|
||
defItem = true;
|
||
}
|
||
|
||
var key = this.getIdentifier();
|
||
this.getWS();
|
||
if (this._source[this._index] !== ':') {
|
||
throw this.error('Expected ":"');
|
||
}
|
||
++this._index;
|
||
this.getWS();
|
||
|
||
return [key, this.getValue(), defItem];
|
||
},
|
||
|
||
getComment: function () {
|
||
this._index += 2;
|
||
var start = this._index;
|
||
var end = this._source.indexOf('*/', start);
|
||
|
||
if (end === -1) {
|
||
throw this.error('Comment without a closing tag');
|
||
}
|
||
|
||
this._index = end + 2;
|
||
},
|
||
|
||
getExpression: function () {
|
||
var exp = this.getPrimaryExpression();
|
||
|
||
while (true) {
|
||
var ch = this._source[this._index];
|
||
if (ch === '.' || ch === '[') {
|
||
++this._index;
|
||
exp = this.getPropertyExpression(exp, ch === '[');
|
||
} else if (ch === '(') {
|
||
++this._index;
|
||
exp = this.getCallExpression(exp);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return exp;
|
||
},
|
||
|
||
getPropertyExpression: function (idref, computed) {
|
||
var exp = undefined;
|
||
|
||
if (computed) {
|
||
this.getWS();
|
||
exp = this.getExpression();
|
||
this.getWS();
|
||
if (this._source[this._index] !== ']') {
|
||
throw this.error('Expected "]"');
|
||
}
|
||
++this._index;
|
||
} else {
|
||
exp = this.getIdentifier();
|
||
}
|
||
|
||
return {
|
||
type: 'prop',
|
||
expr: idref,
|
||
prop: exp,
|
||
cmpt: computed
|
||
};
|
||
},
|
||
|
||
getCallExpression: function (callee) {
|
||
this.getWS();
|
||
|
||
return {
|
||
type: 'call',
|
||
expr: callee,
|
||
args: this.getItemList(this.getExpression, ')')
|
||
};
|
||
},
|
||
|
||
getPrimaryExpression: function () {
|
||
var ch = this._source[this._index];
|
||
|
||
switch (ch) {
|
||
case '$':
|
||
++this._index;
|
||
return {
|
||
type: 'var',
|
||
name: this.getIdentifier()
|
||
};
|
||
case '@':
|
||
++this._index;
|
||
return {
|
||
type: 'glob',
|
||
name: this.getIdentifier()
|
||
};
|
||
default:
|
||
return {
|
||
type: 'id',
|
||
name: this.getIdentifier()
|
||
};
|
||
}
|
||
},
|
||
|
||
getItemList: function (callback, closeChar) {
|
||
var items = [];
|
||
var closed = false;
|
||
|
||
this.getWS();
|
||
|
||
if (this._source[this._index] === closeChar) {
|
||
++this._index;
|
||
closed = true;
|
||
}
|
||
|
||
while (!closed) {
|
||
items.push(callback.call(this));
|
||
this.getWS();
|
||
var ch = this._source.charAt(this._index);
|
||
switch (ch) {
|
||
case ',':
|
||
++this._index;
|
||
this.getWS();
|
||
break;
|
||
case closeChar:
|
||
++this._index;
|
||
closed = true;
|
||
break;
|
||
default:
|
||
throw this.error('Expected "," or "' + closeChar + '"');
|
||
}
|
||
}
|
||
|
||
return items;
|
||
},
|
||
|
||
getJunkEntry: function () {
|
||
var pos = this._index;
|
||
var nextEntity = this._source.indexOf('<', pos);
|
||
var nextComment = this._source.indexOf('/*', pos);
|
||
|
||
if (nextEntity === -1) {
|
||
nextEntity = this._length;
|
||
}
|
||
if (nextComment === -1) {
|
||
nextComment = this._length;
|
||
}
|
||
|
||
var nextEntry = Math.min(nextEntity, nextComment);
|
||
|
||
this._index = nextEntry;
|
||
},
|
||
|
||
error: function (message) {
|
||
var type = arguments.length <= 1 || arguments[1] === undefined ? 'parsererror' : arguments[1];
|
||
|
||
var pos = this._index;
|
||
|
||
var start = this._source.lastIndexOf('<', pos - 1);
|
||
var lastClose = this._source.lastIndexOf('>', pos - 1);
|
||
start = lastClose > start ? lastClose + 1 : start;
|
||
var context = this._source.slice(start, pos + 10);
|
||
|
||
var msg = message + ' at pos ' + pos + ': `' + context + '`';
|
||
var err = new L10nError(msg);
|
||
if (this.emit) {
|
||
this.emit(type, err);
|
||
}
|
||
return err;
|
||
}
|
||
};
|
||
|
||
function emit(listeners) {
|
||
var _this7 = this;
|
||
|
||
for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
|
||
args[_key3 - 1] = arguments[_key3];
|
||
}
|
||
|
||
var type = args.shift();
|
||
|
||
if (listeners['*']) {
|
||
listeners['*'].slice().forEach(function (listener) {
|
||
return listener.apply(_this7, args);
|
||
});
|
||
}
|
||
|
||
if (listeners[type]) {
|
||
listeners[type].slice().forEach(function (listener) {
|
||
return listener.apply(_this7, args);
|
||
});
|
||
}
|
||
}
|
||
|
||
function addEventListener(listeners, type, listener) {
|
||
if (!(type in listeners)) {
|
||
listeners[type] = [];
|
||
}
|
||
listeners[type].push(listener);
|
||
}
|
||
|
||
function removeEventListener(listeners, type, listener) {
|
||
var typeListeners = listeners[type];
|
||
var pos = typeListeners.indexOf(listener);
|
||
if (pos === -1) {
|
||
return;
|
||
}
|
||
|
||
typeListeners.splice(pos, 1);
|
||
}
|
||
|
||
var Env = (function () {
|
||
function Env(fetchResource) {
|
||
_classCallCheck(this, Env);
|
||
|
||
this.fetchResource = fetchResource;
|
||
|
||
this.resCache = new Map();
|
||
this.resRefs = new Map();
|
||
this.numberFormatters = null;
|
||
this.parsers = {
|
||
properties: PropertiesParser,
|
||
l20n: L20nParser
|
||
};
|
||
|
||
var listeners = {};
|
||
this.emit = emit.bind(this, listeners);
|
||
this.addEventListener = addEventListener.bind(this, listeners);
|
||
this.removeEventListener = removeEventListener.bind(this, listeners);
|
||
}
|
||
|
||
Env.prototype.createContext = function createContext(langs, resIds) {
|
||
var _this8 = this;
|
||
|
||
var ctx = new Context(this, langs, resIds);
|
||
resIds.forEach(function (resId) {
|
||
var usedBy = _this8.resRefs.get(resId) || 0;
|
||
_this8.resRefs.set(resId, usedBy + 1);
|
||
});
|
||
|
||
return ctx;
|
||
};
|
||
|
||
Env.prototype.destroyContext = function destroyContext(ctx) {
|
||
var _this9 = this;
|
||
|
||
ctx.resIds.forEach(function (resId) {
|
||
var usedBy = _this9.resRefs.get(resId) || 0;
|
||
|
||
if (usedBy > 1) {
|
||
return _this9.resRefs.set(resId, usedBy - 1);
|
||
}
|
||
|
||
_this9.resRefs.delete(resId);
|
||
_this9.resCache.forEach(function (val, key) {
|
||
return key.startsWith(resId) ? _this9.resCache.delete(key) : null;
|
||
});
|
||
});
|
||
};
|
||
|
||
Env.prototype._parse = function _parse(syntax, lang, data) {
|
||
var _this10 = this;
|
||
|
||
var parser = this.parsers[syntax];
|
||
if (!parser) {
|
||
return data;
|
||
}
|
||
|
||
var emitAndAmend = function (type, err) {
|
||
return _this10.emit(type, amendError$1(lang, err));
|
||
};
|
||
return parser.parse(emitAndAmend, data);
|
||
};
|
||
|
||
Env.prototype._create = function _create(lang, entries) {
|
||
if (lang.src !== 'pseudo') {
|
||
return entries;
|
||
}
|
||
|
||
var pseudoentries = Object.create(null);
|
||
for (var key in entries) {
|
||
pseudoentries[key] = walkEntry(entries[key], pseudo[lang.code].process);
|
||
}
|
||
return pseudoentries;
|
||
};
|
||
|
||
Env.prototype._getResource = function _getResource(lang, res) {
|
||
var _this11 = this;
|
||
|
||
var cache = this.resCache;
|
||
var id = res + lang.code + lang.src;
|
||
|
||
if (cache.has(id)) {
|
||
return cache.get(id);
|
||
}
|
||
|
||
var syntax = res.substr(res.lastIndexOf('.') + 1);
|
||
|
||
var saveEntries = function (data) {
|
||
var entries = _this11._parse(syntax, lang, data);
|
||
cache.set(id, _this11._create(lang, entries));
|
||
};
|
||
|
||
var recover = function (err) {
|
||
err.lang = lang;
|
||
_this11.emit('fetcherror', err);
|
||
cache.set(id, err);
|
||
};
|
||
|
||
var langToFetch = lang.src === 'pseudo' ? { code: 'en-US', src: 'app', ver: lang.ver } : lang;
|
||
|
||
var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover);
|
||
|
||
cache.set(id, resource);
|
||
|
||
return resource;
|
||
};
|
||
|
||
return Env;
|
||
})();
|
||
|
||
function amendError$1(lang, err) {
|
||
err.lang = lang;
|
||
return err;
|
||
}
|
||
|
||
var KNOWN_MACROS = ['plural'];
|
||
var MAX_PLACEABLE_LENGTH = 2500;
|
||
|
||
var nonLatin1 = /[^\x01-\xFF]/;
|
||
|
||
var FSI = '';
|
||
var PDI = '';
|
||
|
||
var resolutionChain = new WeakSet();
|
||
|
||
function createEntry(node) {
|
||
var keys = Object.keys(node);
|
||
|
||
if (typeof node.$v === 'string' && keys.length === 2) {
|
||
return node.$v;
|
||
}
|
||
|
||
var attrs = undefined;
|
||
|
||
for (var i = 0, key = undefined; key = keys[i]; i++) {
|
||
if (key[0] === '$') {
|
||
continue;
|
||
}
|
||
|
||
if (!attrs) {
|
||
attrs = Object.create(null);
|
||
}
|
||
attrs[key] = createAttribute(node[key]);
|
||
}
|
||
|
||
return {
|
||
value: node.$v !== undefined ? node.$v : null,
|
||
index: node.$x || null,
|
||
attrs: attrs || null
|
||
};
|
||
}
|
||
|
||
function createAttribute(node) {
|
||
if (typeof node === 'string') {
|
||
return node;
|
||
}
|
||
|
||
return {
|
||
value: node.$v || (node !== undefined ? node : null),
|
||
index: node.$x || null
|
||
};
|
||
}
|
||
|
||
function format(ctx, lang, args, entity) {
|
||
if (typeof entity === 'string') {
|
||
return [{}, entity];
|
||
}
|
||
|
||
if (resolutionChain.has(entity)) {
|
||
throw new L10nError('Cyclic reference detected');
|
||
}
|
||
|
||
resolutionChain.add(entity);
|
||
|
||
var rv = undefined;
|
||
|
||
try {
|
||
rv = resolveValue({}, ctx, lang, args, entity.value, entity.index);
|
||
} finally {
|
||
resolutionChain.delete(entity);
|
||
}
|
||
return rv;
|
||
}
|
||
|
||
function resolveIdentifier(ctx, lang, args, id) {
|
||
if (KNOWN_MACROS.indexOf(id) > -1) {
|
||
return [{}, ctx._getMacro(lang, id)];
|
||
}
|
||
|
||
if (args && args.hasOwnProperty(id)) {
|
||
if (typeof args[id] === 'string' || typeof args[id] === 'number' && !isNaN(args[id])) {
|
||
return [{}, args[id]];
|
||
} else {
|
||
throw new L10nError('Arg must be a string or a number: ' + id);
|
||
}
|
||
}
|
||
|
||
if (id === '__proto__') {
|
||
throw new L10nError('Illegal id: ' + id);
|
||
}
|
||
|
||
var entity = ctx._getEntity(lang, id);
|
||
|
||
if (entity) {
|
||
return format(ctx, lang, args, entity);
|
||
}
|
||
|
||
throw new L10nError('Unknown reference: ' + id);
|
||
}
|
||
|
||
function subPlaceable(locals, ctx, lang, args, id) {
|
||
var res = undefined;
|
||
|
||
try {
|
||
res = resolveIdentifier(ctx, lang, args, id);
|
||
} catch (err) {
|
||
return [{ error: err }, '{{ ' + id + ' }}'];
|
||
}
|
||
|
||
var value = res[1];
|
||
|
||
if (typeof value === 'number') {
|
||
return res;
|
||
}
|
||
|
||
if (typeof value === 'string') {
|
||
if (value.length >= MAX_PLACEABLE_LENGTH) {
|
||
throw new L10nError('Too many characters in placeable (' + value.length + ', max allowed is ' + MAX_PLACEABLE_LENGTH + ')');
|
||
}
|
||
|
||
if (locals.contextIsNonLatin1 || value.match(nonLatin1)) {
|
||
res[1] = FSI + value + PDI;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
return [{}, '{{ ' + id + ' }}'];
|
||
}
|
||
|
||
function interpolate(locals, ctx, lang, args, arr) {
|
||
return arr.reduce(function (_ref4, cur) {
|
||
var localsSeq = _ref4[0];
|
||
var valueSeq = _ref4[1];
|
||
|
||
if (typeof cur === 'string') {
|
||
return [localsSeq, valueSeq + cur];
|
||
} else if (cur.t === 'idOrVar') {
|
||
var _subPlaceable = subPlaceable(locals, ctx, lang, args, cur.v);
|
||
|
||
var value = _subPlaceable[1];
|
||
|
||
return [localsSeq, valueSeq + value];
|
||
}
|
||
}, [locals, '']);
|
||
}
|
||
|
||
function resolveSelector(ctx, lang, args, expr, index) {
|
||
var selectorName = index[0].v;
|
||
var selector = resolveIdentifier(ctx, lang, args, selectorName)[1];
|
||
|
||
if (typeof selector !== 'function') {
|
||
return selector;
|
||
}
|
||
|
||
var argValue = index[1] ? resolveIdentifier(ctx, lang, args, index[1])[1] : undefined;
|
||
|
||
if (selectorName === 'plural') {
|
||
if (argValue === 0 && 'zero' in expr) {
|
||
return 'zero';
|
||
}
|
||
if (argValue === 1 && 'one' in expr) {
|
||
return 'one';
|
||
}
|
||
if (argValue === 2 && 'two' in expr) {
|
||
return 'two';
|
||
}
|
||
}
|
||
|
||
return selector(argValue);
|
||
}
|
||
|
||
function resolveValue(locals, ctx, lang, args, expr, index) {
|
||
if (!expr) {
|
||
return [locals, expr];
|
||
}
|
||
|
||
if (typeof expr === 'string' || typeof expr === 'boolean' || typeof expr === 'number') {
|
||
return [locals, expr];
|
||
}
|
||
|
||
if (Array.isArray(expr)) {
|
||
locals.contextIsNonLatin1 = expr.some(function ($_) {
|
||
return typeof $_ === 'string' && $_.match(nonLatin1);
|
||
});
|
||
return interpolate(locals, ctx, lang, args, expr);
|
||
}
|
||
|
||
if (index) {
|
||
var selector = resolveSelector(ctx, lang, args, expr, index);
|
||
if (expr.hasOwnProperty(selector)) {
|
||
return resolveValue(locals, ctx, lang, args, expr[selector]);
|
||
}
|
||
}
|
||
|
||
if ('other' in expr) {
|
||
return resolveValue(locals, ctx, lang, args, expr.other);
|
||
}
|
||
|
||
throw new L10nError('Unresolvable value');
|
||
}
|
||
|
||
function LegacyContext(env, langs, resIds) {
|
||
Context.call(this, env, langs, resIds);
|
||
}
|
||
|
||
LegacyContext.prototype = Object.create(Context.prototype);
|
||
|
||
LegacyContext.prototype._formatTuple = function (lang, args, entity, id, key) {
|
||
try {
|
||
return format(this, lang, args, entity);
|
||
} catch (err) {
|
||
err.id = key ? id + '::' + key : id;
|
||
err.lang = lang;
|
||
this.env.emit('resolveerror', err, this);
|
||
return [{ error: err }, err.id];
|
||
}
|
||
};
|
||
|
||
var MAX_PLACEABLES$2 = 100;
|
||
|
||
var PropertiesParser$1 = {
|
||
patterns: null,
|
||
entryIds: null,
|
||
|
||
init: function () {
|
||
this.patterns = {
|
||
comment: /^\s*#|^\s*$/,
|
||
entity: /^([^=\s]+)\s*=\s*(.*)$/,
|
||
multiline: /[^\\]\\$/,
|
||
index: /\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,
|
||
unicode: /\\u([0-9a-fA-F]{1,4})/g,
|
||
entries: /[^\r\n]+/g,
|
||
controlChars: /\\([\\\n\r\t\b\f\{\}\"\'])/g,
|
||
placeables: /\{\{\s*([^\s]*?)\s*\}\}/
|
||
};
|
||
},
|
||
|
||
parse: function (emit, source) {
|
||
if (!this.patterns) {
|
||
this.init();
|
||
}
|
||
|
||
var ast = [];
|
||
this.entryIds = Object.create(null);
|
||
|
||
var entries = source.match(this.patterns.entries);
|
||
if (!entries) {
|
||
return ast;
|
||
}
|
||
for (var i = 0; i < entries.length; i++) {
|
||
var line = entries[i];
|
||
|
||
if (this.patterns.comment.test(line)) {
|
||
continue;
|
||
}
|
||
|
||
while (this.patterns.multiline.test(line) && i < entries.length) {
|
||
line = line.slice(0, -1) + entries[++i].trim();
|
||
}
|
||
|
||
var entityMatch = line.match(this.patterns.entity);
|
||
if (entityMatch) {
|
||
try {
|
||
this.parseEntity(entityMatch[1], entityMatch[2], ast);
|
||
} catch (e) {
|
||
if (emit) {
|
||
emit('parseerror', e);
|
||
} else {
|
||
throw e;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return ast;
|
||
},
|
||
|
||
parseEntity: function (id, value, ast) {
|
||
var name = undefined,
|
||
key = undefined;
|
||
|
||
var pos = id.indexOf('[');
|
||
if (pos !== -1) {
|
||
name = id.substr(0, pos);
|
||
key = id.substring(pos + 1, id.length - 1);
|
||
} else {
|
||
name = id;
|
||
key = null;
|
||
}
|
||
|
||
var nameElements = name.split('.');
|
||
|
||
if (nameElements.length > 2) {
|
||
throw new L10nError('Error in ID: "' + name + '".' + ' Nested attributes are not supported.');
|
||
}
|
||
|
||
var attr = undefined;
|
||
if (nameElements.length > 1) {
|
||
name = nameElements[0];
|
||
attr = nameElements[1];
|
||
|
||
if (attr[0] === '$') {
|
||
throw new L10nError('Attribute can\'t start with "$"', id);
|
||
}
|
||
} else {
|
||
attr = null;
|
||
}
|
||
|
||
this.setEntityValue(name, attr, key, this.unescapeString(value), ast);
|
||
},
|
||
|
||
setEntityValue: function (id, attr, key, rawValue, ast) {
|
||
var pos = undefined,
|
||
v = undefined;
|
||
|
||
var value = rawValue.indexOf('{{') > -1 ? this.parseString(rawValue) : rawValue;
|
||
|
||
if (attr) {
|
||
pos = this.entryIds[id];
|
||
if (pos === undefined) {
|
||
v = { $i: id };
|
||
if (key) {
|
||
v[attr] = { $v: {} };
|
||
v[attr].$v[key] = value;
|
||
} else {
|
||
v[attr] = value;
|
||
}
|
||
ast.push(v);
|
||
this.entryIds[id] = ast.length - 1;
|
||
return;
|
||
}
|
||
if (key) {
|
||
if (typeof ast[pos][attr] === 'string') {
|
||
ast[pos][attr] = {
|
||
$x: this.parseIndex(ast[pos][attr]),
|
||
$v: {}
|
||
};
|
||
}
|
||
ast[pos][attr].$v[key] = value;
|
||
return;
|
||
}
|
||
ast[pos][attr] = value;
|
||
return;
|
||
}
|
||
|
||
if (key) {
|
||
pos = this.entryIds[id];
|
||
if (pos === undefined) {
|
||
v = {};
|
||
v[key] = value;
|
||
ast.push({ $i: id, $v: v });
|
||
this.entryIds[id] = ast.length - 1;
|
||
return;
|
||
}
|
||
if (typeof ast[pos].$v === 'string') {
|
||
ast[pos].$x = this.parseIndex(ast[pos].$v);
|
||
ast[pos].$v = {};
|
||
}
|
||
ast[pos].$v[key] = value;
|
||
return;
|
||
}
|
||
|
||
ast.push({ $i: id, $v: value });
|
||
this.entryIds[id] = ast.length - 1;
|
||
},
|
||
|
||
parseString: function (str) {
|
||
var chunks = str.split(this.patterns.placeables);
|
||
var complexStr = [];
|
||
|
||
var len = chunks.length;
|
||
var placeablesCount = (len - 1) / 2;
|
||
|
||
if (placeablesCount >= MAX_PLACEABLES$2) {
|
||
throw new L10nError('Too many placeables (' + placeablesCount + ', max allowed is ' + MAX_PLACEABLES$2 + ')');
|
||
}
|
||
|
||
for (var i = 0; i < chunks.length; i++) {
|
||
if (chunks[i].length === 0) {
|
||
continue;
|
||
}
|
||
if (i % 2 === 1) {
|
||
complexStr.push({ t: 'idOrVar', v: chunks[i] });
|
||
} else {
|
||
complexStr.push(chunks[i]);
|
||
}
|
||
}
|
||
return complexStr;
|
||
},
|
||
|
||
unescapeString: function (str) {
|
||
if (str.lastIndexOf('\\') !== -1) {
|
||
str = str.replace(this.patterns.controlChars, '$1');
|
||
}
|
||
return str.replace(this.patterns.unicode, function (match, token) {
|
||
return String.fromCodePoint(parseInt(token, 16));
|
||
});
|
||
},
|
||
|
||
parseIndex: function (str) {
|
||
var match = str.match(this.patterns.index);
|
||
if (!match) {
|
||
throw new L10nError('Malformed index');
|
||
}
|
||
if (match[2]) {
|
||
return [{ t: 'idOrVar', v: match[1] }, match[2]];
|
||
} else {
|
||
return [{ t: 'idOrVar', v: match[1] }];
|
||
}
|
||
}
|
||
};
|
||
|
||
function walkContent(node, fn) {
|
||
if (typeof node === 'string') {
|
||
return fn(node);
|
||
}
|
||
|
||
if (node.t === 'idOrVar') {
|
||
return node;
|
||
}
|
||
|
||
var rv = Array.isArray(node) ? [] : {};
|
||
var keys = Object.keys(node);
|
||
|
||
for (var i = 0, key = undefined; key = keys[i]; i++) {
|
||
if (key === '$i' || key === '$x') {
|
||
rv[key] = node[key];
|
||
} else {
|
||
rv[key] = walkContent(node[key], fn);
|
||
}
|
||
}
|
||
return rv;
|
||
}
|
||
|
||
function LegacyEnv(fetchResource) {
|
||
Env.call(this, fetchResource);
|
||
this.parsers = {
|
||
properties: PropertiesParser$1
|
||
};
|
||
}
|
||
|
||
LegacyEnv.prototype = Object.create(Env.prototype);
|
||
|
||
LegacyEnv.prototype.createContext = function (langs, resIds) {
|
||
return new LegacyContext(this, langs, resIds);
|
||
};
|
||
|
||
LegacyEnv.prototype._create = function (lang, ast) {
|
||
var entries = Object.create(null);
|
||
var create = lang.src === 'pseudo' ? createPseudoEntry : createEntry;
|
||
|
||
for (var i = 0, node = undefined; node = ast[i]; i++) {
|
||
var id = node.$i;
|
||
if (id in entries) {
|
||
this.emit('duplicateerror', new L10nError('Duplicate string "' + id + '" found in ' + lang.code, id, lang));
|
||
}
|
||
entries[id] = create(node, lang);
|
||
}
|
||
|
||
return entries;
|
||
};
|
||
|
||
function createPseudoEntry(node, lang) {
|
||
return createEntry(walkContent(node, pseudo[lang.code].process));
|
||
}
|
||
|
||
var observerConfig = {
|
||
attributes: true,
|
||
characterData: false,
|
||
childList: true,
|
||
subtree: true,
|
||
attributeFilter: ['data-l10n-id', 'data-l10n-args']
|
||
};
|
||
|
||
var observers = new WeakMap();
|
||
|
||
function disconnect(view, root, allRoots) {
|
||
var obs = observers.get(view);
|
||
if (obs) {
|
||
obs.observer.disconnect();
|
||
if (allRoots) {
|
||
return;
|
||
}
|
||
obs.roots.delete(root);
|
||
obs.roots.forEach(function (other) {
|
||
return obs.observer.observe(other, observerConfig);
|
||
});
|
||
}
|
||
}
|
||
|
||
function reconnect(view) {
|
||
var obs = observers.get(view);
|
||
if (obs) {
|
||
obs.roots.forEach(function (root) {
|
||
return obs.observer.observe(root, observerConfig);
|
||
});
|
||
}
|
||
}
|
||
|
||
var reOverlay = /<|&#?\w+;/;
|
||
|
||
var allowed = {
|
||
elements: ['a', 'em', 'strong', 'small', 's', 'cite', 'q', 'dfn', 'abbr', 'data', 'time', 'code', 'var', 'samp', 'kbd', 'sub', 'sup', 'i', 'b', 'u', 'mark', 'ruby', 'rt', 'rp', 'bdi', 'bdo', 'span', 'br', 'wbr'],
|
||
attributes: {
|
||
global: ['title', 'aria-label', 'aria-valuetext', 'aria-moz-hint'],
|
||
a: ['download'],
|
||
area: ['download', 'alt'],
|
||
|
||
input: ['alt', 'placeholder'],
|
||
menuitem: ['label'],
|
||
menu: ['label'],
|
||
optgroup: ['label'],
|
||
option: ['label'],
|
||
track: ['label'],
|
||
img: ['alt'],
|
||
textarea: ['placeholder'],
|
||
th: ['abbr']
|
||
}
|
||
};
|
||
|
||
function overlayElement(element, translation) {
|
||
var value = translation.value;
|
||
|
||
if (typeof value === 'string') {
|
||
if (!reOverlay.test(value)) {
|
||
element.textContent = value;
|
||
} else {
|
||
var tmpl = element.ownerDocument.createElement('template');
|
||
tmpl.innerHTML = value;
|
||
|
||
overlay(element, tmpl.content);
|
||
}
|
||
}
|
||
|
||
for (var key in translation.attrs) {
|
||
var attrName = camelCaseToDashed(key);
|
||
if (isAttrAllowed({ name: attrName }, element)) {
|
||
element.setAttribute(attrName, translation.attrs[key]);
|
||
}
|
||
}
|
||
}
|
||
|
||
function overlay(sourceElement, translationElement) {
|
||
var result = translationElement.ownerDocument.createDocumentFragment();
|
||
var k = undefined,
|
||
attr = undefined;
|
||
|
||
var childElement = undefined;
|
||
while (childElement = translationElement.childNodes[0]) {
|
||
translationElement.removeChild(childElement);
|
||
|
||
if (childElement.nodeType === childElement.TEXT_NODE) {
|
||
result.appendChild(childElement);
|
||
continue;
|
||
}
|
||
|
||
var index = getIndexOfType(childElement);
|
||
var sourceChild = getNthElementOfType(sourceElement, childElement, index);
|
||
if (sourceChild) {
|
||
overlay(sourceChild, childElement);
|
||
result.appendChild(sourceChild);
|
||
continue;
|
||
}
|
||
|
||
if (isElementAllowed(childElement)) {
|
||
var sanitizedChild = childElement.ownerDocument.createElement(childElement.nodeName);
|
||
overlay(sanitizedChild, childElement);
|
||
result.appendChild(sanitizedChild);
|
||
continue;
|
||
}
|
||
|
||
result.appendChild(translationElement.ownerDocument.createTextNode(childElement.textContent));
|
||
}
|
||
|
||
sourceElement.textContent = '';
|
||
sourceElement.appendChild(result);
|
||
|
||
if (translationElement.attributes) {
|
||
for (k = 0, attr; attr = translationElement.attributes[k]; k++) {
|
||
if (isAttrAllowed(attr, sourceElement)) {
|
||
sourceElement.setAttribute(attr.name, attr.value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function isElementAllowed(element) {
|
||
return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1;
|
||
}
|
||
|
||
function isAttrAllowed(attr, element) {
|
||
var attrName = attr.name.toLowerCase();
|
||
var tagName = element.tagName.toLowerCase();
|
||
|
||
if (allowed.attributes.global.indexOf(attrName) !== -1) {
|
||
return true;
|
||
}
|
||
|
||
if (!allowed.attributes[tagName]) {
|
||
return false;
|
||
}
|
||
|
||
if (allowed.attributes[tagName].indexOf(attrName) !== -1) {
|
||
return true;
|
||
}
|
||
|
||
if (tagName === 'input' && attrName === 'value') {
|
||
var type = element.type.toLowerCase();
|
||
if (type === 'submit' || type === 'button' || type === 'reset') {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function getNthElementOfType(context, element, index) {
|
||
var nthOfType = 0;
|
||
for (var i = 0, child = undefined; child = context.children[i]; i++) {
|
||
if (child.nodeType === child.ELEMENT_NODE && child.tagName === element.tagName) {
|
||
if (nthOfType === index) {
|
||
return child;
|
||
}
|
||
nthOfType++;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function getIndexOfType(element) {
|
||
var index = 0;
|
||
var child = undefined;
|
||
while (child = element.previousElementSibling) {
|
||
if (child.tagName === element.tagName) {
|
||
index++;
|
||
}
|
||
}
|
||
return index;
|
||
}
|
||
|
||
function camelCaseToDashed(string) {
|
||
if (string === 'ariaValueText') {
|
||
return 'aria-valuetext';
|
||
}
|
||
|
||
return string.replace(/[A-Z]/g, function (match) {
|
||
return '-' + match.toLowerCase();
|
||
}).replace(/^-/, '');
|
||
}
|
||
|
||
var reHtml = /[&<>]/g;
|
||
var htmlEntities = {
|
||
'&': '&',
|
||
'<': '<',
|
||
'>': '>'
|
||
};
|
||
|
||
function getTranslatables(element) {
|
||
var nodes = Array.from(element.querySelectorAll('[data-l10n-id]'));
|
||
|
||
if (typeof element.hasAttribute === 'function' && element.hasAttribute('data-l10n-id')) {
|
||
nodes.push(element);
|
||
}
|
||
|
||
return nodes;
|
||
}
|
||
|
||
function translateFragment(view, frag) {
|
||
return translateElements(view, getTranslatables(frag));
|
||
}
|
||
|
||
function getElementsTranslation(view, elems) {
|
||
var keys = elems.map(function (elem) {
|
||
var id = elem.getAttribute('data-l10n-id');
|
||
var args = elem.getAttribute('data-l10n-args');
|
||
return args ? [id, JSON.parse(args.replace(reHtml, function (match) {
|
||
return htmlEntities[match];
|
||
}))] : id;
|
||
});
|
||
|
||
return view.formatEntities.apply(view, keys);
|
||
}
|
||
|
||
function translateElements(view, elements) {
|
||
return getElementsTranslation(view, elements).then(function (translations) {
|
||
return applyTranslations(view, elements, translations);
|
||
});
|
||
}
|
||
|
||
function applyTranslations(view, elems, translations) {
|
||
disconnect(view, null, true);
|
||
for (var i = 0; i < elems.length; i++) {
|
||
overlayElement(elems[i], translations[i]);
|
||
}
|
||
reconnect(view);
|
||
}
|
||
|
||
function getResourceLinks(head) {
|
||
return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), function (el) {
|
||
return el.getAttribute('href');
|
||
});
|
||
}
|
||
|
||
function serializeContext(ctx) {
|
||
var lang = ctx.langs[0];
|
||
var cache = ctx.env.resCache;
|
||
return ctx.resIds.reduceRight(function (_ref5, cur) {
|
||
var errorsSeq = _ref5[0];
|
||
var entriesSeq = _ref5[1];
|
||
|
||
var sourceRes = cache.get(cur + 'en-USapp');
|
||
var langRes = cache.get(cur + lang.code + lang.src);
|
||
|
||
var _serializeEntries = serializeEntries(lang, langRes instanceof L10nError ? {} : langRes, sourceRes instanceof L10nError ? {} : sourceRes);
|
||
|
||
var errors = _serializeEntries[0];
|
||
var entries = _serializeEntries[1];
|
||
|
||
return [errorsSeq.concat(errors), Object.assign(entriesSeq, entries)];
|
||
}, [[], Object.create(null)]);
|
||
}
|
||
|
||
function serializeEntries(lang, langEntries, sourceEntries) {
|
||
var errors = [];
|
||
var entries = Object.create(null);
|
||
|
||
for (var id in sourceEntries) {
|
||
var sourceEntry = sourceEntries[id];
|
||
var langEntry = langEntries[id];
|
||
|
||
if (!langEntry) {
|
||
errors.push(new L10nError('"' + id + '" not found in ' + lang.code, id, lang));
|
||
entries[id] = sourceEntry;
|
||
continue;
|
||
}
|
||
|
||
if (!areEntityStructsEqual(sourceEntry, langEntry)) {
|
||
errors.push(new L10nError('"' + id + '" is malformed in ' + lang.code, id, lang));
|
||
entries[id] = sourceEntry;
|
||
continue;
|
||
}
|
||
|
||
entries[id] = langEntry;
|
||
}
|
||
|
||
return [errors, entries];
|
||
}
|
||
|
||
function resolvesToString(entity) {
|
||
return typeof entity === 'string' || typeof entity.value === 'string' || Array.isArray(entity.value) || typeof entity.value === 'object' && entity.index !== null;
|
||
}
|
||
|
||
function areAttrsEqual(attrs1, attrs2) {
|
||
var keys1 = Object.keys(attrs1 || Object.create(null));
|
||
var keys2 = Object.keys(attrs2 || Object.create(null));
|
||
|
||
if (keys1.length !== keys2.length) {
|
||
return false;
|
||
}
|
||
|
||
for (var i = 0; i < keys1.length; i++) {
|
||
if (keys2.indexOf(keys1[i]) === -1) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
function areEntityStructsEqual(source, translation) {
|
||
if (resolvesToString(source) && !resolvesToString(translation)) {
|
||
return false;
|
||
}
|
||
|
||
if (source.attrs || translation.attrs) {
|
||
return areAttrsEqual(source.attrs, translation.attrs);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
function serializeLegacyContext(ctx) {
|
||
var lang = ctx.langs[0];
|
||
var cache = ctx.env.resCache;
|
||
return ctx.resIds.reduce(function (_ref6, cur) {
|
||
var errorsSeq = _ref6[0];
|
||
var entriesSeq = _ref6[1];
|
||
|
||
var sourceRes = cache.get(cur + 'en-USapp');
|
||
var langRes = cache.get(cur + lang.code + lang.src);
|
||
|
||
var _serializeEntries$1 = serializeEntries$1(lang, langRes instanceof L10nError ? {} : langRes, sourceRes instanceof L10nError ? {} : sourceRes);
|
||
|
||
var errors = _serializeEntries$1[0];
|
||
var entries = _serializeEntries$1[1];
|
||
|
||
return [errorsSeq.concat(errors), entriesSeq.concat(entries)];
|
||
}, [[], []]);
|
||
}
|
||
|
||
function serializeEntries$1(lang, langEntries, sourceEntries) {
|
||
var errors = [];
|
||
var entries = Object.keys(sourceEntries).map(function (id) {
|
||
var sourceEntry = sourceEntries[id];
|
||
var langEntry = langEntries[id];
|
||
|
||
if (!langEntry) {
|
||
errors.push(new L10nError('"' + id + '" not found in ' + lang.code, id, lang));
|
||
return serializeEntry(sourceEntry, id);
|
||
}
|
||
|
||
if (!areEntityStructsEqual$1(sourceEntry, langEntry)) {
|
||
errors.push(new L10nError('"' + id + '" is malformed in ' + lang.code, id, lang));
|
||
return serializeEntry(sourceEntry, id);
|
||
}
|
||
|
||
return serializeEntry(langEntry, id);
|
||
});
|
||
|
||
return [errors, entries];
|
||
}
|
||
|
||
function serializeEntry(entry, id) {
|
||
if (typeof entry === 'string') {
|
||
return { $i: id, $v: entry };
|
||
}
|
||
|
||
var node = {
|
||
$i: id
|
||
};
|
||
|
||
if (entry.value !== null) {
|
||
node.$v = entry.value;
|
||
}
|
||
|
||
if (entry.index !== null) {
|
||
node.$x = entry.index;
|
||
}
|
||
|
||
for (var key in entry.attrs) {
|
||
node[key] = serializeAttribute(entry.attrs[key]);
|
||
}
|
||
|
||
return node;
|
||
}
|
||
|
||
function serializeAttribute(attr) {
|
||
if (typeof attr === 'string') {
|
||
return attr;
|
||
}
|
||
|
||
var node = {};
|
||
|
||
if (attr.value !== null) {
|
||
node.$v = attr.value;
|
||
}
|
||
|
||
if (attr.index !== null) {
|
||
node.$x = attr.index;
|
||
}
|
||
|
||
return node;
|
||
}
|
||
|
||
function resolvesToString$1(entity) {
|
||
return typeof entity === 'string' || typeof entity.value === 'string' || Array.isArray(entity.value) || typeof entity.value === 'object' && entity.index !== null;
|
||
}
|
||
|
||
function areAttrsEqual$1(attrs1, attrs2) {
|
||
var keys1 = Object.keys(attrs1 || Object.create(null));
|
||
var keys2 = Object.keys(attrs2 || Object.create(null));
|
||
|
||
if (keys1.length !== keys2.length) {
|
||
return false;
|
||
}
|
||
|
||
for (var i = 0; i < keys1.length; i++) {
|
||
if (keys2.indexOf(keys1[i]) === -1) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
function areEntityStructsEqual$1(source, translation) {
|
||
if (resolvesToString$1(source) && !resolvesToString$1(translation)) {
|
||
return false;
|
||
}
|
||
|
||
if (source.attrs || translation.attrs) {
|
||
return areAttrsEqual$1(source.attrs, translation.attrs);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
var View = (function () {
|
||
function View(htmloptimizer, fetchResource) {
|
||
_classCallCheck(this, View);
|
||
|
||
this.htmloptimizer = htmloptimizer;
|
||
this.doc = htmloptimizer.document;
|
||
this.resLinks = getResourceLinks(this.doc.head);
|
||
|
||
this.isEnabled = this.doc.querySelector('link[rel="localization"]');
|
||
|
||
this.isLegacy = !this.doc.querySelector('script[src*="l20n"]');
|
||
|
||
var EnvClass = this.isLegacy ? LegacyEnv : Env;
|
||
this.env = new EnvClass(fetchResource);
|
||
this.sourceCtx = this.env.createContext([{ code: 'en-US', src: 'app' }], this.resLinks);
|
||
|
||
this.env.addEventListener('*', amendError.bind(this));
|
||
|
||
this.stopBuildError = null;
|
||
var log = logError.bind(this);
|
||
var stop = stopBuild.bind(this);
|
||
|
||
this.env.addEventListener('parseerror', stop);
|
||
this.env.addEventListener('duplicateerror', stop);
|
||
this.env.addEventListener('notfounderror', stop);
|
||
|
||
this.env.addEventListener('deprecatewarning', log);
|
||
|
||
if (htmloptimizer.config.LOCALE_BASEDIR !== '') {
|
||
this.env.addEventListener('fetcherror', log);
|
||
this.env.addEventListener('parseerror', log);
|
||
this.env.addEventListener('duplicateerror', log);
|
||
}
|
||
}
|
||
|
||
View.prototype.formatEntities = function formatEntities() {
|
||
var _ctx;
|
||
|
||
return (_ctx = this.ctx).formatEntities.apply(_ctx, arguments);
|
||
};
|
||
|
||
View.prototype.translateDocument = function translateDocument(code) {
|
||
var _this12 = this;
|
||
|
||
var dir = getDirection(code);
|
||
var langs = [{ code: code, src: 'app' }, { code: 'en-US', src: 'app' }];
|
||
var setDocLang = function () {
|
||
_this12.doc.documentElement.lang = code;
|
||
_this12.doc.documentElement.dir = dir;
|
||
};
|
||
|
||
var ctx = this.env.createContext(langs, getResourceLinks(this.doc.head));
|
||
return translateFragment(ctx, this.doc.documentElement).then(setDocLang);
|
||
};
|
||
|
||
View.prototype.serializeResources = function serializeResources(code) {
|
||
var _this13 = this;
|
||
|
||
var langCtx = this.env.createContext([{ code: code, src: code in pseudo ? 'pseudo' : 'app' }], this.resLinks);
|
||
|
||
return Promise.all([this.sourceCtx, langCtx].map(function (ctx) {
|
||
return ctx.fetch();
|
||
})).then(function () {
|
||
var _ref7 = _this13.isLegacy ? serializeLegacyContext(langCtx) : serializeContext(langCtx);
|
||
|
||
var errors = _ref7[0];
|
||
var entries = _ref7[1];
|
||
|
||
if (errors.length) {
|
||
var notFoundErrors = errors.filter(function (err) {
|
||
return err.message.indexOf('not found') > -1;
|
||
}).map(function (err) {
|
||
return err.id;
|
||
});
|
||
var malformedErrors = errors.filter(function (err) {
|
||
return err.message.indexOf('malformed') > -1;
|
||
}).map(function (err) {
|
||
return err.id;
|
||
});
|
||
|
||
if (notFoundErrors.length) {
|
||
_this13.htmloptimizer.dump('[l10n] [' + code + ']: ' + notFoundErrors.length + ' missing compared to en-US: ' + notFoundErrors.join(', '));
|
||
}
|
||
if (malformedErrors.length) {
|
||
_this13.htmloptimizer.dump('[l10n] [' + code + ']: ' + malformedErrors.length + ' malformed compared to en-US: ' + malformedErrors.join(', '));
|
||
}
|
||
}
|
||
|
||
return entries;
|
||
});
|
||
};
|
||
|
||
View.prototype.checkError = function checkError() {
|
||
return {
|
||
wait: false,
|
||
error: this.stopBuildError
|
||
};
|
||
};
|
||
|
||
return View;
|
||
})();
|
||
|
||
function amendError(err) {
|
||
err.message = err.message + ' (' + this.htmloptimizer.webapp.url + ')';
|
||
}
|
||
|
||
function logError(err) {
|
||
this.htmloptimizer.dump('[l10n] ' + err);
|
||
}
|
||
|
||
function stopBuild(err) {
|
||
if (err.lang && err.lang.code === 'en-US' && !this.stopBuildError) {
|
||
this.stopBuildError = err;
|
||
}
|
||
}
|
||
|
||
function getDirection(code) {
|
||
var tag = code.split('-')[0];
|
||
return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr';
|
||
}
|
||
|
||
function getView(htmloptimizer) {
|
||
var htmlFetch = function () {
|
||
for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
||
args[_key4] = arguments[_key4];
|
||
}
|
||
|
||
return fetchResource.apply(undefined, [htmloptimizer].concat(args));
|
||
};
|
||
return new View(htmloptimizer, htmlFetch);
|
||
}
|
||
|
||
exports.getView = getView;
|
||
exports.pseudo = pseudo;
|
||
exports.walkValue = walkValue; |