From 420ea6ab22154b7e1bb293b248599106ece52615 Mon Sep 17 00:00:00 2001 From: renato Date: Fri, 24 Jun 2016 15:13:54 +0200 Subject: [PATCH] workcicle... --- alasql-utility/alasql-utility.js | 29 +++-------------------------- bower_components/l20n/.bower.json | 36 ------------------------------------ bower_components/l20n/bower.json | 26 -------------------------- bower_components/l20n/dist/bundle/aisle/l20n.js | 660 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/bundle/bridge/l20n-client.js | 479 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/bundle/bridge/l20n-service.js | 2123 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/bundle/gaia/build/l20n.js | 2905 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/bundle/gaia/l20n.js | 2567 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/bundle/jsshell/l20n.js | 1385 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/bundle/node/l20n.js | 1836 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/bundle/testing/l20n.js | 308 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/bundle/tooling/l20n.js | 3552 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/bundle/web/l20n-common.js | 1876 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/bundle/web/l20n.js | 2553 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/aisle/l20n.js | 765 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/bridge/l20n-client.js | 507 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/bridge/l20n-service.js | 2131 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/gaia/build/l20n.js | 2814 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/compat/gaia/l20n.js | 2616 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/compat/jsshell/l20n.js | 1361 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/node/l20n.js | 1810 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/testing/l20n.js | 291 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/tooling/l20n.js | 3686 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/web/l20n-common.js | 1847 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/compat/web/l20n.js | 2592 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/compat/web/l20n.min.js | 1 - bower_components/l20n/dist/gecko/bridge/l20n-client.js | 456 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/gecko/bridge/l20n-service.js | 2090 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/gecko/gaia/build/l20n.js | 2847 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/gecko/gaia/l20n.js | 2521 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bower_components/l20n/dist/gecko/web/l20n-common.js | 1860 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ bower_components/l20n/dist/gecko/web/l20n.js | 2507 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- controllets/filters-controllet/filters-controllet.html | 28 +++++++++++++--------------- controllets/select-data-controllet/select-data-controllet.html | 10 +++++----- controllets/select-visualization-controllet/demo/index.html | 2 +- controllets/select-visualization-controllet/select-visualization-controllet.html | 4 ++-- datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior.js | 70 ++++++++++++++++++++++++++++++++-------------------------------------- datalets/base-datalet/static/js/WorkcycleBehavior.js | 16 ++++++++++------ datalets/highcharts-datalet/highcharts-datalet.html | 87 ++------------------------------------------------------------------------------------- 39 files changed, 68 insertions(+), 53186 deletions(-) delete mode 100755 bower_components/l20n/.bower.json delete mode 100755 bower_components/l20n/bower.json delete mode 100755 bower_components/l20n/dist/bundle/aisle/l20n.js delete mode 100755 bower_components/l20n/dist/bundle/bridge/l20n-client.js delete mode 100755 bower_components/l20n/dist/bundle/bridge/l20n-service.js delete mode 100755 bower_components/l20n/dist/bundle/gaia/build/l20n.js delete mode 100755 bower_components/l20n/dist/bundle/gaia/l20n.js delete mode 100755 bower_components/l20n/dist/bundle/jsshell/l20n.js delete mode 100755 bower_components/l20n/dist/bundle/node/l20n.js delete mode 100755 bower_components/l20n/dist/bundle/testing/l20n.js delete mode 100755 bower_components/l20n/dist/bundle/tooling/l20n.js delete mode 100755 bower_components/l20n/dist/bundle/web/l20n-common.js delete mode 100755 bower_components/l20n/dist/bundle/web/l20n.js delete mode 100755 bower_components/l20n/dist/compat/aisle/l20n.js delete mode 100755 bower_components/l20n/dist/compat/bridge/l20n-client.js delete mode 100755 bower_components/l20n/dist/compat/bridge/l20n-service.js delete mode 100755 bower_components/l20n/dist/compat/gaia/build/l20n.js delete mode 100755 bower_components/l20n/dist/compat/gaia/l20n.js delete mode 100755 bower_components/l20n/dist/compat/jsshell/l20n.js delete mode 100755 bower_components/l20n/dist/compat/node/l20n.js delete mode 100755 bower_components/l20n/dist/compat/testing/l20n.js delete mode 100755 bower_components/l20n/dist/compat/tooling/l20n.js delete mode 100755 bower_components/l20n/dist/compat/web/l20n-common.js delete mode 100755 bower_components/l20n/dist/compat/web/l20n.js delete mode 100755 bower_components/l20n/dist/compat/web/l20n.min.js delete mode 100755 bower_components/l20n/dist/gecko/bridge/l20n-client.js delete mode 100755 bower_components/l20n/dist/gecko/bridge/l20n-service.js delete mode 100755 bower_components/l20n/dist/gecko/gaia/build/l20n.js delete mode 100755 bower_components/l20n/dist/gecko/gaia/l20n.js delete mode 100755 bower_components/l20n/dist/gecko/web/l20n-common.js delete mode 100755 bower_components/l20n/dist/gecko/web/l20n.js diff --git a/alasql-utility/alasql-utility.js b/alasql-utility/alasql-utility.js index 1b56b33..9779744 100644 --- a/alasql-utility/alasql-utility.js +++ b/alasql-utility/alasql-utility.js @@ -1,23 +1,4 @@ -function alasql_selectData (data, fields, filters) { - if(fields.length == 0) - return []; - - var _fields = _addParenthesis(fields); - - var select = _alasql_SELECT(_fields); - - var where = ""; - if(filters && filters.length) { - var _filters = _copy(filters); - where = _alasql_WHERE(_filters); - } - - var query = select + " FROM ?" + where; - - return alasql(query, [data]); -} - -function alasql_complexSelectData (data, fields, filters, aggregators, orders) { +function alasql_QUERY (data, fields, filters, aggregators, orders) { if(fields.length == 0) return []; @@ -134,17 +115,13 @@ function _addParenthesis (fields) { } function _normalizeField (field) { - /*DEPRECATED*/return "[" + field.substring(field.lastIndexOf(",") + 1, field.length) + "]"; - //return "[" + field + "]"; + return "[" + field + "]"; } -function transformData (data, fields, truncate) { +function alasql_transformData (data, fields, truncate) { if(!data || data.length == 0) return []; - /*DEPRECATED*/for (var i=0; i < fields.length; i++) - fields[i] = fields[i].substring(fields[i].lastIndexOf(",") + 1, fields[i].length); - var tData = []; for (var i in fields){ diff --git a/bower_components/l20n/.bower.json b/bower_components/l20n/.bower.json deleted file mode 100755 index fe55727..0000000 --- a/bower_components/l20n/.bower.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "l20n", - "description": "A natural-language localization framework", - "version": "3.4.1", - "homepage": "http://l20n.org", - "repository": { - "type": "git", - "url": "git://github.com/l20n/l20n.js.git" - }, - "authors": [ - "Mozilla ", - "Zbigniew Braniecki", - "Staś Małolepszy" - ], - "license": "Apache 2.0", - "keywords": [ - "localization", - "l10n", - "l20n" - ], - "main": [], - "ignore": [ - "**/*", - "!dist/**/*" - ], - "_release": "3.4.1", - "_resolution": { - "type": "version", - "tag": "v3.4.1", - "commit": "4b5428be54003e8dc7243665a5f844a546ddd226" - }, - "_source": "git://github.com/l20n/l20n.js.git", - "_target": "v3.x", - "_originalSource": "l20n", - "_direct": true -} \ No newline at end of file diff --git a/bower_components/l20n/bower.json b/bower_components/l20n/bower.json deleted file mode 100755 index 1ca4d20..0000000 --- a/bower_components/l20n/bower.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "l20n", - "description": "A natural-language localization framework", - "version": "3.4.1", - "homepage": "http://l20n.org", - "repository": { - "type": "git", - "url": "git://github.com/l20n/l20n.js.git" - }, - "authors": [ - "Mozilla ", - "Zbigniew Braniecki", - "Staś Małolepszy" - ], - "license": "Apache 2.0", - "keywords": [ - "localization", - "l10n", - "l20n" - ], - "main": [], - "ignore": [ - "**/*", - "!dist/**/*" - ] -} diff --git a/bower_components/l20n/dist/bundle/aisle/l20n.js b/bower_components/l20n/dist/bundle/aisle/l20n.js deleted file mode 100755 index 5530286..0000000 --- a/bower_components/l20n/dist/bundle/aisle/l20n.js +++ /dev/null @@ -1,660 +0,0 @@ -define(['exports'], function (exports) { 'use strict'; - - class Node { - constructor() { - this.type = this.constructor.name; - } - } - - class Entry extends Node { - constructor() { - super(); - } - } - - class Identifier extends Node { - constructor(name) { - super(); - this.name = name; - } - } - - class Variable extends Node { - constructor(name) { - super(); - this.name = name; - } - } - - class Global extends Node { - constructor(name) { - super(); - this.name = name; - } - } - - class Value extends Node { - constructor() { - super(); - } - } - - class String extends Value { - constructor(source, content) { - super(); - this.source = source; - this.content = content; - - this._opchar = '"'; - } - } - - class Hash extends Value { - constructor(items) { - super(); - this.items = items; - } - } - - - class Entity extends Entry { - constructor(id, value = null, index = null, attrs = []) { - super(); - this.id = id; - this.value = value; - this.index = index; - this.attrs = attrs; - } - } - - class Resource extends Node { - constructor() { - super(); - this.body = []; - } - } - - class Attribute extends Node { - constructor(id, value, index = null) { - super(); - this.id = id; - this.value = value; - this.index = index; - } - } - - class HashItem extends Node { - constructor(id, value, defItem) { - super(); - this.id = id; - this.value = value; - this.default = defItem; - } - } - - class Comment extends Entry { - constructor(body) { - super(); - this.body = body; - } - } - - class Expression extends Node { - constructor() { - super(); - } - } - - class PropertyExpression extends Expression { - constructor(idref, exp, computed = false) { - super(); - this.idref = idref; - this.exp = exp; - this.computed = computed; - } - } - - class CallExpression extends Expression { - constructor(callee, args) { - super(); - this.callee = callee; - this.args = args; - } - } - - class JunkEntry extends Entry { - constructor(content) { - super(); - this.content = content; - } - } - - var AST = { - Node, - Identifier, - Value, - String, - Hash, - Entity, - Resource, - Attribute, - HashItem, - Comment, - Variable, - Global, - Expression, - PropertyExpression, - CallExpression, - JunkEntry, - }; - - 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; - - const MAX_PLACEABLES = 100; - - - class ParseContext { - constructor(string, pos) { - this._config = { - pos: pos - }; - this._source = string; - this._index = 0; - this._length = string.length; - this._curEntryStart = 0; - } - - setPosition(node, start, end) { - if (!this._config.pos) { - return; - } - node._pos = {start, end}; - } - - getResource() { - let resource = new AST.Resource(); - this.setPosition(resource, 0, this._length); - resource._errors = []; - - this.getWS(); - while (this._index < this._length) { - try { - resource.body.push(this.getEntry()); - } catch (e) { - if (e instanceof L10nError) { - resource._errors.push(e); - resource.body.push(this.getJunkEntry()); - } else { - throw e; - } - } - if (this._index < this._length) { - this.getWS(); - } - } - - return resource; - } - - getEntry() { - this._curEntryStart = this._index; - - if (this._source[this._index] === '<') { - ++this._index; - const 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(id, index) { - if (!this.getRequiredWS()) { - throw this.error('Expected white space'); - } - - const ch = this._source.charAt(this._index); - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === null) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++this._index; - - const entity = new AST.Entity(id, value, index, attrs); - this.setPosition(entity, this._curEntryStart, this._index); - return entity; - } - - getValue(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - return null; - } - - getWS() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - } - - getRequiredWS() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - } - - getIdentifier() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - const id = new AST.Identifier(this._source.slice(start, this._index)); - this.setPosition(id, start, this._index); - return id; - } - - getUnicodeChar() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - continue; - } - throw this.error('Illegal unicode escape sequence'); - } - return '\\u' + this._source.slice(this._index - 3, this._index + 1); - } - - getString(opchar, opcharLen) { - let body = []; - let buf = ''; - let placeables = 0; - - this._index += opcharLen - 1; - - const start = this._index + 1; - - let closed = false; - - while (!closed) { - let ch = this._source[++this._index]; - - switch (ch) { - case '\\': - const ch2 = this._source[++this._index]; - if (ch2 === 'u') { - buf += this.getUnicodeChar(); - } else if (ch2 === opchar || ch2 === '\\') { - buf += ch2; - } else if (ch2 === '{' && this._source[this._index + 1] === '{') { - buf += '{'; - } else { - throw this.error('Illegal escape sequence'); - } - break; - case '{': - if (this._source[this._index + 1] === '{') { - if (placeables > MAX_PLACEABLES - 1) { - throw this.error('Too many placeables, maximum allowed is ' + - MAX_PLACEABLES); - } - if (buf.length) { - body.push(buf); - buf = ''; - } - this._index += 2; - this.getWS(); - body.push(this.getExpression()); - this.getWS(); - if (!this._source.startsWith('}}', this._index)) { - throw this.error('Expected "}}"'); - } - this._index += 1; - placeables++; - break; - } - /* falls through */ - default: - if (ch === opchar) { - this._index++; - closed = true; - break; - } - - buf += ch; - if (this._index + 1 >= this._length) { - throw this.error('Unclosed string literal'); - } - } - } - - if (buf.length) { - body.push(buf); - } - - const string = new AST.String( - this._source.slice(start, this._index - 1), body); - this.setPosition(string, start, this._index); - string._opchar = opchar; - - return string; - } - - getAttributes() { - const attrs = []; - - while (true) { - const attr = this.getAttribute(); - attrs.push(attr); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - } - - getAttribute() { - const start = this._index; - const key = this.getIdentifier(); - let index; - - 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(); - const attr = new AST.Attribute(key, this.getValue(), index); - this.setPosition(attr, start, this._index); - return attr; - } - - getHash() { - const start = this._index; - let items = []; - - ++this._index; - this.getWS(); - - while (true) { - items.push(this.getHashItem()); - this.getWS(); - - const 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 "}"'); - } - } - - const hash = new AST.Hash(items); - this.setPosition(hash, start, this._index); - return hash; - } - - getHashItem() { - const start = this._index; - - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const key = this.getIdentifier(); - this.getWS(); - if (this._source[this._index] !== ':') { - throw this.error('Expected ":"'); - } - ++this._index; - this.getWS(); - - const hashItem = new AST.HashItem(key, this.getValue(), defItem); - this.setPosition(hashItem, start, this._index); - return hashItem; - } - - getComment() { - this._index += 2; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - const comment = new AST.Comment(this._source.slice(start, end)); - this.setPosition(comment, start - 2, this._index); - return comment; - } - - getExpression() { - const start = this._index; - let exp = this.getPrimaryExpression(); - - while (true) { - let ch = this._source[this._index]; - if (ch === '.' || ch === '[') { - ++this._index; - exp = this.getPropertyExpression(exp, ch === '[', start); - } else if (ch === '(') { - ++this._index; - exp = this.getCallExpression(exp, start); - } else { - break; - } - } - - return exp; - } - - getPropertyExpression(idref, computed, start) { - let exp; - - if (computed) { - this.getWS(); - exp = this.getExpression(); - this.getWS(); - if (this._source[this._index] !== ']') { - throw this.error('Expected "]"'); - } - ++this._index; - } else { - exp = this.getIdentifier(); - } - - const propExpr = new AST.PropertyExpression(idref, exp, computed); - this.setPosition(propExpr, start, this._index); - return propExpr; - } - - getCallExpression(callee, start) { - this.getWS(); - - const callExpr = new AST.CallExpression(callee, - this.getItemList(this.getExpression, ')')); - this.setPosition(callExpr, start, this._index); - return callExpr; - } - - getPrimaryExpression() { - const start = this._index; - const ch = this._source[this._index]; - - switch (ch) { - case '$': - ++this._index; - const variable = new AST.Variable(this.getIdentifier()); - this.setPosition(variable, start, this._index); - return variable; - case '@': - ++this._index; - const global = new AST.Global(this.getIdentifier()); - this.setPosition(global, start, this._index); - return global; - default: - return this.getIdentifier(); - } - } - - getItemList(callback, closeChar) { - let items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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; - } - - error(message) { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - let lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - let context = this._source.slice(start, pos + 10); - - let msg = message + ' at pos ' + pos + ': `' + context + '`'; - - const err = new L10nError(msg); - err._pos = {start: pos, end: undefined}; - err.offset = pos - start; - err.description = message; - err.context = context; - return err; - } - - getJunkEntry() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - - const junk = new AST.JunkEntry( - this._source.slice(this._curEntryStart, nextEntry)); - - this.setPosition(junk, this._curEntryStart, nextEntry); - return junk; - } - } - - var parser = { - parseResource: function(string, pos = false) { - const parseContext = new ParseContext(string, pos); - return parseContext.getResource(); - }, - }; - - exports.L20nParser = parser; - -}); \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/bridge/l20n-client.js b/bower_components/l20n/dist/bundle/bridge/l20n-client.js deleted file mode 100755 index 430404b..0000000 --- a/bower_components/l20n/dist/bundle/bridge/l20n-client.js +++ /dev/null @@ -1,479 +0,0 @@ -(function () { 'use strict'; - - /* global bridge, BroadcastChannel */ - - const Client = bridge.client; - const channel = new BroadcastChannel('l20n-channel'); - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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' ], - // value is special-cased in isAttrAllowed - input: [ 'alt', 'placeholder' ], - menuitem: [ 'label' ], - menu: [ 'label' ], - optgroup: [ 'label' ], - option: [ 'label' ], - track: [ 'label' ], - img: [ 'alt' ], - textarea: [ 'placeholder' ], - th: [ 'abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while ((childElement = translationElement.childNodes[0])) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement( - childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild( - translationElement.ownerDocument.createTextNode( - childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; (attr = translationElement.attributes[k]); k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && - child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while ((child = element.previousElementSibling)) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string - .replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }) - .replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>', - }; - - function getResourceLinks(head) { - return Array.prototype.map.call( - head.querySelectorAll('link[rel="localization"]'), - el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && - element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [ - id, - JSON.parse(args.replace(reHtml, match => htmlEntities[match])) - ] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then( - translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - // Intl.Locale - function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? - 'rtl' : 'ltr'; - } - - const observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - const readiness = new WeakMap(); - - class View { - constructor(client, doc) { - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then( - () => init(this, client)); - - const observer = new MutationObserver(onMutations.bind(this)); - this._observe = () => observer.observe(doc, observerConfig); - this._disconnect = () => observer.disconnect(); - - const translateView = langs => translateDocument(this, langs); - client.on('translateDocument', translateView); - this.ready = this._interactive.then( - client => client.method('resolvedLanguages')).then( - translateView); - } - - requestLanguages(langs, global) { - return this._interactive.then( - client => client.method('requestLanguages', langs, global)); - } - - _resolveEntities(langs, keys) { - return this._interactive.then( - client => client.method('resolveEntities', client.id, langs, keys)); - } - - formatValue(id, args) { - return this._interactive.then( - client => client.method('formatValues', client.id, [[id, args]])).then( - values => values[0]); - } - - formatValues(...keys) { - return this._interactive.then( - client => client.method('formatValues', client.id, keys)); - } - - translateFragment(frag) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateFragment(this, langs, frag)); - } - } - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: () => view._interactive.then( - client => client.method('getName', code)), - processString: str => view._interactive.then( - client => client.method('processString', code, str)), - }; - } - - function init(view, client) { - view._observe(); - return client.method( - 'registerView', client.id, getResourceLinks(view._doc.head)).then( - () => client); - } - - function onMutations(mutations) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateMutations(this, langs, mutations)); - } - - function translateDocument(view, langs) { - const html = view._doc.documentElement; - - if (readiness.has(html)) { - return translateFragment(view, langs, html).then( - () => setAllAndEmit(html, langs)); - } - - const translated = - // has the document been already pre-translated? - langs[0].code === html.getAttribute('lang') ? - Promise.resolve() : - translateFragment(view, langs, html).then( - () => setLangDir(html, langs)); - - return translated.then(() => { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - const codes = langs.map(lang => lang.code); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - const code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false, - })); - } - - const client = new Client({ - service: 'l20n', - endpoint: channel, - timeout: false - }); - - window.addEventListener('pageshow', () => client.connect()); - window.addEventListener('pagehide', () => client.disconnect()); - - document.l10n = new View(client, document); - - //Bug 1204660 - Temporary proxy for shared code. Will be removed once - // l10n.js migration is completed. - navigator.mozL10n = { - setAttributes: document.l10n.setAttributes, - getAttributes: document.l10n.getAttributes, - formatValue: (...args) => document.l10n.formatValue(...args), - translateFragment: (...args) => document.l10n.translateFragment(...args), - once: cb => document.l10n.ready.then(cb), - ready: cb => document.l10n.ready.then(() => { - document.addEventListener('DOMRetranslated', cb); - cb(); - }), - }; - -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/bridge/l20n-service.js b/bower_components/l20n/dist/bundle/bridge/l20n-service.js deleted file mode 100755 index 29f69e3..0000000 --- a/bower_components/l20n/dist/bundle/bridge/l20n-service.js +++ /dev/null @@ -1,2123 +0,0 @@ -(function () { 'use strict'; - - const Service = bridge.service; - const channel = new BroadcastChannel('l20n-channel'); - - function broadcast(type, data) { - return this.service.broadcast(type, data); - } - - 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 load(type, url) { - return new Promise(function(resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - const io = { - extra: function(code, ver, path, type) { - return navigator.mozApps.getLocalizationResource( - code, ver, path, type); - }, - app: function(code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - }, - }; - - function fetchResource(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, - }; - - 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '\u2068'; - const PDI = '\u2069'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + - value.length + ', max allowed is ' + - MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; - } - - class Context { - constructor(env) { - this._env = env; - this._numberFormatters = null; - } - - _formatTuple(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]; - } - } - - _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null, - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple( - lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - - _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - - fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all( - resIds.map( - this._env._getResource.bind(this._env, langs[0]))).then( - () => langs); - } - - _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? - key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', - new L10nError('"' + id + '"' + ' not found in ' + lang.code, - id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then( - nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - - resolveEntities(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatEntity)); - } - - resolveValues(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatValue)); - } - - _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - - _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false, - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - _getMacro(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - - } - - function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? - id : {value: id, attrs: null}; - }); - - this._env.emit('notfounderror', new L10nError( - '"' + Array.from(missingIds).join(', ') + '"' + - ' not found in any language', missingIds), this); - - return resolved; - } - - // Walk an entry node searching for content leaves - function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; (key = keys[i]); i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; - } - - /* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - - function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': - 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz', - }; - - const mods = { - 'fr-x-psaccent': val => - val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => - val.replace(reWords, match => '\u202e' + match + '\u202c'), - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = - (map, val) => val.replace( - reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = - val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function(part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; - } - - const 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') - } - }); - - function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach( - listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach( - listener => listener.apply(this, args)); - } - } - - function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); - } - - function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); - } - - const parsers = { - properties: PropertiesParser, - l20n: L20nParser, - }; - - class Env { - constructor(defaultLang, fetchResource) { - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - - destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach( - resId => deleteIfOrphan(this._resCache, lists, resId)); - } - - _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - - _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry( - entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - - _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? - { code: this.defaultLang, src: 'app' } : - lang; - - const resource = this.fetchResource(res, langToFetch).then( - saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - } - - function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some( - ([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => - key.startsWith(resId) ? cache.delete(key) : null); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - function prioritizeLocales(def, availableLangs, requested) { - let supportedLocale; - // Find the first locale in the requested list that is supported. - for (let i = 0; i < requested.length; i++) { - const locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || - supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - let availableLangs = Object.create(null); - let defaultLang = null; - let appVersion = null; - - // XXX take last found instead of first? - const metas = head.querySelectorAll( - 'meta[name="availableLanguages"],' + - 'meta[name="defaultLanguage"],' + - 'meta[name="appVersion"]'); - for (let meta of metas) { - const name = meta.getAttribute('name'); - const content = meta.getAttribute('content').trim(); - switch (name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap( - availableLangs, content); - break; - case 'defaultLanguage': - const [lang, rev] = getLangRevisionTuple(content); - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang, - availableLangs, - appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce((seq, cur) => { - const [lang, rev] = getLangRevisionTuple(cur); - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - const [lang, rev] = str.trim().split(':'); - // if revision is missing, use NaN - return [lang, parseInt(rev)]; - } - - function negotiateLanguages( - fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, - requestedLangs) { - - const allAvailableLangs = Object.keys(availableLangs).concat( - additionalLangs || []).concat(Object.keys(pseudo)); - const newLangs = prioritizeLocales( - defaultLang, allAvailableLangs, requestedLangs); - - const langs = newLangs.map(code => ({ - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code), - })); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && - arr1.every((elem, i) => elem === arr2[i]); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (let i = 0, langpack; (langpack = langpacks[i]); i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - const lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && - (!(code in availableLangs) || - parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if ((code in pseudo) && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - class Remote { - constructor(fetchResource, broadcast, requestedLangs) { - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then( - () => this.init(requestedLangs)); - } - - init(requestedLangs) { - const meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env( - this.defaultLanguage, - (...args) => this.fetchResource(this.appVersion, ...args)); - - return this.requestLanguages(requestedLangs); - } - - registerView(view, resources) { - return this.interactive.then(() => { - this.ctxs.set(view, this.env.createContext(resources)); - return true; - }); - } - - unregisterView(view) { - return this.ctxs.delete(view); - } - - resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - } - - formatValues(view, keys) { - return this.languages.then( - langs => this.ctxs.get(view).resolveValues(langs, keys)); - } - - resolvedLanguages() { - return this.languages; - } - - requestLanguages(requestedLangs) { - return changeLanguages.call( - this, getAdditionalLanguages(), requestedLangs); - } - - getName(code) { - return pseudo[code].name; - } - - processString(code, str) { - return pseudo[code].process(str); - } - - handleEvent(evt) { - return changeLanguages.call( - this, evt.detail || getAdditionalLanguages(), navigator.languages); - } - } - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch( - () => []); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - const prevLangs = this.languages || []; - return this.languages = Promise.all([ - additionalLangs, prevLangs]).then( - ([additionalLangs, prevLangs]) => negotiateLanguages( - this.broadcast.bind(this, 'translateDocument'), - this.appVersion, this.defaultLanguage, this.availableLanguages, - additionalLangs, prevLangs, requestedLangs)); - } - - const remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - remote.service = new Service('l20n') - .method('registerView', (...args) => remote.registerView(...args)) - .method('resolvedLanguages', (...args) => remote.resolvedLanguages(...args)) - .method('requestLanguages', (...args) => remote.requestLanguages(...args)) - .method('resolveEntities', (...args) => remote.resolveEntities(...args)) - .method('formatValues', (...args) => remote.formatValues(...args)) - .method('getName', (...args) => remote.getName(...args)) - .method('processString', (...args) => remote.processString(...args)) - .on('disconnect', clientId => remote.unregisterView(clientId)) - .listen(channel); - -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/gaia/build/l20n.js b/bower_components/l20n/dist/bundle/gaia/build/l20n.js deleted file mode 100755 index b5ba771..0000000 --- a/bower_components/l20n/dist/bundle/gaia/build/l20n.js +++ /dev/null @@ -1,2905 +0,0 @@ -'use strict'; - -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, lang) { - // We need to decode URI because build system DOM reader - // may replace `{locale}` with `%7Blocale%7D`. See bug 1098188 - const url = decodeURI(res).replace('{locale}', lang.code); - const {file, content} = htmloptimizer.getFileByRelativePath(url); - if (!file) { - return Promise.reject(new L10nError('Not found: ' + url)); - } - - const parsed = res.endsWith('.json') ? - JSON.parse(content) : content; - return Promise.resolve(parsed); -} - -// Walk an entry node searching for content leaves -function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const newEntry = Object.create(null); - - if (entry.value) { - newEntry.value = walkValue$1(entry.value, fn); - } - - if (entry.index) { - newEntry.index = entry.index; - } - - if (entry.attrs) { - newEntry.attrs = Object.create(null); - for (let key in entry.attrs) { - newEntry.attrs[key] = walkEntry(entry.attrs[key], fn); - } - } - - return newEntry; -} - -function walkValue$1(value, fn) { - if (typeof value === 'string') { - return fn(value); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; (key = keys[i]); i++) { - newValue[key] = walkValue$1(value[key], fn); - } - - return newValue; -} - -/* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - -function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': - 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz', - }; - - const mods = { - 'fr-x-psaccent': val => - val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => - val.replace(reWords, match => '\u202e' + match + '\u202c'), - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = - (map, val) => val.replace( - reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = - val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function(part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; -} - -const pseudo$1 = 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') - } -}); - -const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, -}; - -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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } -}; - -const KNOWN_MACROS$1 = ['plural']; -const MAX_PLACEABLE_LENGTH$1 = 2500; - -// Unicode bidi isolation characters -const FSI$1 = '\u2068'; -const PDI$1 = '\u2069'; - -const 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier$1(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI$1 + '{{ ' + id + ' }}' + PDI$1]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - 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([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable$1(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector$1(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier$1(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier$1(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector$1(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue$1(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue$1(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); -} - -const 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 -}; - -// utility functions for plural rules methods -function isIn(n, list) { - return list.indexOf(n) !== -1; -} -function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; -} - -// list of all plural rules methods: -// map an integer to the plural form name to use -const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; -} - -class Context { - constructor(env) { - this._env = env; - this._numberFormatters = null; - } - - _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._env.emit('resolveerror', err, this); - return [{ error: err }, err.id]; - } - } - - _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null, - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple( - lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - - _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - - fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all( - resIds.map( - this._env._getResource.bind(this._env, langs[0]))).then( - () => langs); - } - - _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? - key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', - new L10nError('"' + id + '"' + ' not found in ' + lang.code, - id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then( - nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - - resolveEntities(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatEntity)); - } - - resolveValues(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatValue)); - } - - _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - - _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false, - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - _getMacro(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - -} - -function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? - id : {value: id, attrs: null}; - }); - - this._env.emit('notfounderror', new L10nError( - '"' + Array.from(missingIds).join(', ') + '"' + - ' not found in any language', missingIds), this); - - return resolved; -} - -function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach( - listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach( - listener => listener.apply(this, args)); - } -} - -function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); -} - -function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); -} - -const parsers = { - properties: PropertiesParser, - l20n: L20nParser, -}; - -class Env { - constructor(defaultLang, fetchResource) { - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - - destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach( - resId => deleteIfOrphan(this._resCache, lists, resId)); - } - - _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError$1(lang, err)); - return parser.parse.call(parser, emit, data); - } - - _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry( - entries[key], pseudo$1[lang.code].process); - } - return pseudoentries; - } - - _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? - { code: this.defaultLang, src: 'app' } : - lang; - - const resource = this.fetchResource(res, langToFetch).then( - saveEntries, recover); - - cache.set(id, resource); - - return resource; - } -} - -function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some( - ([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => - key.startsWith(resId) ? cache.delete(key) : null); - } -} - -function amendError$1(lang, err) { - err.lang = lang; - return err; -} - -const KNOWN_MACROS = ['plural']; -const MAX_PLACEABLE_LENGTH = 2500; - -// Matches characters outside of the Latin-1 character set -const nonLatin1 = /[^\x01-\xFF]/; - -// Unicode bidi isolation characters -const FSI = '\u2068'; -const PDI = '\u2069'; - -const resolutionChain = new WeakSet(); - -function createEntry(node) { - const keys = Object.keys(node); - - // the most common scenario: a simple string with no arguments - if (typeof node.$v === 'string' && keys.length === 2) { - return node.$v; - } - - let attrs; - - for (let i = 0, key; (key = keys[i]); i++) { - // skip $i (id), $v (value), $x (index) - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let res; - - try { - res = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, '{{ ' + id + ' }}']; - } - - const value = res[1]; - - if (typeof value === 'number') { - return res; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - 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)) { - // When dealing with non-Latin-1 text - // we wrap substitutions in bidi isolate characters - // to avoid bidi issues. - res[1] = FSI + value + PDI; - } - - return res; - } - - return [{}, '{{ ' + id + ' }}']; -} - -function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else if (cur.t === 'idOrVar'){ - const [, value] = subPlaceable(locals, ctx, lang, args, cur.v); - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector(ctx, lang, args, expr, index) { - const selectorName = index[0].v; - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[1] ? - resolveIdentifier(ctx, lang, args, index[1])[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (expr.hasOwnProperty(selector)) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try 'other' - if ('other' in expr) { - return resolveValue(locals, ctx, lang, args, expr.other); - } - - throw new L10nError('Unresolvable value'); -} - -function LegacyContext(env) { - Context.call(this, env); -} - -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, key; - - 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; - 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, v; - - 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; - } - - // Hash value - 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; - } - - // simple value - 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]}]; - } - } -}; - -// Recursively walk an AST node searching for content leaves -function walkContent(node, fn) { - if (typeof node === 'string') { - return fn(node); - } - - if (node.t === 'idOrVar') { - return node; - } - - const rv = Array.isArray(node) ? [] : {}; - const keys = Object.keys(node); - - for (let i = 0, key; (key = keys[i]); i++) { - // don't change identifier ($i) nor indices ($x) - if (key === '$i' || key === '$x') { - rv[key] = node[key]; - } else { - rv[key] = walkContent(node[key], fn); - } - } - return rv; -} - -// XXX babel's inheritance code triggers JavaScript warnings about modifying -// the prototype object so we use regular prototypal inheritance here -function LegacyEnv(defaultLang, fetchResource) { - Env.call(this, defaultLang, fetchResource); -} - -LegacyEnv.prototype = Object.create(Env.prototype); - -LegacyEnv.prototype.createContext = function(resIds) { - const ctx = new LegacyContext(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; -}; - -LegacyEnv.prototype._parse = function(syntax, lang, data) { - const emit = (type, err) => this.emit(type, amendError$1(lang, err)); - return PropertiesParser$1.parse.call(PropertiesParser$1, emit, data); -}; - -LegacyEnv.prototype._create = function(lang, ast) { - const entries = Object.create(null); - const create = lang.src === 'pseudo' ? - createPseudoEntry : createEntry; - - for (let i = 0, node; node = ast[i]; i++) { - const 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$1[lang.code].process)); -} - -// match the opening angle bracket (<) in HTML tags, and HTML entities like -// &, &, &. -const reOverlay = /<|&#?\w+;/; - -const 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' ], - // value is special-cased in isAttrAllowed - input: [ 'alt', 'placeholder' ], - menuitem: [ 'label' ], - menu: [ 'label' ], - optgroup: [ 'label' ], - option: [ 'label' ], - track: [ 'label' ], - img: [ 'alt' ], - textarea: [ 'placeholder' ], - th: [ 'abbr'] - } -}; - -function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } -} - -// The goal of overlay is to move the children of `translationElement` -// into `sourceElement` such that `sourceElement`'s own children are not -// replaced, but onle have their text nodes and their attributes modified. -// -// We want to make it possible for localizers to apply text-level semantics to -// the translations and make use of HTML entities. At the same time, we -// don't trust translations so we need to filter unsafe elements and -// attribtues out and we don't want to break the Web by replacing elements to -// which third-party code might have created references (e.g. two-way -// bindings in MVC frameworks). -function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while ((childElement = translationElement.childNodes[0])) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement( - childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild( - translationElement.ownerDocument.createTextNode( - childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; (attr = translationElement.attributes[k]); k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } -} - -// XXX the allowed list should be amendable; https://bugzil.la/922573 -function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; -} - -function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; -} - -// Get n-th immediate child of context that is of the same type as element. -// XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: -// 1) :scope is widely supported in more browsers and 2) it works with -// DocumentFragments. -function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && - child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; -} - -// Get the index of the element among siblings of the same type. -function getIndexOfType(element) { - let index = 0; - let child; - while ((child = element.previousElementSibling)) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; -} - -function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string - .replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }) - .replace(/^-/, ''); -} - -const reHtml = /[&<>]/g; -const htmlEntities = { - '&': '&', - '<': '<', - '>': '>', -}; - -function getResourceLinks(head) { - return Array.prototype.map.call( - head.querySelectorAll('link[rel="localization"]'), - el => el.getAttribute('href')); -} - -function getTranslatables(element) { - const 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, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); -} - -function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [ - id, - JSON.parse(args.replace(reHtml, match => htmlEntities[match])) - ] : id; - }); - - return view._resolveEntities(langs, keys); -} - -function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then( - translations => applyTranslations(view, elements, translations)); -} - -function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); -} - -// Polyfill NodeList.prototype[Symbol.iterator] for Chrome. -// See https://code.google.com/p/chromium/issues/detail?id=401699 -if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; -} - -// Intl.Locale -function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? - 'rtl' : 'ltr'; -} - -function serializeContext(ctx, lang) { - const cache = ctx._env._resCache; - const resIds = Array.from(ctx._env._resLists.get(ctx)); - return resIds.reduceRight(([errorsSeq, entriesSeq], cur) => { - const sourceRes = cache.get(cur + 'en-USapp'); - const langRes = cache.get(cur + lang.code + lang.src); - const [errors, entries] = serializeEntries( - lang, - langRes instanceof L10nError ? {} : langRes, - sourceRes instanceof L10nError ? {} : sourceRes); - return [errorsSeq.concat(errors), Object.assign(entriesSeq, entries)]; - }, [[], Object.create(null)]); -} - -function serializeEntries(lang, langEntries, sourceEntries) { - const errors = []; - const entries = Object.create(null); - - for (let id in sourceEntries) { - const sourceEntry = sourceEntries[id]; - const 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' || // a simple string - typeof entity.value === 'string' || // a simple string, entity with attrs - Array.isArray(entity.value) || // a complex string - typeof entity.value === 'object' && // a dict with an index - entity.index !== null; -} - -function areAttrsEqual(attrs1, attrs2) { - const keys1 = Object.keys(attrs1 || Object.create(null)); - const keys2 = Object.keys(attrs2 || Object.create(null)); - - if (keys1.length !== keys2.length) { - return false; - } - - for (let 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, lang) { - const cache = ctx._env._resCache; - const resIds = Array.from(ctx._env._resLists.get(ctx)); - return resIds.reduce(([errorsSeq, entriesSeq], cur) => { - const sourceRes = cache.get(cur + 'en-USapp'); - const langRes = cache.get(cur + lang.code + lang.src); - const [errors, entries] = serializeEntries$1( - lang, - langRes instanceof L10nError ? {} : langRes, - sourceRes instanceof L10nError ? {} : sourceRes); - return [errorsSeq.concat(errors), entriesSeq.concat(entries)]; - }, [[], []]); -} - -function serializeEntries$1(lang, langEntries, sourceEntries) { - const errors = []; - const entries = Object.keys(sourceEntries).map(id => { - const sourceEntry = sourceEntries[id]; - const 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 }; - } - - const node = { - $i: id, - }; - - if (entry.value !== null) { - node.$v = entry.value; - } - - if (entry.index !== null) { - node.$x = entry.index; - } - - for (let key in entry.attrs) { - node[key] = serializeAttribute(entry.attrs[key]); - } - - return node; -} - -function serializeAttribute(attr) { - if (typeof attr === 'string') { - return attr; - } - - const 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' || // a simple string - typeof entity.value === 'string' || // a simple string, entity with attrs - Array.isArray(entity.value) || // a complex string - typeof entity.value === 'object' && // a dict with an index - entity.index !== null; -} - -function areAttrsEqual$1(attrs1, attrs2) { - const keys1 = Object.keys(attrs1 || Object.create(null)); - const keys2 = Object.keys(attrs2 || Object.create(null)); - - if (keys1.length !== keys2.length) { - return false; - } - - for (let 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; -} - -class View { - constructor(htmloptimizer, fetchResource) { - this.htmloptimizer = htmloptimizer; - this.doc = htmloptimizer.document; - - this.isEnabled = this.doc.querySelector('link[rel="localization"]'); - // XXX we should check if the app uses l10n.js instead, but due to lazy - // loading we can't rely on querySelector. - this.isLegacy = !this.doc.querySelector('script[src*="l20n"]'); - - const EnvClass = this.isLegacy ? LegacyEnv : Env; - this.env = new EnvClass( - htmloptimizer.config.GAIA_DEFAULT_LOCALE, fetchResource); - this.ctx = this.env.createContext(getResourceLinks(this.doc.head)); - - // add the url of the currently processed webapp to all errors - this.env.addEventListener('*', amendError.bind(this)); - - this.stopBuildError = null; - const log = logError.bind(this); - const stop = stopBuild.bind(this); - - // stop the build if these errors happen for en-US - // XXX tv_apps break the build https://bugzil.la/1179833 - // this.env.addEventListener('fetcherror', stop); - this.env.addEventListener('parseerror', stop); - this.env.addEventListener('duplicateerror', stop); - this.env.addEventListener('notfounderror', stop); - // XXX sms breaks the build https://bugzil.la/1178187 - // this.env.addEventListener('resolveerror', stop); - - this.env.addEventListener('deprecatewarning', log); - - // if LOCALE_BASEDIR is set alert about missing strings - if (htmloptimizer.config.LOCALE_BASEDIR !== '') { - this.env.addEventListener('fetcherror', log); - this.env.addEventListener('parseerror', log); - this.env.addEventListener('duplicateerror', log); - } - } - - _observe() {} - _disconnect() {} - - _resolveEntities(langs, keys) { - return this.ctx.resolveEntities(langs, keys); - } - - translateDocument(code) { - const dir = getDirection(code); - const langs = [{ code, src: 'app' }]; - const setDocLang = () => { - this.doc.documentElement.lang = code; - this.doc.documentElement.dir = dir; - }; - return this.ctx.fetch(langs).then( - langs => translateFragment(this, langs, this.doc.documentElement)).then( - setDocLang); - } - - serializeResources(code) { - const lang = { - code, - src: code in pseudo$1 ? 'pseudo' : 'app' - }; - return fetchContext(this.ctx, lang).then(() => { - const [errors, entries] = this.isLegacy ? - serializeLegacyContext(this.ctx, lang) : - serializeContext(this.ctx, lang); - - if (errors.length) { - const notFoundErrors = errors.filter( - err => err.message.indexOf('not found') > -1).map( - err => err.id); - const malformedErrors = errors.filter( - err => err.message.indexOf('malformed') > -1).map( - err => err.id); - - if (notFoundErrors.length) { - this.htmloptimizer.dump( - '[l10n] [' + lang.code + ']: ' + notFoundErrors.length + - ' missing compared to en-US: ' + notFoundErrors.join(', ')); - } - if (malformedErrors.length) { - this.htmloptimizer.dump( - '[l10n] [' + lang.code + ']: ' + malformedErrors.length + - ' malformed compared to en-US: ' + malformedErrors.join(', ')); - } - } - - return entries; - }); - } - - checkError() { - return { - wait: false, - error: this.stopBuildError - }; - } -} - -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 fetchContext(ctx, lang) { - const sourceLang = { code: 'en-US', src: 'app' }; - return Promise.all( - [sourceLang, lang].map(lang => ctx.fetch([lang]))); -} - -function getView(htmloptimizer) { - const htmlFetch = (...args) => fetchResource(htmloptimizer, ...args); - return new View(htmloptimizer, htmlFetch); -} - -exports.getView = getView; -exports.pseudo = pseudo$1; -exports.walkValue = walkValue$1; \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/gaia/l20n.js b/bower_components/l20n/dist/bundle/gaia/l20n.js deleted file mode 100755 index 56ef7a9..0000000 --- a/bower_components/l20n/dist/bundle/gaia/l20n.js +++ /dev/null @@ -1,2567 +0,0 @@ -(function () { 'use strict'; - - function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach( - listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach( - listener => listener.apply(this, args)); - } - } - - function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); - } - - function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); - } - - class Client { - constructor(remote) { - this.id = this; - this.remote = remote; - - const listeners = {}; - this.on = (...args) => addEventListener(listeners, ...args); - this.emit = (...args) => emit(listeners, ...args); - } - - method(name, ...args) { - return this.remote[name](...args); - } - } - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach( - client => client.emit(type, data)); - } - - 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 load(type, url) { - return new Promise(function(resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - const io = { - extra: function(code, ver, path, type) { - return navigator.mozApps.getLocalizationResource( - code, ver, path, type); - }, - app: function(code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - }, - }; - - function fetchResource(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, - }; - - 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '\u2068'; - const PDI = '\u2069'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + - value.length + ', max allowed is ' + - MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; - } - - class Context { - constructor(env) { - this._env = env; - this._numberFormatters = null; - } - - _formatTuple(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]; - } - } - - _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null, - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple( - lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - - _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - - fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all( - resIds.map( - this._env._getResource.bind(this._env, langs[0]))).then( - () => langs); - } - - _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? - key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', - new L10nError('"' + id + '"' + ' not found in ' + lang.code, - id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then( - nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - - resolveEntities(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatEntity)); - } - - resolveValues(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatValue)); - } - - _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - - _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false, - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - _getMacro(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - - } - - function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? - id : {value: id, attrs: null}; - }); - - this._env.emit('notfounderror', new L10nError( - '"' + Array.from(missingIds).join(', ') + '"' + - ' not found in any language', missingIds), this); - - return resolved; - } - - // Walk an entry node searching for content leaves - function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; (key = keys[i]); i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; - } - - /* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - - function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': - 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz', - }; - - const mods = { - 'fr-x-psaccent': val => - val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => - val.replace(reWords, match => '\u202e' + match + '\u202c'), - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = - (map, val) => val.replace( - reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = - val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function(part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; - } - - const 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') - } - }); - - const parsers = { - properties: PropertiesParser, - l20n: L20nParser, - }; - - class Env { - constructor(defaultLang, fetchResource) { - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - - destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach( - resId => deleteIfOrphan(this._resCache, lists, resId)); - } - - _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - - _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry( - entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - - _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? - { code: this.defaultLang, src: 'app' } : - lang; - - const resource = this.fetchResource(res, langToFetch).then( - saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - } - - function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some( - ([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => - key.startsWith(resId) ? cache.delete(key) : null); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - // Intl.Locale - function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? - 'rtl' : 'ltr'; - } - - function prioritizeLocales(def, availableLangs, requested) { - let supportedLocale; - // Find the first locale in the requested list that is supported. - for (let i = 0; i < requested.length; i++) { - const locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || - supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - let availableLangs = Object.create(null); - let defaultLang = null; - let appVersion = null; - - // XXX take last found instead of first? - const metas = head.querySelectorAll( - 'meta[name="availableLanguages"],' + - 'meta[name="defaultLanguage"],' + - 'meta[name="appVersion"]'); - for (let meta of metas) { - const name = meta.getAttribute('name'); - const content = meta.getAttribute('content').trim(); - switch (name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap( - availableLangs, content); - break; - case 'defaultLanguage': - const [lang, rev] = getLangRevisionTuple(content); - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang, - availableLangs, - appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce((seq, cur) => { - const [lang, rev] = getLangRevisionTuple(cur); - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - const [lang, rev] = str.trim().split(':'); - // if revision is missing, use NaN - return [lang, parseInt(rev)]; - } - - function negotiateLanguages( - fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, - requestedLangs) { - - const allAvailableLangs = Object.keys(availableLangs).concat( - additionalLangs || []).concat(Object.keys(pseudo)); - const newLangs = prioritizeLocales( - defaultLang, allAvailableLangs, requestedLangs); - - const langs = newLangs.map(code => ({ - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code), - })); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && - arr1.every((elem, i) => elem === arr2[i]); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (let i = 0, langpack; (langpack = langpacks[i]); i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - const lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && - (!(code in availableLangs) || - parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if ((code in pseudo) && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - class Remote { - constructor(fetchResource, broadcast, requestedLangs) { - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then( - () => this.init(requestedLangs)); - } - - init(requestedLangs) { - const meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env( - this.defaultLanguage, - (...args) => this.fetchResource(this.appVersion, ...args)); - - return this.requestLanguages(requestedLangs); - } - - registerView(view, resources) { - return this.interactive.then(() => { - this.ctxs.set(view, this.env.createContext(resources)); - return true; - }); - } - - unregisterView(view) { - return this.ctxs.delete(view); - } - - resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - } - - formatValues(view, keys) { - return this.languages.then( - langs => this.ctxs.get(view).resolveValues(langs, keys)); - } - - resolvedLanguages() { - return this.languages; - } - - requestLanguages(requestedLangs) { - return changeLanguages.call( - this, getAdditionalLanguages(), requestedLangs); - } - - getName(code) { - return pseudo[code].name; - } - - processString(code, str) { - return pseudo[code].process(str); - } - - handleEvent(evt) { - return changeLanguages.call( - this, evt.detail || getAdditionalLanguages(), navigator.languages); - } - } - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch( - () => []); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - const prevLangs = this.languages || []; - return this.languages = Promise.all([ - additionalLangs, prevLangs]).then( - ([additionalLangs, prevLangs]) => negotiateLanguages( - this.broadcast.bind(this, 'translateDocument'), - this.appVersion, this.defaultLanguage, this.availableLanguages, - additionalLangs, prevLangs, requestedLangs)); - } - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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' ], - // value is special-cased in isAttrAllowed - input: [ 'alt', 'placeholder' ], - menuitem: [ 'label' ], - menu: [ 'label' ], - optgroup: [ 'label' ], - option: [ 'label' ], - track: [ 'label' ], - img: [ 'alt' ], - textarea: [ 'placeholder' ], - th: [ 'abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while ((childElement = translationElement.childNodes[0])) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement( - childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild( - translationElement.ownerDocument.createTextNode( - childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; (attr = translationElement.attributes[k]); k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && - child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while ((child = element.previousElementSibling)) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string - .replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }) - .replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>', - }; - - function getResourceLinks(head) { - return Array.prototype.map.call( - head.querySelectorAll('link[rel="localization"]'), - el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && - element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [ - id, - JSON.parse(args.replace(reHtml, match => htmlEntities[match])) - ] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then( - translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - const observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - const readiness = new WeakMap(); - - class View { - constructor(client, doc) { - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then( - () => init(this, client)); - - const observer = new MutationObserver(onMutations.bind(this)); - this._observe = () => observer.observe(doc, observerConfig); - this._disconnect = () => observer.disconnect(); - - const translateView = langs => translateDocument(this, langs); - client.on('translateDocument', translateView); - this.ready = this._interactive.then( - client => client.method('resolvedLanguages')).then( - translateView); - } - - requestLanguages(langs, global) { - return this._interactive.then( - client => client.method('requestLanguages', langs, global)); - } - - _resolveEntities(langs, keys) { - return this._interactive.then( - client => client.method('resolveEntities', client.id, langs, keys)); - } - - formatValue(id, args) { - return this._interactive.then( - client => client.method('formatValues', client.id, [[id, args]])).then( - values => values[0]); - } - - formatValues(...keys) { - return this._interactive.then( - client => client.method('formatValues', client.id, keys)); - } - - translateFragment(frag) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateFragment(this, langs, frag)); - } - } - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: () => view._interactive.then( - client => client.method('getName', code)), - processString: str => view._interactive.then( - client => client.method('processString', code, str)), - }; - } - - function init(view, client) { - view._observe(); - return client.method( - 'registerView', client.id, getResourceLinks(view._doc.head)).then( - () => client); - } - - function onMutations(mutations) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateMutations(this, langs, mutations)); - } - - function translateDocument(view, langs) { - const html = view._doc.documentElement; - - if (readiness.has(html)) { - return translateFragment(view, langs, html).then( - () => setAllAndEmit(html, langs)); - } - - const translated = - // has the document been already pre-translated? - langs[0].code === html.getAttribute('lang') ? - Promise.resolve() : - translateFragment(view, langs, html).then( - () => setLangDir(html, langs)); - - return translated.then(() => { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - const codes = langs.map(lang => lang.code); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - const code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false, - })); - } - - const remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - document.l10n = new View( - new Client(remote), document); - - //Bug 1204660 - Temporary proxy for shared code. Will be removed once - // l10n.js migration is completed. - navigator.mozL10n = { - setAttributes: document.l10n.setAttributes, - getAttributes: document.l10n.getAttributes, - formatValue: (...args) => document.l10n.formatValue(...args), - translateFragment: (...args) => document.l10n.translateFragment(...args), - once: cb => document.l10n.ready.then(cb), - ready: cb => document.l10n.ready.then(() => { - document.addEventListener('DOMRetranslated', cb); - cb(); - }), - }; - -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/jsshell/l20n.js b/bower_components/l20n/dist/bundle/jsshell/l20n.js deleted file mode 100755 index b5f2a46..0000000 --- a/bower_components/l20n/dist/bundle/jsshell/l20n.js +++ /dev/null @@ -1,1385 +0,0 @@ -(function () { 'use strict'; - - 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; - - const MAX_PLACEABLES = 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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) { - throw this.error('Too many placeables, maximum allowed is ' + - MAX_PLACEABLES); - } - 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, - }; - - var MAX_PLACEABLES$1 = 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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$1) { - throw this.error('Too many placeables (' + placeablesCount + - ', max allowed is ' + MAX_PLACEABLES$1 + ')'); - } - - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '\u2068'; - const PDI = '\u2069'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + - value.length + ', max allowed is ' + - MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; - } - - function MockContext(entries) { - this._getNumberFormatter = function() { - return { - format: function(value) { - return value; - } - }; - }; - this._getEntity = function(lang, id) { - return entries[id]; - }; - - this._getMacro = function(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - }; - } - - this.L20n = { - MockContext, - L20nParser, - PropertiesParser, - format - }; - -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/node/l20n.js b/bower_components/l20n/dist/bundle/node/l20n.js deleted file mode 100755 index ea6cc47..0000000 --- a/bower_components/l20n/dist/bundle/node/l20n.js +++ /dev/null @@ -1,1836 +0,0 @@ -'use strict'; - -var string_prototype_startswith = require('string.prototype.startswith'); -var string_prototype_endswith = require('string.prototype.endswith'); -var fs = require('fs'); - -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 load(url) { - return new Promise(function(resolve, reject) { - fs.readFile(url, function(err, data) { - if (err) { - reject(new L10nError(err.message)); - } else { - resolve(data.toString()); - } - }); - }); -} - -function fetchResource$1(res, lang) { - const url = res.replace('{locale}', lang.code); - return res.endsWith('.json') ? - load(url).then(JSON.parse) : load(url); -} - -const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, -}; - -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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } -}; - -const KNOWN_MACROS = ['plural']; -const MAX_PLACEABLE_LENGTH = 2500; - -// Unicode bidi isolation characters -const FSI = '\u2068'; -const PDI = '\u2069'; - -const resolutionChain = new WeakSet(); - -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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + - value.length + ', max allowed is ' + - MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; -} - -function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); -} - -const 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 -}; - -// utility functions for plural rules methods -function isIn(n, list) { - return list.indexOf(n) !== -1; -} -function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; -} - -// list of all plural rules methods: -// map an integer to the plural form name to use -const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; -} - -class Context { - constructor(env) { - this._env = env; - this._numberFormatters = null; - } - - _formatTuple(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]; - } - } - - _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null, - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple( - lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - - _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - - fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all( - resIds.map( - this._env._getResource.bind(this._env, langs[0]))).then( - () => langs); - } - - _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? - key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', - new L10nError('"' + id + '"' + ' not found in ' + lang.code, - id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then( - nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - - resolveEntities(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatEntity)); - } - - resolveValues(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatValue)); - } - - _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - - _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false, - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - _getMacro(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - -} - -function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? - id : {value: id, attrs: null}; - }); - - this._env.emit('notfounderror', new L10nError( - '"' + Array.from(missingIds).join(', ') + '"' + - ' not found in any language', missingIds), this); - - return resolved; -} - -// Walk an entry node searching for content leaves -function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; (key = keys[i]); i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; -} - -/* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - -function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': - 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz', - }; - - const mods = { - 'fr-x-psaccent': val => - val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => - val.replace(reWords, match => '\u202e' + match + '\u202c'), - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = - (map, val) => val.replace( - reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = - val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function(part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; -} - -const 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') - } -}); - -function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach( - listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach( - listener => listener.apply(this, args)); - } -} - -function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); -} - -function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); -} - -const parsers = { - properties: PropertiesParser, - l20n: L20nParser, -}; - -class Env$1 { - constructor(defaultLang, fetchResource) { - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - - destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach( - resId => deleteIfOrphan(this._resCache, lists, resId)); - } - - _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - - _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry( - entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - - _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? - { code: this.defaultLang, src: 'app' } : - lang; - - const resource = this.fetchResource(res, langToFetch).then( - saveEntries, recover); - - cache.set(id, resource); - - return resource; - } -} - -function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some( - ([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => - key.startsWith(resId) ? cache.delete(key) : null); - } -} - -function amendError(lang, err) { - err.lang = lang; - return err; -} - -exports.fetchResource = fetchResource$1; -exports.Env = Env$1; \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/testing/l20n.js b/bower_components/l20n/dist/bundle/testing/l20n.js deleted file mode 100755 index 73d155e..0000000 --- a/bower_components/l20n/dist/bundle/testing/l20n.js +++ /dev/null @@ -1,308 +0,0 @@ -(function () { 'use strict'; - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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' ], - // value is special-cased in isAttrAllowed - input: [ 'alt', 'placeholder' ], - menuitem: [ 'label' ], - menu: [ 'label' ], - optgroup: [ 'label' ], - option: [ 'label' ], - track: [ 'label' ], - img: [ 'alt' ], - textarea: [ 'placeholder' ], - th: [ 'abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while ((childElement = translationElement.childNodes[0])) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement( - childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild( - translationElement.ownerDocument.createTextNode( - childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; (attr = translationElement.attributes[k]); k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && - child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while ((child = element.previousElementSibling)) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string - .replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }) - .replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>', - }; - - function getResourceLinks(head) { - return Array.prototype.map.call( - head.querySelectorAll('link[rel="localization"]'), - el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && - element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [ - id, - JSON.parse(args.replace(reHtml, match => htmlEntities[match])) - ] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then( - translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - - var dom = { - getResourceLinks: getResourceLinks, - setAttributes: setAttributes, - getAttributes: getAttributes, - translateMutations: translateMutations, - translateFragment: translateFragment - }; - - window.L20n = { - dom - }; - -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/tooling/l20n.js b/bower_components/l20n/dist/bundle/tooling/l20n.js deleted file mode 100755 index d3d8612..0000000 --- a/bower_components/l20n/dist/bundle/tooling/l20n.js +++ /dev/null @@ -1,3552 +0,0 @@ -(function () { 'use strict'; - - 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 load(type, url) { - return new Promise(function(resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - const io = { - extra: function(code, ver, path, type) { - return navigator.mozApps.getLocalizationResource( - code, ver, path, type); - }, - app: function(code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - }, - }; - - function fetchResource(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach( - listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach( - listener => listener.apply(this, args)); - } - } - - function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); - } - - function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); - } - - class Client { - constructor(remote) { - this.id = this; - this.remote = remote; - - const listeners = {}; - this.on = (...args) => addEventListener(listeners, ...args); - this.emit = (...args) => emit(listeners, ...args); - } - - method(name, ...args) { - return this.remote[name](...args); - } - } - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach( - client => client.emit(type, data)); - } - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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' ], - // value is special-cased in isAttrAllowed - input: [ 'alt', 'placeholder' ], - menuitem: [ 'label' ], - menu: [ 'label' ], - optgroup: [ 'label' ], - option: [ 'label' ], - track: [ 'label' ], - img: [ 'alt' ], - textarea: [ 'placeholder' ], - th: [ 'abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while ((childElement = translationElement.childNodes[0])) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement( - childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild( - translationElement.ownerDocument.createTextNode( - childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; (attr = translationElement.attributes[k]); k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && - child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while ((child = element.previousElementSibling)) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string - .replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }) - .replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>', - }; - - function getResourceLinks(head) { - return Array.prototype.map.call( - head.querySelectorAll('link[rel="localization"]'), - el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && - element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [ - id, - JSON.parse(args.replace(reHtml, match => htmlEntities[match])) - ] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then( - translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - // Intl.Locale - function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? - 'rtl' : 'ltr'; - } - - const observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - const readiness = new WeakMap(); - - class View { - constructor(client, doc) { - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then( - () => init(this, client)); - - const observer = new MutationObserver(onMutations.bind(this)); - this._observe = () => observer.observe(doc, observerConfig); - this._disconnect = () => observer.disconnect(); - - const translateView = langs => translateDocument(this, langs); - client.on('translateDocument', translateView); - this.ready = this._interactive.then( - client => client.method('resolvedLanguages')).then( - translateView); - } - - requestLanguages(langs, global) { - return this._interactive.then( - client => client.method('requestLanguages', langs, global)); - } - - _resolveEntities(langs, keys) { - return this._interactive.then( - client => client.method('resolveEntities', client.id, langs, keys)); - } - - formatValue(id, args) { - return this._interactive.then( - client => client.method('formatValues', client.id, [[id, args]])).then( - values => values[0]); - } - - formatValues(...keys) { - return this._interactive.then( - client => client.method('formatValues', client.id, keys)); - } - - translateFragment(frag) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateFragment(this, langs, frag)); - } - } - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: () => view._interactive.then( - client => client.method('getName', code)), - processString: str => view._interactive.then( - client => client.method('processString', code, str)), - }; - } - - function init(view, client) { - view._observe(); - return client.method( - 'registerView', client.id, getResourceLinks(view._doc.head)).then( - () => client); - } - - function onMutations(mutations) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateMutations(this, langs, mutations)); - } - - function translateDocument(view, langs) { - const html = view._doc.documentElement; - - if (readiness.has(html)) { - return translateFragment(view, langs, html).then( - () => setAllAndEmit(html, langs)); - } - - const translated = - // has the document been already pre-translated? - langs[0].code === html.getAttribute('lang') ? - Promise.resolve() : - translateFragment(view, langs, html).then( - () => setLangDir(html, langs)); - - return translated.then(() => { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - const codes = langs.map(lang => lang.code); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - const code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false, - })); - } - - const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, - }; - - var MAX_PLACEABLES$2 = 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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$2) { - throw this.error('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({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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '\u2068'; - const PDI = '\u2069'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + - value.length + ', max allowed is ' + - MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; - } - - class Context { - constructor(env) { - this._env = env; - this._numberFormatters = null; - } - - _formatTuple(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]; - } - } - - _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null, - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple( - lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - - _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - - fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all( - resIds.map( - this._env._getResource.bind(this._env, langs[0]))).then( - () => langs); - } - - _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? - key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', - new L10nError('"' + id + '"' + ' not found in ' + lang.code, - id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then( - nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - - resolveEntities(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatEntity)); - } - - resolveValues(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatValue)); - } - - _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - - _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false, - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - _getMacro(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - - } - - function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? - id : {value: id, attrs: null}; - }); - - this._env.emit('notfounderror', new L10nError( - '"' + Array.from(missingIds).join(', ') + '"' + - ' not found in any language', missingIds), this); - - return resolved; - } - - // Walk an entry node searching for content leaves - function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; (key = keys[i]); i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; - } - - /* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - - function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': - 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz', - }; - - const mods = { - 'fr-x-psaccent': val => - val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => - val.replace(reWords, match => '\u202e' + match + '\u202c'), - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = - (map, val) => val.replace( - reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = - val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function(part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; - } - - const 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') - } - }); - - const parsers = { - properties: PropertiesParser, - l20n: L20nParser, - }; - - class Env { - constructor(defaultLang, fetchResource) { - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - - destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach( - resId => deleteIfOrphan(this._resCache, lists, resId)); - } - - _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - - _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry( - entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - - _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? - { code: this.defaultLang, src: 'app' } : - lang; - - const resource = this.fetchResource(res, langToFetch).then( - saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - } - - function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some( - ([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => - key.startsWith(resId) ? cache.delete(key) : null); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - function prioritizeLocales(def, availableLangs, requested) { - let supportedLocale; - // Find the first locale in the requested list that is supported. - for (let i = 0; i < requested.length; i++) { - const locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || - supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - let availableLangs = Object.create(null); - let defaultLang = null; - let appVersion = null; - - // XXX take last found instead of first? - const metas = head.querySelectorAll( - 'meta[name="availableLanguages"],' + - 'meta[name="defaultLanguage"],' + - 'meta[name="appVersion"]'); - for (let meta of metas) { - const name = meta.getAttribute('name'); - const content = meta.getAttribute('content').trim(); - switch (name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap( - availableLangs, content); - break; - case 'defaultLanguage': - const [lang, rev] = getLangRevisionTuple(content); - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang, - availableLangs, - appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce((seq, cur) => { - const [lang, rev] = getLangRevisionTuple(cur); - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - const [lang, rev] = str.trim().split(':'); - // if revision is missing, use NaN - return [lang, parseInt(rev)]; - } - - function negotiateLanguages( - fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, - requestedLangs) { - - const allAvailableLangs = Object.keys(availableLangs).concat( - additionalLangs || []).concat(Object.keys(pseudo)); - const newLangs = prioritizeLocales( - defaultLang, allAvailableLangs, requestedLangs); - - const langs = newLangs.map(code => ({ - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code), - })); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && - arr1.every((elem, i) => elem === arr2[i]); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (let i = 0, langpack; (langpack = langpacks[i]); i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - const lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && - (!(code in availableLangs) || - parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if ((code in pseudo) && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - class Remote { - constructor(fetchResource, broadcast, requestedLangs) { - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then( - () => this.init(requestedLangs)); - } - - init(requestedLangs) { - const meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env( - this.defaultLanguage, - (...args) => this.fetchResource(this.appVersion, ...args)); - - return this.requestLanguages(requestedLangs); - } - - registerView(view, resources) { - return this.interactive.then(() => { - this.ctxs.set(view, this.env.createContext(resources)); - return true; - }); - } - - unregisterView(view) { - return this.ctxs.delete(view); - } - - resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - } - - formatValues(view, keys) { - return this.languages.then( - langs => this.ctxs.get(view).resolveValues(langs, keys)); - } - - resolvedLanguages() { - return this.languages; - } - - requestLanguages(requestedLangs) { - return changeLanguages.call( - this, getAdditionalLanguages(), requestedLangs); - } - - getName(code) { - return pseudo[code].name; - } - - processString(code, str) { - return pseudo[code].process(str); - } - - handleEvent(evt) { - return changeLanguages.call( - this, evt.detail || getAdditionalLanguages(), navigator.languages); - } - } - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch( - () => []); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - const prevLangs = this.languages || []; - return this.languages = Promise.all([ - additionalLangs, prevLangs]).then( - ([additionalLangs, prevLangs]) => negotiateLanguages( - this.broadcast.bind(this, 'translateDocument'), - this.appVersion, this.defaultLanguage, this.availableLanguages, - additionalLangs, prevLangs, requestedLangs)); - } - - class Node { - constructor() { - this.type = this.constructor.name; - } - } - - class Entry extends Node { - constructor() { - super(); - } - } - - class Identifier extends Node { - constructor(name) { - super(); - this.name = name; - } - } - - class Variable extends Node { - constructor(name) { - super(); - this.name = name; - } - } - - class Global extends Node { - constructor(name) { - super(); - this.name = name; - } - } - - class Value extends Node { - constructor() { - super(); - } - } - - class String$1 extends Value { - constructor(source, content) { - super(); - this.source = source; - this.content = content; - - this._opchar = '"'; - } - } - - class Hash extends Value { - constructor(items) { - super(); - this.items = items; - } - } - - - class Entity extends Entry { - constructor(id, value = null, index = null, attrs = []) { - super(); - this.id = id; - this.value = value; - this.index = index; - this.attrs = attrs; - } - } - - class Resource extends Node { - constructor() { - super(); - this.body = []; - } - } - - class Attribute extends Node { - constructor(id, value, index = null) { - super(); - this.id = id; - this.value = value; - this.index = index; - } - } - - class HashItem extends Node { - constructor(id, value, defItem) { - super(); - this.id = id; - this.value = value; - this.default = defItem; - } - } - - class Comment extends Entry { - constructor(body) { - super(); - this.body = body; - } - } - - class Expression extends Node { - constructor() { - super(); - } - } - - class PropertyExpression extends Expression { - constructor(idref, exp, computed = false) { - super(); - this.idref = idref; - this.exp = exp; - this.computed = computed; - } - } - - class CallExpression extends Expression { - constructor(callee, args) { - super(); - this.callee = callee; - this.args = args; - } - } - - class JunkEntry extends Entry { - constructor(content) { - super(); - this.content = content; - } - } - - var AST = { - Node, - Identifier, - Value, - String: String$1, - Hash, - Entity, - Resource, - Attribute, - HashItem, - Comment, - Variable, - Global, - Expression, - PropertyExpression, - CallExpression, - JunkEntry, - }; - - const MAX_PLACEABLES = 100; - - - class ParseContext { - constructor(string, pos) { - this._config = { - pos: pos - }; - this._source = string; - this._index = 0; - this._length = string.length; - this._curEntryStart = 0; - } - - setPosition(node, start, end) { - if (!this._config.pos) { - return; - } - node._pos = {start, end}; - } - - getResource() { - let resource = new AST.Resource(); - this.setPosition(resource, 0, this._length); - resource._errors = []; - - this.getWS(); - while (this._index < this._length) { - try { - resource.body.push(this.getEntry()); - } catch (e) { - if (e instanceof L10nError) { - resource._errors.push(e); - resource.body.push(this.getJunkEntry()); - } else { - throw e; - } - } - if (this._index < this._length) { - this.getWS(); - } - } - - return resource; - } - - getEntry() { - this._curEntryStart = this._index; - - if (this._source[this._index] === '<') { - ++this._index; - const 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(id, index) { - if (!this.getRequiredWS()) { - throw this.error('Expected white space'); - } - - const ch = this._source.charAt(this._index); - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === null) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++this._index; - - const entity = new AST.Entity(id, value, index, attrs); - this.setPosition(entity, this._curEntryStart, this._index); - return entity; - } - - getValue(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - return null; - } - - getWS() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - } - - getRequiredWS() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - } - - getIdentifier() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - const id = new AST.Identifier(this._source.slice(start, this._index)); - this.setPosition(id, start, this._index); - return id; - } - - getUnicodeChar() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - continue; - } - throw this.error('Illegal unicode escape sequence'); - } - return '\\u' + this._source.slice(this._index - 3, this._index + 1); - } - - getString(opchar, opcharLen) { - let body = []; - let buf = ''; - let placeables = 0; - - this._index += opcharLen - 1; - - const start = this._index + 1; - - let closed = false; - - while (!closed) { - let ch = this._source[++this._index]; - - switch (ch) { - case '\\': - const ch2 = this._source[++this._index]; - if (ch2 === 'u') { - buf += this.getUnicodeChar(); - } else if (ch2 === opchar || ch2 === '\\') { - buf += ch2; - } else if (ch2 === '{' && this._source[this._index + 1] === '{') { - buf += '{'; - } else { - throw this.error('Illegal escape sequence'); - } - break; - case '{': - if (this._source[this._index + 1] === '{') { - if (placeables > MAX_PLACEABLES - 1) { - throw this.error('Too many placeables, maximum allowed is ' + - MAX_PLACEABLES); - } - if (buf.length) { - body.push(buf); - buf = ''; - } - this._index += 2; - this.getWS(); - body.push(this.getExpression()); - this.getWS(); - if (!this._source.startsWith('}}', this._index)) { - throw this.error('Expected "}}"'); - } - this._index += 1; - placeables++; - break; - } - /* falls through */ - default: - if (ch === opchar) { - this._index++; - closed = true; - break; - } - - buf += ch; - if (this._index + 1 >= this._length) { - throw this.error('Unclosed string literal'); - } - } - } - - if (buf.length) { - body.push(buf); - } - - const string = new AST.String( - this._source.slice(start, this._index - 1), body); - this.setPosition(string, start, this._index); - string._opchar = opchar; - - return string; - } - - getAttributes() { - const attrs = []; - - while (true) { - const attr = this.getAttribute(); - attrs.push(attr); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - } - - getAttribute() { - const start = this._index; - const key = this.getIdentifier(); - let index; - - 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(); - const attr = new AST.Attribute(key, this.getValue(), index); - this.setPosition(attr, start, this._index); - return attr; - } - - getHash() { - const start = this._index; - let items = []; - - ++this._index; - this.getWS(); - - while (true) { - items.push(this.getHashItem()); - this.getWS(); - - const 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 "}"'); - } - } - - const hash = new AST.Hash(items); - this.setPosition(hash, start, this._index); - return hash; - } - - getHashItem() { - const start = this._index; - - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const key = this.getIdentifier(); - this.getWS(); - if (this._source[this._index] !== ':') { - throw this.error('Expected ":"'); - } - ++this._index; - this.getWS(); - - const hashItem = new AST.HashItem(key, this.getValue(), defItem); - this.setPosition(hashItem, start, this._index); - return hashItem; - } - - getComment() { - this._index += 2; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - const comment = new AST.Comment(this._source.slice(start, end)); - this.setPosition(comment, start - 2, this._index); - return comment; - } - - getExpression() { - const start = this._index; - let exp = this.getPrimaryExpression(); - - while (true) { - let ch = this._source[this._index]; - if (ch === '.' || ch === '[') { - ++this._index; - exp = this.getPropertyExpression(exp, ch === '[', start); - } else if (ch === '(') { - ++this._index; - exp = this.getCallExpression(exp, start); - } else { - break; - } - } - - return exp; - } - - getPropertyExpression(idref, computed, start) { - let exp; - - if (computed) { - this.getWS(); - exp = this.getExpression(); - this.getWS(); - if (this._source[this._index] !== ']') { - throw this.error('Expected "]"'); - } - ++this._index; - } else { - exp = this.getIdentifier(); - } - - const propExpr = new AST.PropertyExpression(idref, exp, computed); - this.setPosition(propExpr, start, this._index); - return propExpr; - } - - getCallExpression(callee, start) { - this.getWS(); - - const callExpr = new AST.CallExpression(callee, - this.getItemList(this.getExpression, ')')); - this.setPosition(callExpr, start, this._index); - return callExpr; - } - - getPrimaryExpression() { - const start = this._index; - const ch = this._source[this._index]; - - switch (ch) { - case '$': - ++this._index; - const variable = new AST.Variable(this.getIdentifier()); - this.setPosition(variable, start, this._index); - return variable; - case '@': - ++this._index; - const global = new AST.Global(this.getIdentifier()); - this.setPosition(global, start, this._index); - return global; - default: - return this.getIdentifier(); - } - } - - getItemList(callback, closeChar) { - let items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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; - } - - error(message) { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - let lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - let context = this._source.slice(start, pos + 10); - - let msg = message + ' at pos ' + pos + ': `' + context + '`'; - - const err = new L10nError(msg); - err._pos = {start: pos, end: undefined}; - err.offset = pos - start; - err.description = message; - err.context = context; - return err; - } - - getJunkEntry() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - - const junk = new AST.JunkEntry( - this._source.slice(this._curEntryStart, nextEntry)); - - this.setPosition(junk, this._curEntryStart, nextEntry); - return junk; - } - } - - var ASTParser = { - parseResource: function(string, pos = false) { - const parseContext = new ParseContext(string, pos); - return parseContext.getResource(); - }, - }; - - var ASTSerializer = { - serialize: function(ast) { - var string = ''; - for (var id in ast) { - string += this.dumpEntry(ast[id]) + '\n'; - } - return string; - }, - - serializeString: function(ast) { - let string = ''; - - if (typeof(ast) === 'object') { - string += this.dumpValue(ast, 0); - } else { - string += this.dumpString(ast); - } - - return string; - }, - - dumpEntry: function(entry) { - return this.dumpEntity(entry); - }, - - dumpEntity: function(entity) { - var id, val = null, attrs = {}; - var index = ''; - - for (var key in entity) { - switch (key) { - case '$v': - val = entity.$v; - break; - case '$x': - index = this.dumpIndex(entity.$x); - break; - case '$i': - id = this.dumpIdentifier(entity.$i); - break; - default: - attrs[key] = entity[key]; - } - } - - if (Object.keys(attrs).length === 0) { - return '<' + id + index + ' ' + this.dumpValue(val, 0) + '>'; - } else { - return '<' + id + index + ' ' + this.dumpValue(val, 0) + - '\n' + this.dumpAttributes(attrs) + '>'; - } - }, - - dumpIdentifier: function(id) { - return id.replace(/-/g, '_'); - }, - - dumpValue: function(value, depth) { - if (value === null) { - return ''; - } - - // tl;dr - unescaping is a bitch - // - // we need to figure out the rules so that we can reescape - // when we serialize - if (typeof value === 'string') { - return this.dumpString(value); - } - if (Array.isArray(value)) { - return this.dumpComplexString(value); - } - if (typeof value === 'object') { - if (value.o) { - return this.dumpValue(value.v); - } - return this.dumpHash(value, depth); - } - }, - - dumpString: function(str) { - if (str) { - return '"' + str.replace(/"/g, '\\"') + '"'; - } - return ''; - }, - - dumpComplexString: function(chunks) { - var str = '"'; - for (var i = 0; i < chunks.length; i++) { - if (typeof chunks[i] === 'string') { - str += chunks[i].replace(/"/g, '\\"'); - } else { - str += '{{ ' + this.dumpExpression(chunks[i]) + ' }}'; - } - } - return str + '"'; - }, - - dumpAttributes: function(attrs) { - var str = ''; - for (var key in attrs) { - if (attrs[key].x) { - str += ' ' + key + this.dumpIndex(attrs[key].x) + ': ' + - this.dumpValue(attrs[key].v, 1) + '\n'; - } else { - str += ' ' + key + ': ' + this.dumpValue(attrs[key], 1) + '\n'; - } - } - - return str; - }, - - dumpExpression: function(exp) { - switch (exp.t) { - case 'call': - return this.dumpCallExpression(exp); - case 'prop': - return this.dumpPropertyExpression(exp); - } - - return this.dumpPrimaryExpression(exp); - }, - - dumpPropertyExpression: function(exp) { - const idref = this.dumpExpression(exp.e); - let prop; - - if (exp.c) { - prop = this.dumpExpression(exp.p); - return idref + '[' + prop + ']'; - } - - prop = this.dumpIdentifier(exp.p); - return idref + '.' + prop; - }, - - dumpCallExpression: function(exp) { - var pexp = this.dumpExpression(exp.v); - - var attrs = this.dumpItemList(exp.a, this.dumpExpression.bind(this)); - pexp += '(' + attrs + ')'; - return pexp; - }, - - dumpPrimaryExpression: function(exp) { - var ret = ''; - - if (typeof(exp) === 'string') { - return exp; - } - - switch (exp.t) { - case 'glob': - ret += '@'; - ret += exp.v; - break; - case 'var': - ret += '$'; - ret += exp.v; - break; - case 'id': - ret += this.dumpIdentifier(exp.v); - break; - case 'idOrVar': - ret += this.dumpIdentifier(exp.v); - break; - default: - throw new L10nError('Unknown primary expression'); - } - - return ret; - }, - - dumpHash: function(hash, depth) { - var items = []; - var str; - - var defIndex; - if ('__default' in hash) { - defIndex = hash.__default; - } - - for (var key in hash) { - let indent = ' '; - if (key.charAt(0) === '_' && key.charAt(1) === '_') { - continue; - } - - if (key === defIndex) { - indent = ' *'; - } - str = indent + key + ': ' + this.dumpValue(hash[key], depth + 1); - items.push(str); - } - - let indent = new Array(depth + 1).join(' '); // str.repeat - return '{\n' + indent + items.join(',\n' + indent) + '\n'+indent+'}'; - }, - - dumpItemList: function(itemList, cb) { - return itemList.map(cb).join(', '); - }, - - dumpIndex: function(index) { - return '[' + this.dumpItemList(index, this.dumpExpression.bind(this)) + ']'; - }, - }; - - /* jshint -W089 */ - /* jshint -W069 */ - - function EntriesSerializer() { - this.serialize = function (ast) { - var string = ''; - for (var id in ast) { - string += dumpEntry(ast[id]) + '\n'; - } - - return string; - }; - - function dumpEntry(entry) { - return dumpEntity(entry); - } - - function dumpEntity(entity) { - var id, val = null, attrs = {}; - var index = ''; - - for (var key in entity) { - switch (key) { - case '$v': - val = entity.$v; - break; - case '$x': - index = dumpIndex(entity.$x); - break; - case '$i': - id = entity.$i.replace(/-/g, '_'); - break; - default: - attrs[key] = entity[key]; - } - } - - if (Object.keys(attrs).length === 0) { - return '<' + id + index + ' ' + dumpValue(val, 0) + '>'; - } else { - return '<' + id + index + ' ' + dumpValue(val, 0) + - '\n' + dumpAttributes(attrs) + '>'; - } - } - - function dumpIndex(index) { - if (index[0].v === 'plural') { - return '[ @cldr.plural($' + index[1] + ') ]'; - } - } - - function dumpValue(value, depth) { - if (value === null) { - return ''; - } - if (typeof value === 'string') { - return dumpString(value); - } - if (Array.isArray(value)) { - return dumpComplexString(value); - } - if (typeof value === 'object') { - if (value.$o) { - return dumpValue(value.$o); - } - return dumpHash(value, depth); - } - } - - function dumpString(str) { - if (str) { - return '"' + str.replace(/"/g, '\\"') + '"'; - } - return ''; - } - - function dumpComplexString(chunks) { - var str = '"'; - for (var i = 0; i < chunks.length; i++) { - if (typeof chunks[i] === 'string') { - str += chunks[i]; - } else { - str += '{{ ' + chunks[i].v.replace(/-/g, '_') + ' }}'; - } - } - return str + '"'; - } - - function dumpHash(hash, depth) { - var items = []; - var str; - - for (var key in hash) { - str = ' ' + key + ': ' + dumpValue(hash[key]); - items.push(str); - } - - var indent = depth ? ' ' : ''; - return '{\n' + indent + items.join(',\n' + indent) + '\n'+indent+'}'; - } - - function dumpAttributes(attrs) { - var str = ''; - for (var key in attrs) { - if (attrs[key].$x) { - str += ' ' + key + dumpIndex(attrs[key].$x) + ': ' + - dumpValue(attrs[key].$v, 1) + '\n'; - } else { - str += ' ' + key + ': ' + dumpValue(attrs[key], 1) + '\n'; - } - } - - return str; - } - } - - const lang = { - code:'en-US', - src: 'app', - }; - - function MockContext(entries) { - this._getNumberFormatter = function() { - return { - format: function(value) { - return value; - } - }; - }; - this._getEntity = function(lang, id) { - return entries[id]; - }; - - this._getMacro = function(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - }; - } - - window.L20n = { - fetchResource, Client, Remote, View, broadcast, - ASTParser, ASTSerializer, EntriesParser: L20nParser, EntriesSerializer, PropertiesParser, - Context, Env, L10nError, emit, addEventListener, removeEventListener, - prioritizeLocales, MockContext, lang, getPluralRule, walkEntry, walkValue, - pseudo, format - }; - -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/web/l20n-common.js b/bower_components/l20n/dist/bundle/web/l20n-common.js deleted file mode 100755 index ebdfdf2..0000000 --- a/bower_components/l20n/dist/bundle/web/l20n-common.js +++ /dev/null @@ -1,1876 +0,0 @@ -'use strict'; - -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 load(type, url) { - return new Promise(function(resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); -} - -const io = { - extra: function(code, ver, path, type) { - return navigator.mozApps.getLocalizationResource( - code, ver, path, type); - }, - app: function(code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - }, -}; - -function fetchResource$1(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); -} - -const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, -}; - -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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } -}; - -const KNOWN_MACROS = ['plural']; -const MAX_PLACEABLE_LENGTH = 2500; - -// Unicode bidi isolation characters -const FSI = '\u2068'; -const PDI = '\u2069'; - -const resolutionChain = new WeakSet(); - -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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + - value.length + ', max allowed is ' + - MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; -} - -function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); -} - -const 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 -}; - -// utility functions for plural rules methods -function isIn(n, list) { - return list.indexOf(n) !== -1; -} -function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; -} - -// list of all plural rules methods: -// map an integer to the plural form name to use -const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; -} - -class Context { - constructor(env) { - this._env = env; - this._numberFormatters = null; - } - - _formatTuple(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]; - } - } - - _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null, - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple( - lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - - _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - - fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all( - resIds.map( - this._env._getResource.bind(this._env, langs[0]))).then( - () => langs); - } - - _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? - key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', - new L10nError('"' + id + '"' + ' not found in ' + lang.code, - id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then( - nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - - resolveEntities(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatEntity)); - } - - resolveValues(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatValue)); - } - - _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - - _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false, - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - _getMacro(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - -} - -function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? - id : {value: id, attrs: null}; - }); - - this._env.emit('notfounderror', new L10nError( - '"' + Array.from(missingIds).join(', ') + '"' + - ' not found in any language', missingIds), this); - - return resolved; -} - -// Walk an entry node searching for content leaves -function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; (key = keys[i]); i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; -} - -/* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - -function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': - 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz', - }; - - const mods = { - 'fr-x-psaccent': val => - val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => - val.replace(reWords, match => '\u202e' + match + '\u202c'), - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = - (map, val) => val.replace( - reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = - val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function(part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; -} - -const 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') - } -}); - -function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach( - listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach( - listener => listener.apply(this, args)); - } -} - -function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); -} - -function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); -} - -const parsers = { - properties: PropertiesParser, - l20n: L20nParser, -}; - -class Env$1 { - constructor(defaultLang, fetchResource) { - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - - destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach( - resId => deleteIfOrphan(this._resCache, lists, resId)); - } - - _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - - _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry( - entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - - _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? - { code: this.defaultLang, src: 'app' } : - lang; - - const resource = this.fetchResource(res, langToFetch).then( - saveEntries, recover); - - cache.set(id, resource); - - return resource; - } -} - -function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some( - ([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => - key.startsWith(resId) ? cache.delete(key) : null); - } -} - -function amendError(lang, err) { - err.lang = lang; - return err; -} - -exports.fetchResource = fetchResource$1; -exports.Env = Env$1; \ No newline at end of file diff --git a/bower_components/l20n/dist/bundle/web/l20n.js b/bower_components/l20n/dist/bundle/web/l20n.js deleted file mode 100755 index 9ad266f..0000000 --- a/bower_components/l20n/dist/bundle/web/l20n.js +++ /dev/null @@ -1,2553 +0,0 @@ -(function () { 'use strict'; - - function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach( - listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach( - listener => listener.apply(this, args)); - } - } - - function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); - } - - function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); - } - - class Client { - constructor(remote) { - this.id = this; - this.remote = remote; - - const listeners = {}; - this.on = (...args) => addEventListener(listeners, ...args); - this.emit = (...args) => emit(listeners, ...args); - } - - method(name, ...args) { - return this.remote[name](...args); - } - } - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach( - client => client.emit(type, data)); - } - - 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 load(type, url) { - return new Promise(function(resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - const io = { - extra: function(code, ver, path, type) { - return navigator.mozApps.getLocalizationResource( - code, ver, path, type); - }, - app: function(code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - }, - }; - - function fetchResource(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function(ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function() { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function() { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function() { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if ((cc >= 97 && cc <= 122) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - 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) || // a-z - (cc >= 65 && cc <= 90) || // A-Z - (cc >= 48 && cc <= 57) || // 0-9 - cc === 95) { // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function() { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if ((cc > 96 && cc < 103) || // a-f - (cc > 64 && cc < 71) || // A-F - (cc > 47 && cc < 58)) { // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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() { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function(attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function() { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function() { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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() { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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() { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function(message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - }, - }; - - 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '\u2068'; - const PDI = '\u2069'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + - value.length + ', max allowed is ' + - MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && - index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? - resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function() { return 'other'; }; - } - return pluralRules[index]; - } - - class Context { - constructor(env) { - this._env = env; - this._numberFormatters = null; - } - - _formatTuple(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]; - } - } - - _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null, - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple( - lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - - _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - - fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all( - resIds.map( - this._env._getResource.bind(this._env, langs[0]))).then( - () => langs); - } - - _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? - key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', - new L10nError('"' + id + '"' + ' not found in ' + lang.code, - id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then( - nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - - resolveEntities(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatEntity)); - } - - resolveValues(langs, keys) { - return this.fetch(langs).then( - langs => this._resolve(langs, keys, this._formatValue)); - } - - _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - - _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false, - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - _getMacro(lang, id) { - switch(id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - - } - - function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? - id : {value: id, attrs: null}; - }); - - this._env.emit('notfounderror', new L10nError( - '"' + Array.from(missingIds).join(', ') + '"' + - ' not found in any language', missingIds), this); - - return resolved; - } - - // Walk an entry node searching for content leaves - function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; (key = keys[i]); i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; - } - - /* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - - function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': - 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz', - }; - - const mods = { - 'fr-x-psaccent': val => - val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => - val.replace(reWords, match => '\u202e' + match + '\u202c'), - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = - (map, val) => val.replace( - reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = - val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function(part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; - } - - const 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') - } - }); - - const parsers = { - properties: PropertiesParser, - l20n: L20nParser, - }; - - class Env { - constructor(defaultLang, fetchResource) { - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - - destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach( - resId => deleteIfOrphan(this._resCache, lists, resId)); - } - - _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - - _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry( - entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - - _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? - { code: this.defaultLang, src: 'app' } : - lang; - - const resource = this.fetchResource(res, langToFetch).then( - saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - } - - function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some( - ([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => - key.startsWith(resId) ? cache.delete(key) : null); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - // Intl.Locale - function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? - 'rtl' : 'ltr'; - } - - function prioritizeLocales(def, availableLangs, requested) { - let supportedLocale; - // Find the first locale in the requested list that is supported. - for (let i = 0; i < requested.length; i++) { - const locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || - supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - let availableLangs = Object.create(null); - let defaultLang = null; - let appVersion = null; - - // XXX take last found instead of first? - const metas = head.querySelectorAll( - 'meta[name="availableLanguages"],' + - 'meta[name="defaultLanguage"],' + - 'meta[name="appVersion"]'); - for (let meta of metas) { - const name = meta.getAttribute('name'); - const content = meta.getAttribute('content').trim(); - switch (name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap( - availableLangs, content); - break; - case 'defaultLanguage': - const [lang, rev] = getLangRevisionTuple(content); - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang, - availableLangs, - appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce((seq, cur) => { - const [lang, rev] = getLangRevisionTuple(cur); - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - const [lang, rev] = str.trim().split(':'); - // if revision is missing, use NaN - return [lang, parseInt(rev)]; - } - - function negotiateLanguages( - fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, - requestedLangs) { - - const allAvailableLangs = Object.keys(availableLangs).concat( - additionalLangs || []).concat(Object.keys(pseudo)); - const newLangs = prioritizeLocales( - defaultLang, allAvailableLangs, requestedLangs); - - const langs = newLangs.map(code => ({ - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code), - })); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && - arr1.every((elem, i) => elem === arr2[i]); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (let i = 0, langpack; (langpack = langpacks[i]); i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - const lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && - (!(code in availableLangs) || - parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if ((code in pseudo) && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - class Remote { - constructor(fetchResource, broadcast, requestedLangs) { - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then( - () => this.init(requestedLangs)); - } - - init(requestedLangs) { - const meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env( - this.defaultLanguage, - (...args) => this.fetchResource(this.appVersion, ...args)); - - return this.requestLanguages(requestedLangs); - } - - registerView(view, resources) { - return this.interactive.then(() => { - this.ctxs.set(view, this.env.createContext(resources)); - return true; - }); - } - - unregisterView(view) { - return this.ctxs.delete(view); - } - - resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - } - - formatValues(view, keys) { - return this.languages.then( - langs => this.ctxs.get(view).resolveValues(langs, keys)); - } - - resolvedLanguages() { - return this.languages; - } - - requestLanguages(requestedLangs) { - return changeLanguages.call( - this, getAdditionalLanguages(), requestedLangs); - } - - getName(code) { - return pseudo[code].name; - } - - processString(code, str) { - return pseudo[code].process(str); - } - - handleEvent(evt) { - return changeLanguages.call( - this, evt.detail || getAdditionalLanguages(), navigator.languages); - } - } - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch( - () => []); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - const prevLangs = this.languages || []; - return this.languages = Promise.all([ - additionalLangs, prevLangs]).then( - ([additionalLangs, prevLangs]) => negotiateLanguages( - this.broadcast.bind(this, 'translateDocument'), - this.appVersion, this.defaultLanguage, this.availableLanguages, - additionalLangs, prevLangs, requestedLangs)); - } - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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' ], - // value is special-cased in isAttrAllowed - input: [ 'alt', 'placeholder' ], - menuitem: [ 'label' ], - menu: [ 'label' ], - optgroup: [ 'label' ], - option: [ 'label' ], - track: [ 'label' ], - img: [ 'alt' ], - textarea: [ 'placeholder' ], - th: [ 'abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while ((childElement = translationElement.childNodes[0])) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement( - childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild( - translationElement.ownerDocument.createTextNode( - childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; (attr = translationElement.attributes[k]); k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && - child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while ((child = element.previousElementSibling)) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string - .replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }) - .replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>', - }; - - function getResourceLinks(head) { - return Array.prototype.map.call( - head.querySelectorAll('link[rel="localization"]'), - el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && - element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [ - id, - JSON.parse(args.replace(reHtml, match => htmlEntities[match])) - ] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then( - translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - const observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - const readiness = new WeakMap(); - - class View { - constructor(client, doc) { - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then( - () => init(this, client)); - - const observer = new MutationObserver(onMutations.bind(this)); - this._observe = () => observer.observe(doc, observerConfig); - this._disconnect = () => observer.disconnect(); - - const translateView = langs => translateDocument(this, langs); - client.on('translateDocument', translateView); - this.ready = this._interactive.then( - client => client.method('resolvedLanguages')).then( - translateView); - } - - requestLanguages(langs, global) { - return this._interactive.then( - client => client.method('requestLanguages', langs, global)); - } - - _resolveEntities(langs, keys) { - return this._interactive.then( - client => client.method('resolveEntities', client.id, langs, keys)); - } - - formatValue(id, args) { - return this._interactive.then( - client => client.method('formatValues', client.id, [[id, args]])).then( - values => values[0]); - } - - formatValues(...keys) { - return this._interactive.then( - client => client.method('formatValues', client.id, keys)); - } - - translateFragment(frag) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateFragment(this, langs, frag)); - } - } - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: () => view._interactive.then( - client => client.method('getName', code)), - processString: str => view._interactive.then( - client => client.method('processString', code, str)), - }; - } - - function init(view, client) { - view._observe(); - return client.method( - 'registerView', client.id, getResourceLinks(view._doc.head)).then( - () => client); - } - - function onMutations(mutations) { - return this._interactive.then( - client => client.method('resolvedLanguages')).then( - langs => translateMutations(this, langs, mutations)); - } - - function translateDocument(view, langs) { - const html = view._doc.documentElement; - - if (readiness.has(html)) { - return translateFragment(view, langs, html).then( - () => setAllAndEmit(html, langs)); - } - - const translated = - // has the document been already pre-translated? - langs[0].code === html.getAttribute('lang') ? - Promise.resolve() : - translateFragment(view, langs, html).then( - () => setLangDir(html, langs)); - - return translated.then(() => { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - const codes = langs.map(lang => lang.code); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - const code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false, - })); - } - - const remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - document.l10n = new View( - new Client(remote), document); - -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/aisle/l20n.js b/bower_components/l20n/dist/compat/aisle/l20n.js deleted file mode 100755 index 3d98ec0..0000000 --- a/bower_components/l20n/dist/compat/aisle/l20n.js +++ /dev/null @@ -1,765 +0,0 @@ -'use strict'; - -function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -define(['exports'], function (exports) { - 'use strict'; - - var Node = function Node() { - _classCallCheck(this, Node); - - this.type = this.constructor.name; - }; - - var Entry = (function (_Node) { - _inherits(Entry, _Node); - - function Entry() { - _classCallCheck(this, Entry); - - _Node.call(this); - } - - return Entry; - })(Node); - - var Identifier = (function (_Node2) { - _inherits(Identifier, _Node2); - - function Identifier(name) { - _classCallCheck(this, Identifier); - - _Node2.call(this); - this.name = name; - } - - return Identifier; - })(Node); - - var Variable = (function (_Node3) { - _inherits(Variable, _Node3); - - function Variable(name) { - _classCallCheck(this, Variable); - - _Node3.call(this); - this.name = name; - } - - return Variable; - })(Node); - - var Global = (function (_Node4) { - _inherits(Global, _Node4); - - function Global(name) { - _classCallCheck(this, Global); - - _Node4.call(this); - this.name = name; - } - - return Global; - })(Node); - - var Value = (function (_Node5) { - _inherits(Value, _Node5); - - function Value() { - _classCallCheck(this, Value); - - _Node5.call(this); - } - - return Value; - })(Node); - - var String = (function (_Value) { - _inherits(String, _Value); - - function String(source, content) { - _classCallCheck(this, String); - - _Value.call(this); - this.source = source; - this.content = content; - - this._opchar = '"'; - } - - return String; - })(Value); - - var Hash = (function (_Value2) { - _inherits(Hash, _Value2); - - function Hash(items) { - _classCallCheck(this, Hash); - - _Value2.call(this); - this.items = items; - } - - return Hash; - })(Value); - - var Entity = (function (_Entry) { - _inherits(Entity, _Entry); - - function Entity(id) { - var value = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; - var index = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; - var attrs = arguments.length <= 3 || arguments[3] === undefined ? [] : arguments[3]; - - _classCallCheck(this, Entity); - - _Entry.call(this); - this.id = id; - this.value = value; - this.index = index; - this.attrs = attrs; - } - - return Entity; - })(Entry); - - var Resource = (function (_Node6) { - _inherits(Resource, _Node6); - - function Resource() { - _classCallCheck(this, Resource); - - _Node6.call(this); - this.body = []; - } - - return Resource; - })(Node); - - var Attribute = (function (_Node7) { - _inherits(Attribute, _Node7); - - function Attribute(id, value) { - var index = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; - - _classCallCheck(this, Attribute); - - _Node7.call(this); - this.id = id; - this.value = value; - this.index = index; - } - - return Attribute; - })(Node); - - var HashItem = (function (_Node8) { - _inherits(HashItem, _Node8); - - function HashItem(id, value, defItem) { - _classCallCheck(this, HashItem); - - _Node8.call(this); - this.id = id; - this.value = value; - this.default = defItem; - } - - return HashItem; - })(Node); - - var Comment = (function (_Entry2) { - _inherits(Comment, _Entry2); - - function Comment(body) { - _classCallCheck(this, Comment); - - _Entry2.call(this); - this.body = body; - } - - return Comment; - })(Entry); - - var Expression = (function (_Node9) { - _inherits(Expression, _Node9); - - function Expression() { - _classCallCheck(this, Expression); - - _Node9.call(this); - } - - return Expression; - })(Node); - - var PropertyExpression = (function (_Expression) { - _inherits(PropertyExpression, _Expression); - - function PropertyExpression(idref, exp) { - var computed = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; - - _classCallCheck(this, PropertyExpression); - - _Expression.call(this); - this.idref = idref; - this.exp = exp; - this.computed = computed; - } - - return PropertyExpression; - })(Expression); - - var CallExpression = (function (_Expression2) { - _inherits(CallExpression, _Expression2); - - function CallExpression(callee, args) { - _classCallCheck(this, CallExpression); - - _Expression2.call(this); - this.callee = callee; - this.args = args; - } - - return CallExpression; - })(Expression); - - var JunkEntry = (function (_Entry3) { - _inherits(JunkEntry, _Entry3); - - function JunkEntry(content) { - _classCallCheck(this, JunkEntry); - - _Entry3.call(this); - this.content = content; - } - - return JunkEntry; - })(Entry); - - var AST = { - Node: Node, - Identifier: Identifier, - Value: Value, - String: String, - Hash: Hash, - Entity: Entity, - Resource: Resource, - Attribute: Attribute, - HashItem: HashItem, - Comment: Comment, - Variable: Variable, - Global: Global, - Expression: Expression, - PropertyExpression: PropertyExpression, - CallExpression: CallExpression, - JunkEntry: JunkEntry - }; - - 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; - - var MAX_PLACEABLES = 100; - - var ParseContext = (function () { - function ParseContext(string, pos) { - _classCallCheck(this, ParseContext); - - this._config = { - pos: pos - }; - this._source = string; - this._index = 0; - this._length = string.length; - this._curEntryStart = 0; - } - - ParseContext.prototype.setPosition = function setPosition(node, start, end) { - if (!this._config.pos) { - return; - } - node._pos = { start: start, end: end }; - }; - - ParseContext.prototype.getResource = function getResource() { - var resource = new AST.Resource(); - this.setPosition(resource, 0, this._length); - resource._errors = []; - - this.getWS(); - while (this._index < this._length) { - try { - resource.body.push(this.getEntry()); - } catch (e) { - if (e instanceof L10nError) { - resource._errors.push(e); - resource.body.push(this.getJunkEntry()); - } else { - throw e; - } - } - if (this._index < this._length) { - this.getWS(); - } - } - - return resource; - }; - - ParseContext.prototype.getEntry = function getEntry() { - this._curEntryStart = this._index; - - 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'); - }; - - ParseContext.prototype.getEntity = function getEntity(id, index) { - if (!this.getRequiredWS()) { - throw this.error('Expected white space'); - } - - var ch = this._source.charAt(this._index); - var value = this.getValue(ch, index === undefined); - var attrs = undefined; - - if (value === null) { - 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; - - var entity = new AST.Entity(id, value, index, attrs); - this.setPosition(entity, this._curEntryStart, this._index); - return entity; - }; - - ParseContext.prototype.getValue = function getValue() { - var ch = arguments.length <= 0 || arguments[0] === undefined ? this._source[this._index] : arguments[0]; - var optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - return null; - }; - - ParseContext.prototype.getWS = function getWS() { - var cc = this._source.charCodeAt(this._index); - - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }; - - ParseContext.prototype.getRequiredWS = function getRequiredWS() { - 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; - }; - - ParseContext.prototype.getIdentifier = function getIdentifier() { - 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); - } - - var id = new AST.Identifier(this._source.slice(start, this._index)); - this.setPosition(id, start, this._index); - return id; - }; - - ParseContext.prototype.getUnicodeChar = function getUnicodeChar() { - 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'); - } - return '\\u' + this._source.slice(this._index - 3, this._index + 1); - }; - - ParseContext.prototype.getString = function getString(opchar, opcharLen) { - var body = []; - var buf = ''; - var placeables = 0; - - this._index += opcharLen - 1; - - var start = this._index + 1; - - var closed = false; - - while (!closed) { - var ch = this._source[++this._index]; - - switch (ch) { - case '\\': - var ch2 = this._source[++this._index]; - if (ch2 === 'u') { - buf += this.getUnicodeChar(); - } else if (ch2 === opchar || ch2 === '\\') { - buf += ch2; - } else if (ch2 === '{' && this._source[this._index + 1] === '{') { - buf += '{'; - } else { - throw this.error('Illegal escape sequence'); - } - break; - case '{': - if (this._source[this._index + 1] === '{') { - if (placeables > MAX_PLACEABLES - 1) { - throw this.error('Too many placeables, maximum allowed is ' + MAX_PLACEABLES); - } - if (buf.length) { - body.push(buf); - buf = ''; - } - this._index += 2; - this.getWS(); - body.push(this.getExpression()); - this.getWS(); - if (!this._source.startsWith('}}', this._index)) { - throw this.error('Expected "}}"'); - } - this._index += 1; - placeables++; - break; - } - - default: - if (ch === opchar) { - this._index++; - closed = true; - break; - } - - buf += ch; - if (this._index + 1 >= this._length) { - throw this.error('Unclosed string literal'); - } - } - } - - if (buf.length) { - body.push(buf); - } - - var string = new AST.String(this._source.slice(start, this._index - 1), body); - this.setPosition(string, start, this._index); - string._opchar = opchar; - - return string; - }; - - ParseContext.prototype.getAttributes = function getAttributes() { - var attrs = []; - - while (true) { - var attr = this.getAttribute(); - attrs.push(attr); - var ws1 = this.getRequiredWS(); - var ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }; - - ParseContext.prototype.getAttribute = function getAttribute() { - var start = this._index; - 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 attr = new AST.Attribute(key, this.getValue(), index); - this.setPosition(attr, start, this._index); - return attr; - }; - - ParseContext.prototype.getHash = function getHash() { - var start = this._index; - var items = []; - - ++this._index; - this.getWS(); - - while (true) { - items.push(this.getHashItem()); - 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 "}"'); - } - } - - var hash = new AST.Hash(items); - this.setPosition(hash, start, this._index); - return hash; - }; - - ParseContext.prototype.getHashItem = function getHashItem() { - var start = this._index; - - 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(); - - var hashItem = new AST.HashItem(key, this.getValue(), defItem); - this.setPosition(hashItem, start, this._index); - return hashItem; - }; - - ParseContext.prototype.getComment = function getComment() { - 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; - var comment = new AST.Comment(this._source.slice(start, end)); - this.setPosition(comment, start - 2, this._index); - return comment; - }; - - ParseContext.prototype.getExpression = function getExpression() { - var start = this._index; - var exp = this.getPrimaryExpression(); - - while (true) { - var ch = this._source[this._index]; - if (ch === '.' || ch === '[') { - ++this._index; - exp = this.getPropertyExpression(exp, ch === '[', start); - } else if (ch === '(') { - ++this._index; - exp = this.getCallExpression(exp, start); - } else { - break; - } - } - - return exp; - }; - - ParseContext.prototype.getPropertyExpression = function getPropertyExpression(idref, computed, start) { - 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(); - } - - var propExpr = new AST.PropertyExpression(idref, exp, computed); - this.setPosition(propExpr, start, this._index); - return propExpr; - }; - - ParseContext.prototype.getCallExpression = function getCallExpression(callee, start) { - this.getWS(); - - var callExpr = new AST.CallExpression(callee, this.getItemList(this.getExpression, ')')); - this.setPosition(callExpr, start, this._index); - return callExpr; - }; - - ParseContext.prototype.getPrimaryExpression = function getPrimaryExpression() { - var start = this._index; - var ch = this._source[this._index]; - - switch (ch) { - case '$': - ++this._index; - var variable = new AST.Variable(this.getIdentifier()); - this.setPosition(variable, start, this._index); - return variable; - case '@': - ++this._index; - var global = new AST.Global(this.getIdentifier()); - this.setPosition(global, start, this._index); - return global; - default: - return this.getIdentifier(); - } - }; - - ParseContext.prototype.getItemList = function getItemList(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; - }; - - ParseContext.prototype.error = function error(message) { - 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); - err._pos = { start: pos, end: undefined }; - err.offset = pos - start; - err.description = message; - err.context = context; - return err; - }; - - ParseContext.prototype.getJunkEntry = function getJunkEntry() { - 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; - - var junk = new AST.JunkEntry(this._source.slice(this._curEntryStart, nextEntry)); - - this.setPosition(junk, this._curEntryStart, nextEntry); - return junk; - }; - - return ParseContext; - })(); - - var parser = { - parseResource: function (string) { - var pos = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - var parseContext = new ParseContext(string, pos); - return parseContext.getResource(); - } - }; - - exports.L20nParser = parser; -}); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/bridge/l20n-client.js b/bower_components/l20n/dist/compat/bridge/l20n-client.js deleted file mode 100755 index 0bd3af0..0000000 --- a/bower_components/l20n/dist/compat/bridge/l20n-client.js +++ /dev/null @@ -1,507 +0,0 @@ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - var Client = bridge.client; - var channel = new BroadcastChannel('l20n-channel'); - - 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 getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), function (el) { - return el.getAttribute('href'); - }); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - 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 translateMutations(view, langs, mutations) { - var targets = new Set(); - - for (var _iterator = mutations, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var mutation = _ref; - - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (var _iterator2 = mutation.addedNodes, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - var addedNode = _ref2; - - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function _translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, 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._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(function (translations) { - return applyTranslations(view, elements, translations); - }); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (var i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(function (resolve) { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - function getDirection(code) { - var tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; - } - - var observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - var readiness = new WeakMap(); - - var View = (function () { - function View(client, doc) { - var _this = this; - - _classCallCheck(this, View); - - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then(function () { - return init(_this, client); - }); - - var observer = new MutationObserver(onMutations.bind(this)); - this._observe = function () { - return observer.observe(doc, observerConfig); - }; - this._disconnect = function () { - return observer.disconnect(); - }; - - var translateView = function (langs) { - return translateDocument(_this, langs); - }; - client.on('translateDocument', translateView); - this.ready = this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(translateView); - } - - View.prototype.requestLanguages = function requestLanguages(langs, global) { - return this._interactive.then(function (client) { - return client.method('requestLanguages', langs, global); - }); - }; - - View.prototype._resolveEntities = function _resolveEntities(langs, keys) { - return this._interactive.then(function (client) { - return client.method('resolveEntities', client.id, langs, keys); - }); - }; - - View.prototype.formatValue = function formatValue(id, args) { - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, [[id, args]]); - }).then(function (values) { - return values[0]; - }); - }; - - View.prototype.formatValues = function formatValues() { - for (var _len = arguments.length, keys = Array(_len), _key = 0; _key < _len; _key++) { - keys[_key] = arguments[_key]; - } - - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, keys); - }); - }; - - View.prototype.translateFragment = function translateFragment(frag) { - var _this2 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return _translateFragment(_this2, langs, frag); - }); - }; - - return View; - })(); - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: function () { - return view._interactive.then(function (client) { - return client.method('getName', code); - }); - }, - processString: function (str) { - return view._interactive.then(function (client) { - return client.method('processString', code, str); - }); - } - }; - } - - function init(view, client) { - view._observe(); - return client.method('registerView', client.id, getResourceLinks(view._doc.head)).then(function () { - return client; - }); - } - - function onMutations(mutations) { - var _this3 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return translateMutations(_this3, langs, mutations); - }); - } - - function translateDocument(view, langs) { - var html = view._doc.documentElement; - - if (readiness.has(html)) { - return _translateFragment(view, langs, html).then(function () { - return setAllAndEmit(html, langs); - }); - } - - var translated = langs[0].code === html.getAttribute('lang') ? Promise.resolve() : _translateFragment(view, langs, html).then(function () { - return setLangDir(html, langs); - }); - - return translated.then(function () { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - var codes = langs.map(function (lang) { - return lang.code; - }); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - var code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false - })); - } - - var client = new Client({ - service: 'l20n', - endpoint: channel, - timeout: false - }); - - window.addEventListener('pageshow', function () { - return client.connect(); - }); - window.addEventListener('pagehide', function () { - return client.disconnect(); - }); - - document.l10n = new View(client, document); - - navigator.mozL10n = { - setAttributes: document.l10n.setAttributes, - getAttributes: document.l10n.getAttributes, - formatValue: function () { - var _document$l10n; - - return (_document$l10n = document.l10n).formatValue.apply(_document$l10n, arguments); - }, - translateFragment: function () { - var _document$l10n2; - - return (_document$l10n2 = document.l10n).translateFragment.apply(_document$l10n2, arguments); - }, - once: function (cb) { - return document.l10n.ready.then(cb); - }, - ready: function (cb) { - return document.l10n.ready.then(function () { - document.addEventListener('DOMRetranslated', cb); - cb(); - }); - } - }; -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/bridge/l20n-service.js b/bower_components/l20n/dist/compat/bridge/l20n-service.js deleted file mode 100755 index 3566734..0000000 --- a/bower_components/l20n/dist/compat/bridge/l20n-service.js +++ /dev/null @@ -1,2131 +0,0 @@ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - var Service = bridge.service; - var channel = new BroadcastChannel('l20n-channel'); - - function broadcast(type, data) { - return this.service.broadcast(type, data); - } - - 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 load(type, url) { - return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - var io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } - }; - - function fetchResource(ver, res, lang) { - var url = res.replace('{locale}', lang.code); - var type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - 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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } - }; - - 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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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 KNOWN_MACROS = ['plural']; - var MAX_PLACEABLE_LENGTH = 2500; - - var FSI = '⁨'; - var PDI = '⁩'; - - var resolutionChain = new WeakSet(); - - 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 newLocals = undefined, - value = undefined; - - try { - var _resolveIdentifier = resolveIdentifier(ctx, lang, args, id); - - newLocals = _resolveIdentifier[0]; - value = _resolveIdentifier[1]; - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - var formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - 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 + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(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 = subPlaceable(locals, ctx, lang, args, cur.name); - - var value = _subPlaceable[1]; - - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(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(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - return selector; - } - - var argValue = index[0].args ? resolveIdentifier(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(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(locals, ctx, lang, args, expr); - } - - if (index) { - var selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - var defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(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 Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - Context.prototype._formatTuple = function _formatTuple(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]; - } - }; - - 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(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - var resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(function () { - return langs; - }); - }; - - Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) { - var _this = 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 = _this._getEntity(lang, id); - - if (entity) { - return formatter.call(_this, lang, args, entity, id); - } - - _this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), _this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(function (nextLangs) { - return _this._resolve(nextLangs, keys, formatter, resolved); - }); - }; - - Context.prototype.resolveEntities = function resolveEntities(langs, keys) { - var _this2 = this; - - return this.fetch(langs).then(function (langs) { - return _this2._resolve(langs, keys, _this2._formatEntity); - }); - }; - - Context.prototype.resolveValues = function resolveValues(langs, keys) { - var _this3 = this; - - return this.fetch(langs).then(function (langs) { - return _this3._resolve(langs, keys, _this3._formatValue); - }); - }; - - Context.prototype._getEntity = function _getEntity(lang, id) { - var cache = this._env._resCache; - var resIds = Array.from(this._env._resLists.get(this)); - - for (var i = 0, resId = undefined; resId = 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._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - var formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._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 _this4 = 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 === _this4._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; - } - - 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 replaceChars = function (map, val) { - return val.replace(reAlphas, function (match) { - return map.charAt(match.charCodeAt(0) - 65); - }); - }; - - 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') - } - }); - - function emit(listeners) { - var _this5 = this; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(function (listener) { - return listener.apply(_this5, args); - }); - } - - if (listeners[type]) { - listeners[type].slice().forEach(function (listener) { - return listener.apply(_this5, 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 parsers = { - properties: PropertiesParser, - l20n: L20nParser - }; - - var Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - 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(resIds) { - var ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - }; - - Env.prototype.destroyContext = function destroyContext(ctx) { - var _this6 = this; - - var lists = this._resLists; - var resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(function (resId) { - return deleteIfOrphan(_this6._resCache, lists, resId); - }); - }; - - Env.prototype._parse = function _parse(syntax, lang, data) { - var _this7 = this; - - var parser = parsers[syntax]; - if (!parser) { - return data; - } - - var emit = function (type, err) { - return _this7.emit(type, amendError(lang, err)); - }; - return parser.parse.call(parser, emit, 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 _this8 = 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 = _this8._parse(syntax, lang, data); - cache.set(id, _this8._create(lang, entries)); - }; - - var recover = function (err) { - err.lang = lang; - _this8.emit('fetcherror', err); - cache.set(id, err); - }; - - var langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - }; - - return Env; - })(); - - function deleteIfOrphan(cache, lists, resId) { - var isNeeded = Array.from(lists).some(function (_ref4) { - var ctx = _ref4[0]; - var resIds = _ref4[1]; - return resIds.has(resId); - }); - - if (!isNeeded) { - cache.forEach(function (val, key) { - return key.startsWith(resId) ? cache.delete(key) : null; - }); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(function (resolve) { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - function prioritizeLocales(def, availableLangs, requested) { - var supportedLocale = undefined; - - for (var i = 0; i < requested.length; i++) { - var locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - var availableLangs = Object.create(null); - var defaultLang = null; - var appVersion = null; - - var metas = head.querySelectorAll('meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"]'); - for (var _iterator = metas, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var meta = _ref; - - var _name = meta.getAttribute('name'); - var content = meta.getAttribute('content').trim(); - switch (_name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap(availableLangs, content); - break; - case 'defaultLanguage': - var _getLangRevisionTuple = getLangRevisionTuple(content), - lang = _getLangRevisionTuple[0], - rev = _getLangRevisionTuple[1]; - - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang: defaultLang, - availableLangs: availableLangs, - appVersion: appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce(function (seq, cur) { - var _getLangRevisionTuple2 = getLangRevisionTuple(cur); - - var lang = _getLangRevisionTuple2[0]; - var rev = _getLangRevisionTuple2[1]; - - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - var _str$trim$split = str.trim().split(':'); - - var lang = _str$trim$split[0]; - var rev = _str$trim$split[1]; - - return [lang, parseInt(rev)]; - } - - function negotiateLanguages(fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, requestedLangs) { - - var allAvailableLangs = Object.keys(availableLangs).concat(additionalLangs || []).concat(Object.keys(pseudo)); - var newLangs = prioritizeLocales(defaultLang, allAvailableLangs, requestedLangs); - - var langs = newLangs.map(function (code) { - return { - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code) - }; - }); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && arr1.every(function (elem, i) { - return elem === arr2[i]; - }); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (var i = 0, langpack = undefined; langpack = langpacks[i]; i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - var lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && (!(code in availableLangs) || parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if (code in pseudo && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - var Remote = (function () { - function Remote(fetchResource, broadcast, requestedLangs) { - var _this9 = this; - - _classCallCheck(this, Remote); - - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then(function () { - return _this9.init(requestedLangs); - }); - } - - Remote.prototype.init = function init(requestedLangs) { - var _this10 = this; - - var meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env(this.defaultLanguage, function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return _this10.fetchResource.apply(_this10, [_this10.appVersion].concat(args)); - }); - - return this.requestLanguages(requestedLangs); - }; - - Remote.prototype.registerView = function registerView(view, resources) { - var _this11 = this; - - return this.interactive.then(function () { - _this11.ctxs.set(view, _this11.env.createContext(resources)); - return true; - }); - }; - - Remote.prototype.unregisterView = function unregisterView(view) { - return this.ctxs.delete(view); - }; - - Remote.prototype.resolveEntities = function resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - }; - - Remote.prototype.formatValues = function formatValues(view, keys) { - var _this12 = this; - - return this.languages.then(function (langs) { - return _this12.ctxs.get(view).resolveValues(langs, keys); - }); - }; - - Remote.prototype.resolvedLanguages = function resolvedLanguages() { - return this.languages; - }; - - Remote.prototype.requestLanguages = function requestLanguages(requestedLangs) { - return changeLanguages.call(this, getAdditionalLanguages(), requestedLangs); - }; - - Remote.prototype.getName = function getName(code) { - return pseudo[code].name; - }; - - Remote.prototype.processString = function processString(code, str) { - return pseudo[code].process(str); - }; - - Remote.prototype.handleEvent = function handleEvent(evt) { - return changeLanguages.call(this, evt.detail || getAdditionalLanguages(), navigator.languages); - }; - - return Remote; - })(); - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch(function () { - return []; - }); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - var _this13 = this; - - var prevLangs = this.languages || []; - return this.languages = Promise.all([additionalLangs, prevLangs]).then(function (_ref5) { - var additionalLangs = _ref5[0]; - var prevLangs = _ref5[1]; - return negotiateLanguages(_this13.broadcast.bind(_this13, 'translateDocument'), _this13.appVersion, _this13.defaultLanguage, _this13.availableLanguages, additionalLangs, prevLangs, requestedLangs); - }); - } - - var remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - remote.service = new Service('l20n').method('registerView', function () { - return remote.registerView.apply(remote, arguments); - }).method('resolvedLanguages', function () { - return remote.resolvedLanguages.apply(remote, arguments); - }).method('requestLanguages', function () { - return remote.requestLanguages.apply(remote, arguments); - }).method('resolveEntities', function () { - return remote.resolveEntities.apply(remote, arguments); - }).method('formatValues', function () { - return remote.formatValues.apply(remote, arguments); - }).method('getName', function () { - return remote.getName.apply(remote, arguments); - }).method('processString', function () { - return remote.processString.apply(remote, arguments); - }).on('disconnect', function (clientId) { - return remote.unregisterView(clientId); - }).listen(channel); -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/gaia/build/l20n.js b/bower_components/l20n/dist/compat/gaia/build/l20n.js deleted file mode 100755 index 1145754..0000000 --- a/bower_components/l20n/dist/compat/gaia/build/l20n.js +++ /dev/null @@ -1,2814 +0,0 @@ -'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, lang) { - var url = decodeURI(res).replace('{locale}', lang.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$1(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$1(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$1(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 replaceChars = function (map, val) { - return val.replace(reAlphas, function (match) { - return map.charAt(match.charCodeAt(0) - 65); - }); - }; - - 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$1 = 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 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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } -}; - -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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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 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 (_ref, cur) { - var localsSeq = _ref[0]; - var valueSeq = _ref[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 Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - 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._env.emit('resolveerror', err, this); - 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(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - var resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(function () { - return langs; - }); - }; - - Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) { - var _this = 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 _ref2 = Array.isArray(key) ? key : [key, undefined]; - - var id = _ref2[0]; - var args = _ref2[1]; - - var entity = _this._getEntity(lang, id); - - if (entity) { - return formatter.call(_this, lang, args, entity, id); - } - - _this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), _this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(function (nextLangs) { - return _this._resolve(nextLangs, keys, formatter, resolved); - }); - }; - - Context.prototype.resolveEntities = function resolveEntities(langs, keys) { - var _this2 = this; - - return this.fetch(langs).then(function (langs) { - return _this2._resolve(langs, keys, _this2._formatEntity); - }); - }; - - Context.prototype.resolveValues = function resolveValues(langs, keys) { - var _this3 = this; - - return this.fetch(langs).then(function (langs) { - return _this3._resolve(langs, keys, _this3._formatValue); - }); - }; - - Context.prototype._getEntity = function _getEntity(lang, id) { - var cache = this._env._resCache; - var resIds = Array.from(this._env._resLists.get(this)); - - for (var i = 0, resId = undefined; resId = 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._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - var formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._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 _this4 = 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 === _this4._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; -} - -function emit(listeners) { - var _this5 = this; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(function (listener) { - return listener.apply(_this5, args); - }); - } - - if (listeners[type]) { - listeners[type].slice().forEach(function (listener) { - return listener.apply(_this5, 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 parsers = { - properties: PropertiesParser, - l20n: L20nParser -}; - -var Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - 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(resIds) { - var ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - }; - - Env.prototype.destroyContext = function destroyContext(ctx) { - var _this6 = this; - - var lists = this._resLists; - var resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(function (resId) { - return deleteIfOrphan(_this6._resCache, lists, resId); - }); - }; - - Env.prototype._parse = function _parse(syntax, lang, data) { - var _this7 = this; - - var parser = parsers[syntax]; - if (!parser) { - return data; - } - - var emit = function (type, err) { - return _this7.emit(type, amendError$1(lang, err)); - }; - return parser.parse.call(parser, emit, 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$1[lang.code].process); - } - return pseudoentries; - }; - - Env.prototype._getResource = function _getResource(lang, res) { - var _this8 = 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 = _this8._parse(syntax, lang, data); - cache.set(id, _this8._create(lang, entries)); - }; - - var recover = function (err) { - err.lang = lang; - _this8.emit('fetcherror', err); - cache.set(id, err); - }; - - var langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - }; - - return Env; -})(); - -function deleteIfOrphan(cache, lists, resId) { - var isNeeded = Array.from(lists).some(function (_ref3) { - var ctx = _ref3[0]; - var resIds = _ref3[1]; - return resIds.has(resId); - }); - - if (!isNeeded) { - cache.forEach(function (val, key) { - return key.startsWith(resId) ? cache.delete(key) : null; - }); - } -} - -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) { - Context.call(this, env); -} - -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, key; - - 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; - 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, v; - - 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(defaultLang, fetchResource) { - Env.call(this, defaultLang, fetchResource); -} - -LegacyEnv.prototype = Object.create(Env.prototype); - -LegacyEnv.prototype.createContext = function (resIds) { - var ctx = new LegacyContext(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; -}; - -LegacyEnv.prototype._parse = function (syntax, lang, data) { - var _this9 = this; - - var emit = function (type, err) { - return _this9.emit(type, amendError$1(lang, err)); - }; - return PropertiesParser$1.parse.call(PropertiesParser$1, emit, data); -}; - -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$1[lang.code].process)); -} - -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 getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), function (el) { - return el.getAttribute('href'); - }); -} - -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, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); -} - -function getElementsTranslation(view, langs, 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._resolveEntities(langs, keys); -} - -function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(function (translations) { - return applyTranslations(view, elements, translations); - }); -} - -function applyTranslations(view, elems, translations) { - view._disconnect(); - for (var i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); -} - -if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; -} - -function getDirection(code) { - var tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; -} - -function serializeContext(ctx, lang) { - var cache = ctx._env._resCache; - var resIds = Array.from(ctx._env._resLists.get(ctx)); - return 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, lang) { - var cache = ctx._env._resCache; - var resIds = Array.from(ctx._env._resLists.get(ctx)); - return 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.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(htmloptimizer.config.GAIA_DEFAULT_LOCALE, fetchResource); - this.ctx = this.env.createContext(getResourceLinks(this.doc.head)); - - 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._observe = function _observe() {}; - - View.prototype._disconnect = function _disconnect() {}; - - View.prototype._resolveEntities = function _resolveEntities(langs, keys) { - return this.ctx.resolveEntities(langs, keys); - }; - - View.prototype.translateDocument = function translateDocument(code) { - var _this10 = this; - - var dir = getDirection(code); - var langs = [{ code: code, src: 'app' }]; - var setDocLang = function () { - _this10.doc.documentElement.lang = code; - _this10.doc.documentElement.dir = dir; - }; - return this.ctx.fetch(langs).then(function (langs) { - return translateFragment(_this10, langs, _this10.doc.documentElement); - }).then(setDocLang); - }; - - View.prototype.serializeResources = function serializeResources(code) { - var _this11 = this; - - var lang = { - code: code, - src: code in pseudo$1 ? 'pseudo' : 'app' - }; - return fetchContext(this.ctx, lang).then(function () { - var _ref7 = _this11.isLegacy ? serializeLegacyContext(_this11.ctx, lang) : serializeContext(_this11.ctx, lang); - - 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) { - _this11.htmloptimizer.dump('[l10n] [' + lang.code + ']: ' + notFoundErrors.length + ' missing compared to en-US: ' + notFoundErrors.join(', ')); - } - if (malformedErrors.length) { - _this11.htmloptimizer.dump('[l10n] [' + lang.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 fetchContext(ctx, lang) { - var sourceLang = { code: 'en-US', src: 'app' }; - return Promise.all([sourceLang, lang].map(function (lang) { - return ctx.fetch([lang]); - })); -} - -function getView(htmloptimizer) { - var htmlFetch = function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return fetchResource.apply(undefined, [htmloptimizer].concat(args)); - }; - return new View(htmloptimizer, htmlFetch); -} - -exports.getView = getView; -exports.pseudo = pseudo$1; -exports.walkValue = walkValue$1; \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/gaia/l20n.js b/bower_components/l20n/dist/compat/gaia/l20n.js deleted file mode 100755 index ff45baa..0000000 --- a/bower_components/l20n/dist/compat/gaia/l20n.js +++ /dev/null @@ -1,2616 +0,0 @@ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - function emit(listeners) { - var _this = this; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(function (listener) { - return listener.apply(_this, args); - }); - } - - if (listeners[type]) { - listeners[type].slice().forEach(function (listener) { - return listener.apply(_this, 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 Client = (function () { - function Client(remote) { - _classCallCheck(this, Client); - - this.id = this; - this.remote = remote; - - var listeners = {}; - this.on = function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return addEventListener.apply(undefined, [listeners].concat(args)); - }; - this.emit = function () { - for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - args[_key3] = arguments[_key3]; - } - - return emit.apply(undefined, [listeners].concat(args)); - }; - } - - Client.prototype.method = function method(name) { - var _remote; - - for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { - args[_key4 - 1] = arguments[_key4]; - } - - return (_remote = this.remote)[name].apply(_remote, args); - }; - - return Client; - })(); - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach(function (client) { - return client.emit(type, data); - }); - } - - 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 load(type, url) { - return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - var io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } - }; - - function fetchResource(ver, res, lang) { - var url = res.replace('{locale}', lang.code); - var type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - 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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } - }; - - 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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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 KNOWN_MACROS = ['plural']; - var MAX_PLACEABLE_LENGTH = 2500; - - var FSI = '⁨'; - var PDI = '⁩'; - - var resolutionChain = new WeakSet(); - - 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 newLocals = undefined, - value = undefined; - - try { - var _resolveIdentifier = resolveIdentifier(ctx, lang, args, id); - - newLocals = _resolveIdentifier[0]; - value = _resolveIdentifier[1]; - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - var formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - 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 + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - 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 { - var _subPlaceable = subPlaceable(locals, ctx, lang, args, cur.name); - - var value = _subPlaceable[1]; - - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(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(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - return selector; - } - - var argValue = index[0].args ? resolveIdentifier(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(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(locals, ctx, lang, args, expr); - } - - if (index) { - var selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - var defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(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 Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - Context.prototype._formatTuple = function _formatTuple(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]; - } - }; - - 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(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - var resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(function () { - return langs; - }); - }; - - Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) { - var _this2 = 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 _ref5 = Array.isArray(key) ? key : [key, undefined]; - - var id = _ref5[0]; - var args = _ref5[1]; - - var entity = _this2._getEntity(lang, id); - - if (entity) { - return formatter.call(_this2, lang, args, entity, id); - } - - _this2._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), _this2); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(function (nextLangs) { - return _this2._resolve(nextLangs, keys, formatter, resolved); - }); - }; - - Context.prototype.resolveEntities = function resolveEntities(langs, keys) { - var _this3 = this; - - return this.fetch(langs).then(function (langs) { - return _this3._resolve(langs, keys, _this3._formatEntity); - }); - }; - - Context.prototype.resolveValues = function resolveValues(langs, keys) { - var _this4 = this; - - return this.fetch(langs).then(function (langs) { - return _this4._resolve(langs, keys, _this4._formatValue); - }); - }; - - Context.prototype._getEntity = function _getEntity(lang, id) { - var cache = this._env._resCache; - var resIds = Array.from(this._env._resLists.get(this)); - - for (var i = 0, resId = undefined; resId = 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._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - var formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._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 _this5 = 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 === _this5._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; - } - - 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 replaceChars = function (map, val) { - return val.replace(reAlphas, function (match) { - return map.charAt(match.charCodeAt(0) - 65); - }); - }; - - 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 parsers = { - properties: PropertiesParser, - l20n: L20nParser - }; - - var Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - 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(resIds) { - var ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - }; - - Env.prototype.destroyContext = function destroyContext(ctx) { - var _this6 = this; - - var lists = this._resLists; - var resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(function (resId) { - return deleteIfOrphan(_this6._resCache, lists, resId); - }); - }; - - Env.prototype._parse = function _parse(syntax, lang, data) { - var _this7 = this; - - var parser = parsers[syntax]; - if (!parser) { - return data; - } - - var emit = function (type, err) { - return _this7.emit(type, amendError(lang, err)); - }; - return parser.parse.call(parser, emit, 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 _this8 = 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 = _this8._parse(syntax, lang, data); - cache.set(id, _this8._create(lang, entries)); - }; - - var recover = function (err) { - err.lang = lang; - _this8.emit('fetcherror', err); - cache.set(id, err); - }; - - var langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - }; - - return Env; - })(); - - function deleteIfOrphan(cache, lists, resId) { - var isNeeded = Array.from(lists).some(function (_ref6) { - var ctx = _ref6[0]; - var resIds = _ref6[1]; - return resIds.has(resId); - }); - - if (!isNeeded) { - cache.forEach(function (val, key) { - return key.startsWith(resId) ? cache.delete(key) : null; - }); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(function (resolve) { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - function getDirection(code) { - var tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; - } - - function prioritizeLocales(def, availableLangs, requested) { - var supportedLocale = undefined; - - for (var i = 0; i < requested.length; i++) { - var locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - var availableLangs = Object.create(null); - var defaultLang = null; - var appVersion = null; - - var metas = head.querySelectorAll('meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"]'); - for (var _iterator = metas, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var meta = _ref; - - var _name = meta.getAttribute('name'); - var content = meta.getAttribute('content').trim(); - switch (_name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap(availableLangs, content); - break; - case 'defaultLanguage': - var _getLangRevisionTuple = getLangRevisionTuple(content), - lang = _getLangRevisionTuple[0], - rev = _getLangRevisionTuple[1]; - - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang: defaultLang, - availableLangs: availableLangs, - appVersion: appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce(function (seq, cur) { - var _getLangRevisionTuple2 = getLangRevisionTuple(cur); - - var lang = _getLangRevisionTuple2[0]; - var rev = _getLangRevisionTuple2[1]; - - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - var _str$trim$split = str.trim().split(':'); - - var lang = _str$trim$split[0]; - var rev = _str$trim$split[1]; - - return [lang, parseInt(rev)]; - } - - function negotiateLanguages(fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, requestedLangs) { - - var allAvailableLangs = Object.keys(availableLangs).concat(additionalLangs || []).concat(Object.keys(pseudo)); - var newLangs = prioritizeLocales(defaultLang, allAvailableLangs, requestedLangs); - - var langs = newLangs.map(function (code) { - return { - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code) - }; - }); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && arr1.every(function (elem, i) { - return elem === arr2[i]; - }); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (var i = 0, langpack = undefined; langpack = langpacks[i]; i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - var lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && (!(code in availableLangs) || parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if (code in pseudo && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - var Remote = (function () { - function Remote(fetchResource, broadcast, requestedLangs) { - var _this9 = this; - - _classCallCheck(this, Remote); - - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then(function () { - return _this9.init(requestedLangs); - }); - } - - Remote.prototype.init = function init(requestedLangs) { - var _this10 = this; - - var meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env(this.defaultLanguage, function () { - for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { - args[_key5] = arguments[_key5]; - } - - return _this10.fetchResource.apply(_this10, [_this10.appVersion].concat(args)); - }); - - return this.requestLanguages(requestedLangs); - }; - - Remote.prototype.registerView = function registerView(view, resources) { - var _this11 = this; - - return this.interactive.then(function () { - _this11.ctxs.set(view, _this11.env.createContext(resources)); - return true; - }); - }; - - Remote.prototype.unregisterView = function unregisterView(view) { - return this.ctxs.delete(view); - }; - - Remote.prototype.resolveEntities = function resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - }; - - Remote.prototype.formatValues = function formatValues(view, keys) { - var _this12 = this; - - return this.languages.then(function (langs) { - return _this12.ctxs.get(view).resolveValues(langs, keys); - }); - }; - - Remote.prototype.resolvedLanguages = function resolvedLanguages() { - return this.languages; - }; - - Remote.prototype.requestLanguages = function requestLanguages(requestedLangs) { - return changeLanguages.call(this, getAdditionalLanguages(), requestedLangs); - }; - - Remote.prototype.getName = function getName(code) { - return pseudo[code].name; - }; - - Remote.prototype.processString = function processString(code, str) { - return pseudo[code].process(str); - }; - - Remote.prototype.handleEvent = function handleEvent(evt) { - return changeLanguages.call(this, evt.detail || getAdditionalLanguages(), navigator.languages); - }; - - return Remote; - })(); - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch(function () { - return []; - }); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - var _this13 = this; - - var prevLangs = this.languages || []; - return this.languages = Promise.all([additionalLangs, prevLangs]).then(function (_ref7) { - var additionalLangs = _ref7[0]; - var prevLangs = _ref7[1]; - return negotiateLanguages(_this13.broadcast.bind(_this13, 'translateDocument'), _this13.appVersion, _this13.defaultLanguage, _this13.availableLanguages, additionalLangs, prevLangs, requestedLangs); - }); - } - - 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 getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), function (el) { - return el.getAttribute('href'); - }); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - 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 translateMutations(view, langs, mutations) { - var targets = new Set(); - - for (var _iterator2 = mutations, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - var mutation = _ref2; - - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (var _iterator3 = mutation.addedNodes, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; - - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; - } - - var addedNode = _ref3; - - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function _translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, 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._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(function (translations) { - return applyTranslations(view, elements, translations); - }); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (var i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - var observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - var readiness = new WeakMap(); - - var View = (function () { - function View(client, doc) { - var _this14 = this; - - _classCallCheck(this, View); - - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then(function () { - return init(_this14, client); - }); - - var observer = new MutationObserver(onMutations.bind(this)); - this._observe = function () { - return observer.observe(doc, observerConfig); - }; - this._disconnect = function () { - return observer.disconnect(); - }; - - var translateView = function (langs) { - return translateDocument(_this14, langs); - }; - client.on('translateDocument', translateView); - this.ready = this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(translateView); - } - - View.prototype.requestLanguages = function requestLanguages(langs, global) { - return this._interactive.then(function (client) { - return client.method('requestLanguages', langs, global); - }); - }; - - View.prototype._resolveEntities = function _resolveEntities(langs, keys) { - return this._interactive.then(function (client) { - return client.method('resolveEntities', client.id, langs, keys); - }); - }; - - View.prototype.formatValue = function formatValue(id, args) { - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, [[id, args]]); - }).then(function (values) { - return values[0]; - }); - }; - - View.prototype.formatValues = function formatValues() { - for (var _len6 = arguments.length, keys = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { - keys[_key6] = arguments[_key6]; - } - - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, keys); - }); - }; - - View.prototype.translateFragment = function translateFragment(frag) { - var _this15 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return _translateFragment(_this15, langs, frag); - }); - }; - - return View; - })(); - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: function () { - return view._interactive.then(function (client) { - return client.method('getName', code); - }); - }, - processString: function (str) { - return view._interactive.then(function (client) { - return client.method('processString', code, str); - }); - } - }; - } - - function init(view, client) { - view._observe(); - return client.method('registerView', client.id, getResourceLinks(view._doc.head)).then(function () { - return client; - }); - } - - function onMutations(mutations) { - var _this16 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return translateMutations(_this16, langs, mutations); - }); - } - - function translateDocument(view, langs) { - var html = view._doc.documentElement; - - if (readiness.has(html)) { - return _translateFragment(view, langs, html).then(function () { - return setAllAndEmit(html, langs); - }); - } - - var translated = langs[0].code === html.getAttribute('lang') ? Promise.resolve() : _translateFragment(view, langs, html).then(function () { - return setLangDir(html, langs); - }); - - return translated.then(function () { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - var codes = langs.map(function (lang) { - return lang.code; - }); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - var code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false - })); - } - - var remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - document.l10n = new View(new Client(remote), document); - - navigator.mozL10n = { - setAttributes: document.l10n.setAttributes, - getAttributes: document.l10n.getAttributes, - formatValue: function () { - var _document$l10n; - - return (_document$l10n = document.l10n).formatValue.apply(_document$l10n, arguments); - }, - translateFragment: function () { - var _document$l10n2; - - return (_document$l10n2 = document.l10n).translateFragment.apply(_document$l10n2, arguments); - }, - once: function (cb) { - return document.l10n.ready.then(cb); - }, - ready: function (cb) { - return document.l10n.ready.then(function () { - document.addEventListener('DOMRetranslated', cb); - cb(); - }); - } - }; -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/jsshell/l20n.js b/bower_components/l20n/dist/compat/jsshell/l20n.js deleted file mode 100755 index 9741f7e..0000000 --- a/bower_components/l20n/dist/compat/jsshell/l20n.js +++ /dev/null @@ -1,1361 +0,0 @@ -'use strict'; - -(function () { - 'use strict'; - - 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; - - var MAX_PLACEABLES = 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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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) { - throw this.error('Too many placeables, maximum allowed is ' + MAX_PLACEABLES); - } - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } - }; - - var MAX_PLACEABLES$1 = 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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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$1) { - throw this.error('Too many placeables (' + placeablesCount + ', max allowed is ' + MAX_PLACEABLES$1 + ')'); - } - - 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 KNOWN_MACROS = ['plural']; - var MAX_PLACEABLE_LENGTH = 2500; - - var FSI = '⁨'; - var PDI = '⁩'; - - var resolutionChain = new WeakSet(); - - 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 newLocals = undefined, - value = undefined; - - try { - var _resolveIdentifier = resolveIdentifier(ctx, lang, args, id); - - newLocals = _resolveIdentifier[0]; - value = _resolveIdentifier[1]; - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - var formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - 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 + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function (_ref, cur) { - var localsSeq = _ref[0]; - var valueSeq = _ref[1]; - - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - var _subPlaceable = subPlaceable(locals, ctx, lang, args, cur.name); - - var value = _subPlaceable[1]; - - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(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(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - return selector; - } - - var argValue = index[0].args ? resolveIdentifier(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(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(locals, ctx, lang, args, expr); - } - - if (index) { - var selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - var defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(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]; - } - - function MockContext(entries) { - this._getNumberFormatter = function () { - return { - format: function (value) { - return value; - } - }; - }; - this._getEntity = function (lang, id) { - return entries[id]; - }; - - this._getMacro = function (lang, id) { - switch (id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - }; - } - - this.L20n = { - MockContext: MockContext, - L20nParser: L20nParser, - PropertiesParser: PropertiesParser, - format: format - }; -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/node/l20n.js b/bower_components/l20n/dist/compat/node/l20n.js deleted file mode 100755 index ecf7308..0000000 --- a/bower_components/l20n/dist/compat/node/l20n.js +++ /dev/null @@ -1,1810 +0,0 @@ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -var string_prototype_startswith = require('string.prototype.startswith'); -var string_prototype_endswith = require('string.prototype.endswith'); -var fs = require('fs'); - -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 load(url) { - return new Promise(function (resolve, reject) { - fs.readFile(url, function (err, data) { - if (err) { - reject(new L10nError(err.message)); - } else { - resolve(data.toString()); - } - }); - }); -} - -function fetchResource$1(res, lang) { - var url = res.replace('{locale}', lang.code); - return res.endsWith('.json') ? load(url).then(JSON.parse) : load(url); -} - -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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } -}; - -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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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 KNOWN_MACROS = ['plural']; -var MAX_PLACEABLE_LENGTH = 2500; - -var FSI = '⁨'; -var PDI = '⁩'; - -var resolutionChain = new WeakSet(); - -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 newLocals = undefined, - value = undefined; - - try { - var _resolveIdentifier = resolveIdentifier(ctx, lang, args, id); - - newLocals = _resolveIdentifier[0]; - value = _resolveIdentifier[1]; - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - var formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - 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 + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; -} - -function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function (_ref, cur) { - var localsSeq = _ref[0]; - var valueSeq = _ref[1]; - - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - var _subPlaceable = subPlaceable(locals, ctx, lang, args, cur.name); - - var value = _subPlaceable[1]; - - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector(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(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - return selector; - } - - var argValue = index[0].args ? resolveIdentifier(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(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(locals, ctx, lang, args, expr); - } - - if (index) { - var selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - var defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(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 Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - Context.prototype._formatTuple = function _formatTuple(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]; - } - }; - - 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(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - var resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(function () { - return langs; - }); - }; - - Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) { - var _this = 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 _ref2 = Array.isArray(key) ? key : [key, undefined]; - - var id = _ref2[0]; - var args = _ref2[1]; - - var entity = _this._getEntity(lang, id); - - if (entity) { - return formatter.call(_this, lang, args, entity, id); - } - - _this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), _this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(function (nextLangs) { - return _this._resolve(nextLangs, keys, formatter, resolved); - }); - }; - - Context.prototype.resolveEntities = function resolveEntities(langs, keys) { - var _this2 = this; - - return this.fetch(langs).then(function (langs) { - return _this2._resolve(langs, keys, _this2._formatEntity); - }); - }; - - Context.prototype.resolveValues = function resolveValues(langs, keys) { - var _this3 = this; - - return this.fetch(langs).then(function (langs) { - return _this3._resolve(langs, keys, _this3._formatValue); - }); - }; - - Context.prototype._getEntity = function _getEntity(lang, id) { - var cache = this._env._resCache; - var resIds = Array.from(this._env._resLists.get(this)); - - for (var i = 0, resId = undefined; resId = 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._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - var formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._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 _this4 = 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 === _this4._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; -} - -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 replaceChars = function (map, val) { - return val.replace(reAlphas, function (match) { - return map.charAt(match.charCodeAt(0) - 65); - }); - }; - - 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') - } -}); - -function emit(listeners) { - var _this5 = this; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(function (listener) { - return listener.apply(_this5, args); - }); - } - - if (listeners[type]) { - listeners[type].slice().forEach(function (listener) { - return listener.apply(_this5, 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 parsers = { - properties: PropertiesParser, - l20n: L20nParser -}; - -var Env$1 = (function () { - function Env$1(defaultLang, fetchResource) { - _classCallCheck(this, Env$1); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - var listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - Env$1.prototype.createContext = function createContext(resIds) { - var ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - }; - - Env$1.prototype.destroyContext = function destroyContext(ctx) { - var _this6 = this; - - var lists = this._resLists; - var resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(function (resId) { - return deleteIfOrphan(_this6._resCache, lists, resId); - }); - }; - - Env$1.prototype._parse = function _parse(syntax, lang, data) { - var _this7 = this; - - var parser = parsers[syntax]; - if (!parser) { - return data; - } - - var emit = function (type, err) { - return _this7.emit(type, amendError(lang, err)); - }; - return parser.parse.call(parser, emit, data); - }; - - Env$1.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$1.prototype._getResource = function _getResource(lang, res) { - var _this8 = 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 = _this8._parse(syntax, lang, data); - cache.set(id, _this8._create(lang, entries)); - }; - - var recover = function (err) { - err.lang = lang; - _this8.emit('fetcherror', err); - cache.set(id, err); - }; - - var langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - }; - - return Env$1; -})(); - -function deleteIfOrphan(cache, lists, resId) { - var isNeeded = Array.from(lists).some(function (_ref3) { - var ctx = _ref3[0]; - var resIds = _ref3[1]; - return resIds.has(resId); - }); - - if (!isNeeded) { - cache.forEach(function (val, key) { - return key.startsWith(resId) ? cache.delete(key) : null; - }); - } -} - -function amendError(lang, err) { - err.lang = lang; - return err; -} - -exports.fetchResource = fetchResource$1; -exports.Env = Env$1; \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/testing/l20n.js b/bower_components/l20n/dist/compat/testing/l20n.js deleted file mode 100755 index a106918..0000000 --- a/bower_components/l20n/dist/compat/testing/l20n.js +++ /dev/null @@ -1,291 +0,0 @@ -'use strict'; - -(function () { - 'use strict'; - - 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 getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), function (el) { - return el.getAttribute('href'); - }); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - 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 translateMutations(view, langs, mutations) { - var targets = new Set(); - - for (var _iterator = mutations, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var mutation = _ref; - - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (var _iterator2 = mutation.addedNodes, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - var addedNode = _ref2; - - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, 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._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(function (translations) { - return applyTranslations(view, elements, translations); - }); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (var i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - var dom = { - getResourceLinks: getResourceLinks, - setAttributes: setAttributes, - getAttributes: getAttributes, - translateMutations: translateMutations, - translateFragment: translateFragment - }; - - window.L20n = { - dom: dom - }; -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/tooling/l20n.js b/bower_components/l20n/dist/compat/tooling/l20n.js deleted file mode 100755 index 6671626..0000000 --- a/bower_components/l20n/dist/compat/tooling/l20n.js +++ /dev/null @@ -1,3686 +0,0 @@ -'use strict'; - -function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - 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 load(type, url) { - return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - var io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } - }; - - function fetchResource(ver, res, lang) { - var url = res.replace('{locale}', lang.code); - var type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - function emit(listeners) { - var _this = this; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(function (listener) { - return listener.apply(_this, args); - }); - } - - if (listeners[type]) { - listeners[type].slice().forEach(function (listener) { - return listener.apply(_this, 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 Client = (function () { - function Client(remote) { - _classCallCheck(this, Client); - - this.id = this; - this.remote = remote; - - var listeners = {}; - this.on = function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return addEventListener.apply(undefined, [listeners].concat(args)); - }; - this.emit = function () { - for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - args[_key3] = arguments[_key3]; - } - - return emit.apply(undefined, [listeners].concat(args)); - }; - } - - Client.prototype.method = function method(name) { - var _remote; - - for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { - args[_key4 - 1] = arguments[_key4]; - } - - return (_remote = this.remote)[name].apply(_remote, args); - }; - - return Client; - })(); - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach(function (client) { - return client.emit(type, data); - }); - } - - 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 getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), function (el) { - return el.getAttribute('href'); - }); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - 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 translateMutations(view, langs, mutations) { - var targets = new Set(); - - for (var _iterator = mutations, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var mutation = _ref; - - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (var _iterator2 = mutation.addedNodes, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - var addedNode = _ref2; - - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function _translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, 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._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(function (translations) { - return applyTranslations(view, elements, translations); - }); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (var i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(function (resolve) { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - function getDirection(code) { - var tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; - } - - var observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - var readiness = new WeakMap(); - - var View = (function () { - function View(client, doc) { - var _this2 = this; - - _classCallCheck(this, View); - - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then(function () { - return init(_this2, client); - }); - - var observer = new MutationObserver(onMutations.bind(this)); - this._observe = function () { - return observer.observe(doc, observerConfig); - }; - this._disconnect = function () { - return observer.disconnect(); - }; - - var translateView = function (langs) { - return translateDocument(_this2, langs); - }; - client.on('translateDocument', translateView); - this.ready = this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(translateView); - } - - View.prototype.requestLanguages = function requestLanguages(langs, global) { - return this._interactive.then(function (client) { - return client.method('requestLanguages', langs, global); - }); - }; - - View.prototype._resolveEntities = function _resolveEntities(langs, keys) { - return this._interactive.then(function (client) { - return client.method('resolveEntities', client.id, langs, keys); - }); - }; - - View.prototype.formatValue = function formatValue(id, args) { - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, [[id, args]]); - }).then(function (values) { - return values[0]; - }); - }; - - View.prototype.formatValues = function formatValues() { - for (var _len5 = arguments.length, keys = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { - keys[_key5] = arguments[_key5]; - } - - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, keys); - }); - }; - - View.prototype.translateFragment = function translateFragment(frag) { - var _this3 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return _translateFragment(_this3, langs, frag); - }); - }; - - return View; - })(); - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: function () { - return view._interactive.then(function (client) { - return client.method('getName', code); - }); - }, - processString: function (str) { - return view._interactive.then(function (client) { - return client.method('processString', code, str); - }); - } - }; - } - - function init(view, client) { - view._observe(); - return client.method('registerView', client.id, getResourceLinks(view._doc.head)).then(function () { - return client; - }); - } - - function onMutations(mutations) { - var _this4 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return translateMutations(_this4, langs, mutations); - }); - } - - function translateDocument(view, langs) { - var html = view._doc.documentElement; - - if (readiness.has(html)) { - return _translateFragment(view, langs, html).then(function () { - return setAllAndEmit(html, langs); - }); - } - - var translated = langs[0].code === html.getAttribute('lang') ? Promise.resolve() : _translateFragment(view, langs, html).then(function () { - return setLangDir(html, langs); - }); - - return translated.then(function () { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - var codes = langs.map(function (lang) { - return lang.code; - }); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - var code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false - })); - } - - 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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } - }; - - var MAX_PLACEABLES$2 = 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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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$2) { - throw this.error('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({ 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 KNOWN_MACROS = ['plural']; - var MAX_PLACEABLE_LENGTH = 2500; - - var FSI = '⁨'; - var PDI = '⁩'; - - var resolutionChain = new WeakSet(); - - 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 newLocals = undefined, - value = undefined; - - try { - var _resolveIdentifier = resolveIdentifier(ctx, lang, args, id); - - newLocals = _resolveIdentifier[0]; - value = _resolveIdentifier[1]; - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - var formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - 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 + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - 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 { - var _subPlaceable = subPlaceable(locals, ctx, lang, args, cur.name); - - var value = _subPlaceable[1]; - - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(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(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - return selector; - } - - var argValue = index[0].args ? resolveIdentifier(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(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(locals, ctx, lang, args, expr); - } - - if (index) { - var selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - var defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(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 Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - Context.prototype._formatTuple = function _formatTuple(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]; - } - }; - - 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(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - var resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(function () { - return langs; - }); - }; - - Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) { - var _this5 = 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 _ref5 = Array.isArray(key) ? key : [key, undefined]; - - var id = _ref5[0]; - var args = _ref5[1]; - - var entity = _this5._getEntity(lang, id); - - if (entity) { - return formatter.call(_this5, lang, args, entity, id); - } - - _this5._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), _this5); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(function (nextLangs) { - return _this5._resolve(nextLangs, keys, formatter, resolved); - }); - }; - - Context.prototype.resolveEntities = function resolveEntities(langs, keys) { - var _this6 = this; - - return this.fetch(langs).then(function (langs) { - return _this6._resolve(langs, keys, _this6._formatEntity); - }); - }; - - Context.prototype.resolveValues = function resolveValues(langs, keys) { - var _this7 = this; - - return this.fetch(langs).then(function (langs) { - return _this7._resolve(langs, keys, _this7._formatValue); - }); - }; - - Context.prototype._getEntity = function _getEntity(lang, id) { - var cache = this._env._resCache; - var resIds = Array.from(this._env._resLists.get(this)); - - for (var i = 0, resId = undefined; resId = 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._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - var formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._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 _this8 = 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 === _this8._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; - } - - 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 replaceChars = function (map, val) { - return val.replace(reAlphas, function (match) { - return map.charAt(match.charCodeAt(0) - 65); - }); - }; - - 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 parsers = { - properties: PropertiesParser, - l20n: L20nParser - }; - - var Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - 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(resIds) { - var ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - }; - - Env.prototype.destroyContext = function destroyContext(ctx) { - var _this9 = this; - - var lists = this._resLists; - var resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(function (resId) { - return deleteIfOrphan(_this9._resCache, lists, resId); - }); - }; - - Env.prototype._parse = function _parse(syntax, lang, data) { - var _this10 = this; - - var parser = parsers[syntax]; - if (!parser) { - return data; - } - - var emit = function (type, err) { - return _this10.emit(type, amendError(lang, err)); - }; - return parser.parse.call(parser, emit, 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: this.defaultLang, src: 'app' } : lang; - - var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - }; - - return Env; - })(); - - function deleteIfOrphan(cache, lists, resId) { - var isNeeded = Array.from(lists).some(function (_ref6) { - var ctx = _ref6[0]; - var resIds = _ref6[1]; - return resIds.has(resId); - }); - - if (!isNeeded) { - cache.forEach(function (val, key) { - return key.startsWith(resId) ? cache.delete(key) : null; - }); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - function prioritizeLocales(def, availableLangs, requested) { - var supportedLocale = undefined; - - for (var i = 0; i < requested.length; i++) { - var locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - var availableLangs = Object.create(null); - var defaultLang = null; - var appVersion = null; - - var metas = head.querySelectorAll('meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"]'); - for (var _iterator3 = metas, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; - - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; - } - - var meta = _ref3; - - var _name = meta.getAttribute('name'); - var content = meta.getAttribute('content').trim(); - switch (_name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap(availableLangs, content); - break; - case 'defaultLanguage': - var _getLangRevisionTuple = getLangRevisionTuple(content), - lang = _getLangRevisionTuple[0], - rev = _getLangRevisionTuple[1]; - - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang: defaultLang, - availableLangs: availableLangs, - appVersion: appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce(function (seq, cur) { - var _getLangRevisionTuple2 = getLangRevisionTuple(cur); - - var lang = _getLangRevisionTuple2[0]; - var rev = _getLangRevisionTuple2[1]; - - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - var _str$trim$split = str.trim().split(':'); - - var lang = _str$trim$split[0]; - var rev = _str$trim$split[1]; - - return [lang, parseInt(rev)]; - } - - function negotiateLanguages(fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, requestedLangs) { - - var allAvailableLangs = Object.keys(availableLangs).concat(additionalLangs || []).concat(Object.keys(pseudo)); - var newLangs = prioritizeLocales(defaultLang, allAvailableLangs, requestedLangs); - - var langs = newLangs.map(function (code) { - return { - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code) - }; - }); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && arr1.every(function (elem, i) { - return elem === arr2[i]; - }); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (var i = 0, langpack = undefined; langpack = langpacks[i]; i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - var lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && (!(code in availableLangs) || parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if (code in pseudo && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - var Remote = (function () { - function Remote(fetchResource, broadcast, requestedLangs) { - var _this12 = this; - - _classCallCheck(this, Remote); - - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then(function () { - return _this12.init(requestedLangs); - }); - } - - Remote.prototype.init = function init(requestedLangs) { - var _this13 = this; - - var meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env(this.defaultLanguage, function () { - for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { - args[_key6] = arguments[_key6]; - } - - return _this13.fetchResource.apply(_this13, [_this13.appVersion].concat(args)); - }); - - return this.requestLanguages(requestedLangs); - }; - - Remote.prototype.registerView = function registerView(view, resources) { - var _this14 = this; - - return this.interactive.then(function () { - _this14.ctxs.set(view, _this14.env.createContext(resources)); - return true; - }); - }; - - Remote.prototype.unregisterView = function unregisterView(view) { - return this.ctxs.delete(view); - }; - - Remote.prototype.resolveEntities = function resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - }; - - Remote.prototype.formatValues = function formatValues(view, keys) { - var _this15 = this; - - return this.languages.then(function (langs) { - return _this15.ctxs.get(view).resolveValues(langs, keys); - }); - }; - - Remote.prototype.resolvedLanguages = function resolvedLanguages() { - return this.languages; - }; - - Remote.prototype.requestLanguages = function requestLanguages(requestedLangs) { - return changeLanguages.call(this, getAdditionalLanguages(), requestedLangs); - }; - - Remote.prototype.getName = function getName(code) { - return pseudo[code].name; - }; - - Remote.prototype.processString = function processString(code, str) { - return pseudo[code].process(str); - }; - - Remote.prototype.handleEvent = function handleEvent(evt) { - return changeLanguages.call(this, evt.detail || getAdditionalLanguages(), navigator.languages); - }; - - return Remote; - })(); - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch(function () { - return []; - }); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - var _this16 = this; - - var prevLangs = this.languages || []; - return this.languages = Promise.all([additionalLangs, prevLangs]).then(function (_ref7) { - var additionalLangs = _ref7[0]; - var prevLangs = _ref7[1]; - return negotiateLanguages(_this16.broadcast.bind(_this16, 'translateDocument'), _this16.appVersion, _this16.defaultLanguage, _this16.availableLanguages, additionalLangs, prevLangs, requestedLangs); - }); - } - - var Node = function Node() { - _classCallCheck(this, Node); - - this.type = this.constructor.name; - }; - - var Entry = (function (_Node) { - _inherits(Entry, _Node); - - function Entry() { - _classCallCheck(this, Entry); - - _Node.call(this); - } - - return Entry; - })(Node); - - var Identifier = (function (_Node2) { - _inherits(Identifier, _Node2); - - function Identifier(name) { - _classCallCheck(this, Identifier); - - _Node2.call(this); - this.name = name; - } - - return Identifier; - })(Node); - - var Variable = (function (_Node3) { - _inherits(Variable, _Node3); - - function Variable(name) { - _classCallCheck(this, Variable); - - _Node3.call(this); - this.name = name; - } - - return Variable; - })(Node); - - var Global = (function (_Node4) { - _inherits(Global, _Node4); - - function Global(name) { - _classCallCheck(this, Global); - - _Node4.call(this); - this.name = name; - } - - return Global; - })(Node); - - var Value = (function (_Node5) { - _inherits(Value, _Node5); - - function Value() { - _classCallCheck(this, Value); - - _Node5.call(this); - } - - return Value; - })(Node); - - var String$1 = (function (_Value) { - _inherits(String$1, _Value); - - function String$1(source, content) { - _classCallCheck(this, String$1); - - _Value.call(this); - this.source = source; - this.content = content; - - this._opchar = '"'; - } - - return String$1; - })(Value); - - var Hash = (function (_Value2) { - _inherits(Hash, _Value2); - - function Hash(items) { - _classCallCheck(this, Hash); - - _Value2.call(this); - this.items = items; - } - - return Hash; - })(Value); - - var Entity = (function (_Entry) { - _inherits(Entity, _Entry); - - function Entity(id) { - var value = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; - var index = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; - var attrs = arguments.length <= 3 || arguments[3] === undefined ? [] : arguments[3]; - - _classCallCheck(this, Entity); - - _Entry.call(this); - this.id = id; - this.value = value; - this.index = index; - this.attrs = attrs; - } - - return Entity; - })(Entry); - - var Resource = (function (_Node6) { - _inherits(Resource, _Node6); - - function Resource() { - _classCallCheck(this, Resource); - - _Node6.call(this); - this.body = []; - } - - return Resource; - })(Node); - - var Attribute = (function (_Node7) { - _inherits(Attribute, _Node7); - - function Attribute(id, value) { - var index = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; - - _classCallCheck(this, Attribute); - - _Node7.call(this); - this.id = id; - this.value = value; - this.index = index; - } - - return Attribute; - })(Node); - - var HashItem = (function (_Node8) { - _inherits(HashItem, _Node8); - - function HashItem(id, value, defItem) { - _classCallCheck(this, HashItem); - - _Node8.call(this); - this.id = id; - this.value = value; - this.default = defItem; - } - - return HashItem; - })(Node); - - var Comment = (function (_Entry2) { - _inherits(Comment, _Entry2); - - function Comment(body) { - _classCallCheck(this, Comment); - - _Entry2.call(this); - this.body = body; - } - - return Comment; - })(Entry); - - var Expression = (function (_Node9) { - _inherits(Expression, _Node9); - - function Expression() { - _classCallCheck(this, Expression); - - _Node9.call(this); - } - - return Expression; - })(Node); - - var PropertyExpression = (function (_Expression) { - _inherits(PropertyExpression, _Expression); - - function PropertyExpression(idref, exp) { - var computed = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; - - _classCallCheck(this, PropertyExpression); - - _Expression.call(this); - this.idref = idref; - this.exp = exp; - this.computed = computed; - } - - return PropertyExpression; - })(Expression); - - var CallExpression = (function (_Expression2) { - _inherits(CallExpression, _Expression2); - - function CallExpression(callee, args) { - _classCallCheck(this, CallExpression); - - _Expression2.call(this); - this.callee = callee; - this.args = args; - } - - return CallExpression; - })(Expression); - - var JunkEntry = (function (_Entry3) { - _inherits(JunkEntry, _Entry3); - - function JunkEntry(content) { - _classCallCheck(this, JunkEntry); - - _Entry3.call(this); - this.content = content; - } - - return JunkEntry; - })(Entry); - - var AST = { - Node: Node, - Identifier: Identifier, - Value: Value, - String: String$1, - Hash: Hash, - Entity: Entity, - Resource: Resource, - Attribute: Attribute, - HashItem: HashItem, - Comment: Comment, - Variable: Variable, - Global: Global, - Expression: Expression, - PropertyExpression: PropertyExpression, - CallExpression: CallExpression, - JunkEntry: JunkEntry - }; - - var MAX_PLACEABLES = 100; - - var ParseContext = (function () { - function ParseContext(string, pos) { - _classCallCheck(this, ParseContext); - - this._config = { - pos: pos - }; - this._source = string; - this._index = 0; - this._length = string.length; - this._curEntryStart = 0; - } - - ParseContext.prototype.setPosition = function setPosition(node, start, end) { - if (!this._config.pos) { - return; - } - node._pos = { start: start, end: end }; - }; - - ParseContext.prototype.getResource = function getResource() { - var resource = new AST.Resource(); - this.setPosition(resource, 0, this._length); - resource._errors = []; - - this.getWS(); - while (this._index < this._length) { - try { - resource.body.push(this.getEntry()); - } catch (e) { - if (e instanceof L10nError) { - resource._errors.push(e); - resource.body.push(this.getJunkEntry()); - } else { - throw e; - } - } - if (this._index < this._length) { - this.getWS(); - } - } - - return resource; - }; - - ParseContext.prototype.getEntry = function getEntry() { - this._curEntryStart = this._index; - - 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'); - }; - - ParseContext.prototype.getEntity = function getEntity(id, index) { - if (!this.getRequiredWS()) { - throw this.error('Expected white space'); - } - - var ch = this._source.charAt(this._index); - var value = this.getValue(ch, index === undefined); - var attrs = undefined; - - if (value === null) { - 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; - - var entity = new AST.Entity(id, value, index, attrs); - this.setPosition(entity, this._curEntryStart, this._index); - return entity; - }; - - ParseContext.prototype.getValue = function getValue() { - var ch = arguments.length <= 0 || arguments[0] === undefined ? this._source[this._index] : arguments[0]; - var optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - return null; - }; - - ParseContext.prototype.getWS = function getWS() { - var cc = this._source.charCodeAt(this._index); - - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }; - - ParseContext.prototype.getRequiredWS = function getRequiredWS() { - 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; - }; - - ParseContext.prototype.getIdentifier = function getIdentifier() { - 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); - } - - var id = new AST.Identifier(this._source.slice(start, this._index)); - this.setPosition(id, start, this._index); - return id; - }; - - ParseContext.prototype.getUnicodeChar = function getUnicodeChar() { - 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'); - } - return '\\u' + this._source.slice(this._index - 3, this._index + 1); - }; - - ParseContext.prototype.getString = function getString(opchar, opcharLen) { - var body = []; - var buf = ''; - var placeables = 0; - - this._index += opcharLen - 1; - - var start = this._index + 1; - - var closed = false; - - while (!closed) { - var ch = this._source[++this._index]; - - switch (ch) { - case '\\': - var ch2 = this._source[++this._index]; - if (ch2 === 'u') { - buf += this.getUnicodeChar(); - } else if (ch2 === opchar || ch2 === '\\') { - buf += ch2; - } else if (ch2 === '{' && this._source[this._index + 1] === '{') { - buf += '{'; - } else { - throw this.error('Illegal escape sequence'); - } - break; - case '{': - if (this._source[this._index + 1] === '{') { - if (placeables > MAX_PLACEABLES - 1) { - throw this.error('Too many placeables, maximum allowed is ' + MAX_PLACEABLES); - } - if (buf.length) { - body.push(buf); - buf = ''; - } - this._index += 2; - this.getWS(); - body.push(this.getExpression()); - this.getWS(); - if (!this._source.startsWith('}}', this._index)) { - throw this.error('Expected "}}"'); - } - this._index += 1; - placeables++; - break; - } - - default: - if (ch === opchar) { - this._index++; - closed = true; - break; - } - - buf += ch; - if (this._index + 1 >= this._length) { - throw this.error('Unclosed string literal'); - } - } - } - - if (buf.length) { - body.push(buf); - } - - var string = new AST.String(this._source.slice(start, this._index - 1), body); - this.setPosition(string, start, this._index); - string._opchar = opchar; - - return string; - }; - - ParseContext.prototype.getAttributes = function getAttributes() { - var attrs = []; - - while (true) { - var attr = this.getAttribute(); - attrs.push(attr); - var ws1 = this.getRequiredWS(); - var ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }; - - ParseContext.prototype.getAttribute = function getAttribute() { - var start = this._index; - 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 attr = new AST.Attribute(key, this.getValue(), index); - this.setPosition(attr, start, this._index); - return attr; - }; - - ParseContext.prototype.getHash = function getHash() { - var start = this._index; - var items = []; - - ++this._index; - this.getWS(); - - while (true) { - items.push(this.getHashItem()); - 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 "}"'); - } - } - - var hash = new AST.Hash(items); - this.setPosition(hash, start, this._index); - return hash; - }; - - ParseContext.prototype.getHashItem = function getHashItem() { - var start = this._index; - - 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(); - - var hashItem = new AST.HashItem(key, this.getValue(), defItem); - this.setPosition(hashItem, start, this._index); - return hashItem; - }; - - ParseContext.prototype.getComment = function getComment() { - 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; - var comment = new AST.Comment(this._source.slice(start, end)); - this.setPosition(comment, start - 2, this._index); - return comment; - }; - - ParseContext.prototype.getExpression = function getExpression() { - var start = this._index; - var exp = this.getPrimaryExpression(); - - while (true) { - var ch = this._source[this._index]; - if (ch === '.' || ch === '[') { - ++this._index; - exp = this.getPropertyExpression(exp, ch === '[', start); - } else if (ch === '(') { - ++this._index; - exp = this.getCallExpression(exp, start); - } else { - break; - } - } - - return exp; - }; - - ParseContext.prototype.getPropertyExpression = function getPropertyExpression(idref, computed, start) { - 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(); - } - - var propExpr = new AST.PropertyExpression(idref, exp, computed); - this.setPosition(propExpr, start, this._index); - return propExpr; - }; - - ParseContext.prototype.getCallExpression = function getCallExpression(callee, start) { - this.getWS(); - - var callExpr = new AST.CallExpression(callee, this.getItemList(this.getExpression, ')')); - this.setPosition(callExpr, start, this._index); - return callExpr; - }; - - ParseContext.prototype.getPrimaryExpression = function getPrimaryExpression() { - var start = this._index; - var ch = this._source[this._index]; - - switch (ch) { - case '$': - ++this._index; - var variable = new AST.Variable(this.getIdentifier()); - this.setPosition(variable, start, this._index); - return variable; - case '@': - ++this._index; - var global = new AST.Global(this.getIdentifier()); - this.setPosition(global, start, this._index); - return global; - default: - return this.getIdentifier(); - } - }; - - ParseContext.prototype.getItemList = function getItemList(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; - }; - - ParseContext.prototype.error = function error(message) { - 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); - err._pos = { start: pos, end: undefined }; - err.offset = pos - start; - err.description = message; - err.context = context; - return err; - }; - - ParseContext.prototype.getJunkEntry = function getJunkEntry() { - 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; - - var junk = new AST.JunkEntry(this._source.slice(this._curEntryStart, nextEntry)); - - this.setPosition(junk, this._curEntryStart, nextEntry); - return junk; - }; - - return ParseContext; - })(); - - var ASTParser = { - parseResource: function (string) { - var pos = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - var parseContext = new ParseContext(string, pos); - return parseContext.getResource(); - } - }; - - var ASTSerializer = { - serialize: function (ast) { - var string = ''; - for (var id in ast) { - string += this.dumpEntry(ast[id]) + '\n'; - } - return string; - }, - - serializeString: function (ast) { - var string = ''; - - if (typeof ast === 'object') { - string += this.dumpValue(ast, 0); - } else { - string += this.dumpString(ast); - } - - return string; - }, - - dumpEntry: function (entry) { - return this.dumpEntity(entry); - }, - - dumpEntity: function (entity) { - var id, - val = null, - attrs = {}; - var index = ''; - - for (var key in entity) { - switch (key) { - case '$v': - val = entity.$v; - break; - case '$x': - index = this.dumpIndex(entity.$x); - break; - case '$i': - id = this.dumpIdentifier(entity.$i); - break; - default: - attrs[key] = entity[key]; - } - } - - if (Object.keys(attrs).length === 0) { - return '<' + id + index + ' ' + this.dumpValue(val, 0) + '>'; - } else { - return '<' + id + index + ' ' + this.dumpValue(val, 0) + '\n' + this.dumpAttributes(attrs) + '>'; - } - }, - - dumpIdentifier: function (id) { - return id.replace(/-/g, '_'); - }, - - dumpValue: function (value, depth) { - if (value === null) { - return ''; - } - - if (typeof value === 'string') { - return this.dumpString(value); - } - if (Array.isArray(value)) { - return this.dumpComplexString(value); - } - if (typeof value === 'object') { - if (value.o) { - return this.dumpValue(value.v); - } - return this.dumpHash(value, depth); - } - }, - - dumpString: function (str) { - if (str) { - return '"' + str.replace(/"/g, '\\"') + '"'; - } - return ''; - }, - - dumpComplexString: function (chunks) { - var str = '"'; - for (var i = 0; i < chunks.length; i++) { - if (typeof chunks[i] === 'string') { - str += chunks[i].replace(/"/g, '\\"'); - } else { - str += '{{ ' + this.dumpExpression(chunks[i]) + ' }}'; - } - } - return str + '"'; - }, - - dumpAttributes: function (attrs) { - var str = ''; - for (var key in attrs) { - if (attrs[key].x) { - str += ' ' + key + this.dumpIndex(attrs[key].x) + ': ' + this.dumpValue(attrs[key].v, 1) + '\n'; - } else { - str += ' ' + key + ': ' + this.dumpValue(attrs[key], 1) + '\n'; - } - } - - return str; - }, - - dumpExpression: function (exp) { - switch (exp.t) { - case 'call': - return this.dumpCallExpression(exp); - case 'prop': - return this.dumpPropertyExpression(exp); - } - - return this.dumpPrimaryExpression(exp); - }, - - dumpPropertyExpression: function (exp) { - var idref = this.dumpExpression(exp.e); - var prop = undefined; - - if (exp.c) { - prop = this.dumpExpression(exp.p); - return idref + '[' + prop + ']'; - } - - prop = this.dumpIdentifier(exp.p); - return idref + '.' + prop; - }, - - dumpCallExpression: function (exp) { - var pexp = this.dumpExpression(exp.v); - - var attrs = this.dumpItemList(exp.a, this.dumpExpression.bind(this)); - pexp += '(' + attrs + ')'; - return pexp; - }, - - dumpPrimaryExpression: function (exp) { - var ret = ''; - - if (typeof exp === 'string') { - return exp; - } - - switch (exp.t) { - case 'glob': - ret += '@'; - ret += exp.v; - break; - case 'var': - ret += '$'; - ret += exp.v; - break; - case 'id': - ret += this.dumpIdentifier(exp.v); - break; - case 'idOrVar': - ret += this.dumpIdentifier(exp.v); - break; - default: - throw new L10nError('Unknown primary expression'); - } - - return ret; - }, - - dumpHash: function (hash, depth) { - var items = []; - var str; - - var defIndex; - if ('__default' in hash) { - defIndex = hash.__default; - } - - for (var key in hash) { - var _indent = ' '; - if (key.charAt(0) === '_' && key.charAt(1) === '_') { - continue; - } - - if (key === defIndex) { - _indent = ' *'; - } - str = _indent + key + ': ' + this.dumpValue(hash[key], depth + 1); - items.push(str); - } - - var indent = new Array(depth + 1).join(' '); - return '{\n' + indent + items.join(',\n' + indent) + '\n' + indent + '}'; - }, - - dumpItemList: function (itemList, cb) { - return itemList.map(cb).join(', '); - }, - - dumpIndex: function (index) { - return '[' + this.dumpItemList(index, this.dumpExpression.bind(this)) + ']'; - } - }; - - function EntriesSerializer() { - this.serialize = function (ast) { - var string = ''; - for (var id in ast) { - string += dumpEntry(ast[id]) + '\n'; - } - - return string; - }; - - function dumpEntry(entry) { - return dumpEntity(entry); - } - - function dumpEntity(entity) { - var id, - val = null, - attrs = {}; - var index = ''; - - for (var key in entity) { - switch (key) { - case '$v': - val = entity.$v; - break; - case '$x': - index = dumpIndex(entity.$x); - break; - case '$i': - id = entity.$i.replace(/-/g, '_'); - break; - default: - attrs[key] = entity[key]; - } - } - - if (Object.keys(attrs).length === 0) { - return '<' + id + index + ' ' + dumpValue(val, 0) + '>'; - } else { - return '<' + id + index + ' ' + dumpValue(val, 0) + '\n' + dumpAttributes(attrs) + '>'; - } - } - - function dumpIndex(index) { - if (index[0].v === 'plural') { - return '[ @cldr.plural($' + index[1] + ') ]'; - } - } - - function dumpValue(value, depth) { - if (value === null) { - return ''; - } - if (typeof value === 'string') { - return dumpString(value); - } - if (Array.isArray(value)) { - return dumpComplexString(value); - } - if (typeof value === 'object') { - if (value.$o) { - return dumpValue(value.$o); - } - return dumpHash(value, depth); - } - } - - function dumpString(str) { - if (str) { - return '"' + str.replace(/"/g, '\\"') + '"'; - } - return ''; - } - - function dumpComplexString(chunks) { - var str = '"'; - for (var i = 0; i < chunks.length; i++) { - if (typeof chunks[i] === 'string') { - str += chunks[i]; - } else { - str += '{{ ' + chunks[i].v.replace(/-/g, '_') + ' }}'; - } - } - return str + '"'; - } - - function dumpHash(hash, depth) { - var items = []; - var str; - - for (var key in hash) { - str = ' ' + key + ': ' + dumpValue(hash[key]); - items.push(str); - } - - var indent = depth ? ' ' : ''; - return '{\n' + indent + items.join(',\n' + indent) + '\n' + indent + '}'; - } - - function dumpAttributes(attrs) { - var str = ''; - for (var key in attrs) { - if (attrs[key].$x) { - str += ' ' + key + dumpIndex(attrs[key].$x) + ': ' + dumpValue(attrs[key].$v, 1) + '\n'; - } else { - str += ' ' + key + ': ' + dumpValue(attrs[key], 1) + '\n'; - } - } - - return str; - } - } - - var lang = { - code: 'en-US', - src: 'app' - }; - - function MockContext(entries) { - this._getNumberFormatter = function () { - return { - format: function (value) { - return value; - } - }; - }; - this._getEntity = function (lang, id) { - return entries[id]; - }; - - this._getMacro = function (lang, id) { - switch (id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - }; - } - - window.L20n = { - fetchResource: fetchResource, Client: Client, Remote: Remote, View: View, broadcast: broadcast, - ASTParser: ASTParser, ASTSerializer: ASTSerializer, EntriesParser: L20nParser, EntriesSerializer: EntriesSerializer, PropertiesParser: PropertiesParser, - Context: Context, Env: Env, L10nError: L10nError, emit: emit, addEventListener: addEventListener, removeEventListener: removeEventListener, - prioritizeLocales: prioritizeLocales, MockContext: MockContext, lang: lang, getPluralRule: getPluralRule, walkEntry: walkEntry, walkValue: walkValue, - pseudo: pseudo, format: format - }; -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/web/l20n-common.js b/bower_components/l20n/dist/compat/web/l20n-common.js deleted file mode 100755 index 533719a..0000000 --- a/bower_components/l20n/dist/compat/web/l20n-common.js +++ /dev/null @@ -1,1847 +0,0 @@ -'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 load(type, url) { - return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); -} - -var io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } -}; - -function fetchResource$1(ver, res, lang) { - var url = res.replace('{locale}', lang.code); - var type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); -} - -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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } -}; - -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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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 KNOWN_MACROS = ['plural']; -var MAX_PLACEABLE_LENGTH = 2500; - -var FSI = '⁨'; -var PDI = '⁩'; - -var resolutionChain = new WeakSet(); - -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 newLocals = undefined, - value = undefined; - - try { - var _resolveIdentifier = resolveIdentifier(ctx, lang, args, id); - - newLocals = _resolveIdentifier[0]; - value = _resolveIdentifier[1]; - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - var formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - 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 + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; -} - -function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function (_ref, cur) { - var localsSeq = _ref[0]; - var valueSeq = _ref[1]; - - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - var _subPlaceable = subPlaceable(locals, ctx, lang, args, cur.name); - - var value = _subPlaceable[1]; - - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector(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(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - return selector; - } - - var argValue = index[0].args ? resolveIdentifier(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(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(locals, ctx, lang, args, expr); - } - - if (index) { - var selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - var defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(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 Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - Context.prototype._formatTuple = function _formatTuple(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]; - } - }; - - 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(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - var resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(function () { - return langs; - }); - }; - - Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) { - var _this = 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 _ref2 = Array.isArray(key) ? key : [key, undefined]; - - var id = _ref2[0]; - var args = _ref2[1]; - - var entity = _this._getEntity(lang, id); - - if (entity) { - return formatter.call(_this, lang, args, entity, id); - } - - _this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), _this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(function (nextLangs) { - return _this._resolve(nextLangs, keys, formatter, resolved); - }); - }; - - Context.prototype.resolveEntities = function resolveEntities(langs, keys) { - var _this2 = this; - - return this.fetch(langs).then(function (langs) { - return _this2._resolve(langs, keys, _this2._formatEntity); - }); - }; - - Context.prototype.resolveValues = function resolveValues(langs, keys) { - var _this3 = this; - - return this.fetch(langs).then(function (langs) { - return _this3._resolve(langs, keys, _this3._formatValue); - }); - }; - - Context.prototype._getEntity = function _getEntity(lang, id) { - var cache = this._env._resCache; - var resIds = Array.from(this._env._resLists.get(this)); - - for (var i = 0, resId = undefined; resId = 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._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - var formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._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 _this4 = 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 === _this4._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; -} - -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 replaceChars = function (map, val) { - return val.replace(reAlphas, function (match) { - return map.charAt(match.charCodeAt(0) - 65); - }); - }; - - 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') - } -}); - -function emit(listeners) { - var _this5 = this; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(function (listener) { - return listener.apply(_this5, args); - }); - } - - if (listeners[type]) { - listeners[type].slice().forEach(function (listener) { - return listener.apply(_this5, 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 parsers = { - properties: PropertiesParser, - l20n: L20nParser -}; - -var Env$1 = (function () { - function Env$1(defaultLang, fetchResource) { - _classCallCheck(this, Env$1); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - var listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - Env$1.prototype.createContext = function createContext(resIds) { - var ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - }; - - Env$1.prototype.destroyContext = function destroyContext(ctx) { - var _this6 = this; - - var lists = this._resLists; - var resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(function (resId) { - return deleteIfOrphan(_this6._resCache, lists, resId); - }); - }; - - Env$1.prototype._parse = function _parse(syntax, lang, data) { - var _this7 = this; - - var parser = parsers[syntax]; - if (!parser) { - return data; - } - - var emit = function (type, err) { - return _this7.emit(type, amendError(lang, err)); - }; - return parser.parse.call(parser, emit, data); - }; - - Env$1.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$1.prototype._getResource = function _getResource(lang, res) { - var _this8 = 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 = _this8._parse(syntax, lang, data); - cache.set(id, _this8._create(lang, entries)); - }; - - var recover = function (err) { - err.lang = lang; - _this8.emit('fetcherror', err); - cache.set(id, err); - }; - - var langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - }; - - return Env$1; -})(); - -function deleteIfOrphan(cache, lists, resId) { - var isNeeded = Array.from(lists).some(function (_ref3) { - var ctx = _ref3[0]; - var resIds = _ref3[1]; - return resIds.has(resId); - }); - - if (!isNeeded) { - cache.forEach(function (val, key) { - return key.startsWith(resId) ? cache.delete(key) : null; - }); - } -} - -function amendError(lang, err) { - err.lang = lang; - return err; -} - -exports.fetchResource = fetchResource$1; -exports.Env = Env$1; \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/web/l20n.js b/bower_components/l20n/dist/compat/web/l20n.js deleted file mode 100755 index f63b1b2..0000000 --- a/bower_components/l20n/dist/compat/web/l20n.js +++ /dev/null @@ -1,2592 +0,0 @@ -'use strict'; - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - function emit(listeners) { - var _this = this; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - var type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(function (listener) { - return listener.apply(_this, args); - }); - } - - if (listeners[type]) { - listeners[type].slice().forEach(function (listener) { - return listener.apply(_this, 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 Client = (function () { - function Client(remote) { - _classCallCheck(this, Client); - - this.id = this; - this.remote = remote; - - var listeners = {}; - this.on = function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return addEventListener.apply(undefined, [listeners].concat(args)); - }; - this.emit = function () { - for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { - args[_key3] = arguments[_key3]; - } - - return emit.apply(undefined, [listeners].concat(args)); - }; - } - - Client.prototype.method = function method(name) { - var _remote; - - for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { - args[_key4 - 1] = arguments[_key4]; - } - - return (_remote = this.remote)[name].apply(_remote, args); - }; - - return Client; - })(); - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach(function (client) { - return client.emit(type, data); - }); - } - - 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 load(type, url) { - return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - var io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } - }; - - function fetchResource(ver, res, lang) { - var url = res.replace('{locale}', lang.code); - var type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - 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 value = this.getValue(ch, index === undefined); - 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 optional = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; - - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - 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 value = this.getValue(); - - 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 () { - 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; - } - - 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; - } - }; - - 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, key; - - 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; - 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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 KNOWN_MACROS = ['plural']; - var MAX_PLACEABLE_LENGTH = 2500; - - var FSI = '⁨'; - var PDI = '⁩'; - - var resolutionChain = new WeakSet(); - - 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 newLocals = undefined, - value = undefined; - - try { - var _resolveIdentifier = resolveIdentifier(ctx, lang, args, id); - - newLocals = _resolveIdentifier[0]; - value = _resolveIdentifier[1]; - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - var formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - 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 + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - 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 { - var _subPlaceable = subPlaceable(locals, ctx, lang, args, cur.name); - - var value = _subPlaceable[1]; - - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(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(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - return selector; - } - - var argValue = index[0].args ? resolveIdentifier(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(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(locals, ctx, lang, args, expr); - } - - if (index) { - var selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - var defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(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 Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - Context.prototype._formatTuple = function _formatTuple(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]; - } - }; - - 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(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - var resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(function () { - return langs; - }); - }; - - Context.prototype._resolve = function _resolve(langs, keys, formatter, prevResolved) { - var _this2 = 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 _ref5 = Array.isArray(key) ? key : [key, undefined]; - - var id = _ref5[0]; - var args = _ref5[1]; - - var entity = _this2._getEntity(lang, id); - - if (entity) { - return formatter.call(_this2, lang, args, entity, id); - } - - _this2._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), _this2); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(function (nextLangs) { - return _this2._resolve(nextLangs, keys, formatter, resolved); - }); - }; - - Context.prototype.resolveEntities = function resolveEntities(langs, keys) { - var _this3 = this; - - return this.fetch(langs).then(function (langs) { - return _this3._resolve(langs, keys, _this3._formatEntity); - }); - }; - - Context.prototype.resolveValues = function resolveValues(langs, keys) { - var _this4 = this; - - return this.fetch(langs).then(function (langs) { - return _this4._resolve(langs, keys, _this4._formatValue); - }); - }; - - Context.prototype._getEntity = function _getEntity(lang, id) { - var cache = this._env._resCache; - var resIds = Array.from(this._env._resLists.get(this)); - - for (var i = 0, resId = undefined; resId = 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._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - var formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._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 _this5 = 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 === _this5._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; - } - - 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 replaceChars = function (map, val) { - return val.replace(reAlphas, function (match) { - return map.charAt(match.charCodeAt(0) - 65); - }); - }; - - 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 parsers = { - properties: PropertiesParser, - l20n: L20nParser - }; - - var Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - 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(resIds) { - var ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - }; - - Env.prototype.destroyContext = function destroyContext(ctx) { - var _this6 = this; - - var lists = this._resLists; - var resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(function (resId) { - return deleteIfOrphan(_this6._resCache, lists, resId); - }); - }; - - Env.prototype._parse = function _parse(syntax, lang, data) { - var _this7 = this; - - var parser = parsers[syntax]; - if (!parser) { - return data; - } - - var emit = function (type, err) { - return _this7.emit(type, amendError(lang, err)); - }; - return parser.parse.call(parser, emit, 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 _this8 = 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 = _this8._parse(syntax, lang, data); - cache.set(id, _this8._create(lang, entries)); - }; - - var recover = function (err) { - err.lang = lang; - _this8.emit('fetcherror', err); - cache.set(id, err); - }; - - var langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - var resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - }; - - return Env; - })(); - - function deleteIfOrphan(cache, lists, resId) { - var isNeeded = Array.from(lists).some(function (_ref6) { - var ctx = _ref6[0]; - var resIds = _ref6[1]; - return resIds.has(resId); - }); - - if (!isNeeded) { - cache.forEach(function (val, key) { - return key.startsWith(resId) ? cache.delete(key) : null; - }); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(function (resolve) { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - function getDirection(code) { - var tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; - } - - function prioritizeLocales(def, availableLangs, requested) { - var supportedLocale = undefined; - - for (var i = 0; i < requested.length; i++) { - var locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - var availableLangs = Object.create(null); - var defaultLang = null; - var appVersion = null; - - var metas = head.querySelectorAll('meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"]'); - for (var _iterator = metas, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var meta = _ref; - - var _name = meta.getAttribute('name'); - var content = meta.getAttribute('content').trim(); - switch (_name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap(availableLangs, content); - break; - case 'defaultLanguage': - var _getLangRevisionTuple = getLangRevisionTuple(content), - lang = _getLangRevisionTuple[0], - rev = _getLangRevisionTuple[1]; - - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang: defaultLang, - availableLangs: availableLangs, - appVersion: appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce(function (seq, cur) { - var _getLangRevisionTuple2 = getLangRevisionTuple(cur); - - var lang = _getLangRevisionTuple2[0]; - var rev = _getLangRevisionTuple2[1]; - - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - var _str$trim$split = str.trim().split(':'); - - var lang = _str$trim$split[0]; - var rev = _str$trim$split[1]; - - return [lang, parseInt(rev)]; - } - - function negotiateLanguages(fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, requestedLangs) { - - var allAvailableLangs = Object.keys(availableLangs).concat(additionalLangs || []).concat(Object.keys(pseudo)); - var newLangs = prioritizeLocales(defaultLang, allAvailableLangs, requestedLangs); - - var langs = newLangs.map(function (code) { - return { - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code) - }; - }); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && arr1.every(function (elem, i) { - return elem === arr2[i]; - }); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (var i = 0, langpack = undefined; langpack = langpacks[i]; i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - var lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && (!(code in availableLangs) || parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if (code in pseudo && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - var Remote = (function () { - function Remote(fetchResource, broadcast, requestedLangs) { - var _this9 = this; - - _classCallCheck(this, Remote); - - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then(function () { - return _this9.init(requestedLangs); - }); - } - - Remote.prototype.init = function init(requestedLangs) { - var _this10 = this; - - var meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env(this.defaultLanguage, function () { - for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { - args[_key5] = arguments[_key5]; - } - - return _this10.fetchResource.apply(_this10, [_this10.appVersion].concat(args)); - }); - - return this.requestLanguages(requestedLangs); - }; - - Remote.prototype.registerView = function registerView(view, resources) { - var _this11 = this; - - return this.interactive.then(function () { - _this11.ctxs.set(view, _this11.env.createContext(resources)); - return true; - }); - }; - - Remote.prototype.unregisterView = function unregisterView(view) { - return this.ctxs.delete(view); - }; - - Remote.prototype.resolveEntities = function resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - }; - - Remote.prototype.formatValues = function formatValues(view, keys) { - var _this12 = this; - - return this.languages.then(function (langs) { - return _this12.ctxs.get(view).resolveValues(langs, keys); - }); - }; - - Remote.prototype.resolvedLanguages = function resolvedLanguages() { - return this.languages; - }; - - Remote.prototype.requestLanguages = function requestLanguages(requestedLangs) { - return changeLanguages.call(this, getAdditionalLanguages(), requestedLangs); - }; - - Remote.prototype.getName = function getName(code) { - return pseudo[code].name; - }; - - Remote.prototype.processString = function processString(code, str) { - return pseudo[code].process(str); - }; - - Remote.prototype.handleEvent = function handleEvent(evt) { - return changeLanguages.call(this, evt.detail || getAdditionalLanguages(), navigator.languages); - }; - - return Remote; - })(); - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch(function () { - return []; - }); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - var _this13 = this; - - var prevLangs = this.languages || []; - return this.languages = Promise.all([additionalLangs, prevLangs]).then(function (_ref7) { - var additionalLangs = _ref7[0]; - var prevLangs = _ref7[1]; - return negotiateLanguages(_this13.broadcast.bind(_this13, 'translateDocument'), _this13.appVersion, _this13.defaultLanguage, _this13.availableLanguages, additionalLangs, prevLangs, requestedLangs); - }); - } - - 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 getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), function (el) { - return el.getAttribute('href'); - }); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - 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 translateMutations(view, langs, mutations) { - var targets = new Set(); - - for (var _iterator2 = mutations, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - var mutation = _ref2; - - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (var _iterator3 = mutation.addedNodes, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; - - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; - } - - var addedNode = _ref3; - - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function _translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, 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._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(function (translations) { - return applyTranslations(view, elements, translations); - }); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (var i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - var observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - var readiness = new WeakMap(); - - var View = (function () { - function View(client, doc) { - var _this14 = this; - - _classCallCheck(this, View); - - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then(function () { - return init(_this14, client); - }); - - var observer = new MutationObserver(onMutations.bind(this)); - this._observe = function () { - return observer.observe(doc, observerConfig); - }; - this._disconnect = function () { - return observer.disconnect(); - }; - - var translateView = function (langs) { - return translateDocument(_this14, langs); - }; - client.on('translateDocument', translateView); - this.ready = this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(translateView); - } - - View.prototype.requestLanguages = function requestLanguages(langs, global) { - return this._interactive.then(function (client) { - return client.method('requestLanguages', langs, global); - }); - }; - - View.prototype._resolveEntities = function _resolveEntities(langs, keys) { - return this._interactive.then(function (client) { - return client.method('resolveEntities', client.id, langs, keys); - }); - }; - - View.prototype.formatValue = function formatValue(id, args) { - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, [[id, args]]); - }).then(function (values) { - return values[0]; - }); - }; - - View.prototype.formatValues = function formatValues() { - for (var _len6 = arguments.length, keys = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { - keys[_key6] = arguments[_key6]; - } - - return this._interactive.then(function (client) { - return client.method('formatValues', client.id, keys); - }); - }; - - View.prototype.translateFragment = function translateFragment(frag) { - var _this15 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return _translateFragment(_this15, langs, frag); - }); - }; - - return View; - })(); - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: function () { - return view._interactive.then(function (client) { - return client.method('getName', code); - }); - }, - processString: function (str) { - return view._interactive.then(function (client) { - return client.method('processString', code, str); - }); - } - }; - } - - function init(view, client) { - view._observe(); - return client.method('registerView', client.id, getResourceLinks(view._doc.head)).then(function () { - return client; - }); - } - - function onMutations(mutations) { - var _this16 = this; - - return this._interactive.then(function (client) { - return client.method('resolvedLanguages'); - }).then(function (langs) { - return translateMutations(_this16, langs, mutations); - }); - } - - function translateDocument(view, langs) { - var html = view._doc.documentElement; - - if (readiness.has(html)) { - return _translateFragment(view, langs, html).then(function () { - return setAllAndEmit(html, langs); - }); - } - - var translated = langs[0].code === html.getAttribute('lang') ? Promise.resolve() : _translateFragment(view, langs, html).then(function () { - return setLangDir(html, langs); - }); - - return translated.then(function () { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - var codes = langs.map(function (lang) { - return lang.code; - }); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - var code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false - })); - } - - var remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - document.l10n = new View(new Client(remote), document); -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/compat/web/l20n.min.js b/bower_components/l20n/dist/compat/web/l20n.min.js deleted file mode 100755 index ccd2a72..0000000 --- a/bower_components/l20n/dist/compat/web/l20n.min.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}!function(){function a(a){for(var b=this,c=arguments.length,d=Array(c>1?c-1:0),e=1;c>e;e++)d[e-1]=arguments[e];var f=d.shift();a["*"]&&a["*"].slice().forEach(function(a){return a.apply(b,d)}),a[f]&&a[f].slice().forEach(function(a){return a.apply(b,d)})}function b(a,b,c){b in a||(a[b]=[]),a[b].push(c)}function c(a,b,c){var d=a[b],e=d.indexOf(c);-1!==e&&d.splice(e,1)}function d(a,b){Array.from(this.ctxs.keys()).forEach(function(c){return c.emit(a,b)})}function e(a,b,c){this.name="L10nError",this.message=a,this.id=b,this.lang=c}function f(a,b){return new Promise(function(c,d){var f=new XMLHttpRequest;f.overrideMimeType&&f.overrideMimeType(a),f.open("GET",b,!0),"application/json"===a&&(f.responseType="json"),f.addEventListener("load",function(a){200===a.target.status||0===a.target.status?c(a.target.response||a.target.responseText):d(new e("Not found: "+b))}),f.addEventListener("error",d),f.addEventListener("timeout",d);try{f.send(null)}catch(g){if("NS_ERROR_FILE_NOT_FOUND"!==g.name)throw g;d(new e("Not found: "+b))}})}function g(a,b,c){var d=b.replace("{locale}",c.code),e=b.endsWith(".json")?"json":"text";return ea[c.src](c.code,a,d,e)}function h(a,b,c,d){if("string"==typeof d)return[{},d];if(na.has(d))throw new e("Cyclic reference detected");na.add(d);var f=void 0;try{f=m({},a,b,c,d.value,d.index)}finally{na.delete(d)}return f}function i(a,b,c,d){if(ja.indexOf(d)>-1)return[{},a._getMacro(b,d)];if(c&&c.hasOwnProperty(d)){if("string"==typeof c[d]||"number"==typeof c[d]&&!isNaN(c[d]))return[{},c[d]];throw new e("Arg must be a string or a number: "+d)}if("__proto__"===d)throw new e("Illegal id: "+d);var f=a._getEntity(b,d);if(f)return h(a,b,c,f);throw new e("Unknown reference: "+d)}function j(a,b,c,d,f){var g=void 0,h=void 0;try{var j=i(b,c,d,f);g=j[0],h=j[1]}catch(k){return[{error:k},la+"{{ "+f+" }}"+ma]}if("number"==typeof h){var l=b._getNumberFormatter(c);return[g,l.format(h)]}if("string"==typeof h){if(h.length>=ka)throw new e("Too many characters in placeable ("+h.length+", max allowed is "+ka+")");return[g,la+h+ma]}return[{},la+"{{ "+f+" }}"+ma]}function k(a,b,c,d,e){return e.reduce(function(e,f){var g=e[0],h=e[1];if("string"==typeof f)return[g,h+f];var i=j(a,b,c,d,f.name),k=i[1];return[g,h+k]},[a,""])}function l(a,b,c,d,e){var f=void 0;f="call"===e[0].type&&"prop"===e[0].expr.type&&"cldr"===e[0].expr.expr.name?"plural":e[0].name;var g=i(a,b,c,f)[1];if("function"!=typeof g)return g;var h=e[0].args?i(a,b,c,e[0].args[0].name)[1]:void 0;if("plural"===f){if(0===h&&"zero"in d)return"zero";if(1===h&&"one"in d)return"one";if(2===h&&"two"in d)return"two"}return g(h)}function m(a,b,c,d,f,g){if(!f)return[a,f];if("string"==typeof f||"boolean"==typeof f||"number"==typeof f)return[a,f];if(Array.isArray(f))return k(a,b,c,d,f);if(g){var h=l(b,c,d,f,g);if(h in f)return m(a,b,c,d,f[h])}var i=f.__default||"other";if(i in f)return m(a,b,c,d,f[i]);throw new e("Unresolvable value")}function n(a,b){return-1!==b.indexOf(a)}function o(a,b,c){return typeof a==typeof b&&a>=b&&c>=a}function p(a){var b=oa[a.replace(/-.*$/,"")];return b in pa?pa[b]:function(){return"other"}}function q(a,b,c){var d=this,f=new Set;return a.forEach(function(a,e){if(!c||void 0===c[e]){var g=Array.isArray(a)?a[0]:a;f.add(g),c[e]=b===d._formatValue?g:{value:g,attrs:null}}}),this._env.emit("notfounderror",new e('"'+Array.from(f).join(", ")+'" not found in any language',f),this),c}function r(a,b){if("string"==typeof a)return b(a);var c=Object.create(null);if(a.value&&(c.value=s(a.value,b)),a.index&&(c.index=a.index),a.attrs){c.attrs=Object.create(null);for(var d in a.attrs)c.attrs[d]=r(a.attrs[d],b)}return c}function s(a,b){if("string"==typeof a)return b(a);if(a.type)return a;for(var c=Array.isArray(a)?[]:Object.create(null),d=Object.keys(a),e=0,f=void 0;f=d[e];e++)c[f]=s(a[f],b);return c}function t(a,b){var c=null;return function(){if(c)return c;var d=/[a-zA-Z]/g,e=/[aeiouAEIOU]/g,f=/[^\W0-9_]+/g,g=/(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/,h={"fr-x-psaccent":"ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ","ar-x-psbidi":"∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz"},i={"fr-x-psaccent":function(a){return a.replace(e,function(a){return a+a.toLowerCase()})},"ar-x-psbidi":function(a){return a.replace(f,function(a){return"‮"+a+"‬"})}},j=function(a,b){return b.replace(d,function(b){return a.charAt(b.charCodeAt(0)-65)})},k=function(b){return j(h[a],i[a](b))},l=function(a,b){if(!b)return b;var c=b.split(g),d=c.map(function(b){return g.test(b)?b:a(b)});return d.join("")};return c={name:k(b),process:function(a){return l(k,a)}}}}function u(a,b,c){var d=Array.from(b).some(function(a){var b=(a[0],a[1]);return b.has(c)});d||a.forEach(function(b,d){return d.startsWith(c)?a.delete(d):null})}function v(a,b){return b.lang=a,b}function w(){return"loading"!==document.readyState?Promise.resolve():new Promise(function(a){document.addEventListener("readystatechange",function b(){document.removeEventListener("readystatechange",b),a()})})}function x(a){var b=a.split("-")[0];return["ar","he","fa","ps","ur"].indexOf(b)>=0?"rtl":"ltr"}function y(a,b,c){for(var d=void 0,e=0;e=f.length)break;i=f[h++]}else{if(h=f.next(),h.done)break;i=h.value}var j=i,k=j.getAttribute("name"),l=j.getAttribute("content").trim();switch(k){case"availableLanguages":b=A(b,l);break;case"defaultLanguage":var m=B(l),n=m[0],o=m[1];c=n,n in b||(b[n]=o);break;case"appVersion":d=l}}return{defaultLang:c,availableLangs:b,appVersion:d}}function A(a,b){return b.split(",").reduce(function(a,b){var c=B(b),d=c[0],e=c[1];return a[d]=e,a},a)}function B(a){var b=a.trim().split(":"),c=b[0],d=b[1];return[c,parseInt(d)]}function C(a,b,c,d,e,f,g){var h=Object.keys(d).concat(e||[]).concat(Object.keys(ra)),i=y(c,h,g),j=i.map(function(a){return{code:a,src:F(b,d,e,a)}});return D(f,i)||a(j),j}function D(a,b){return a.length===b.length&&a.every(function(a,c){return a===b[c]})}function E(a,b){for(var c=0,d=void 0;d=b[c];c++)if(d.target===a)return d;return null}function F(a,b,c,d){if(c&&c[d]){var e=E(a,c[d]);if(e&&(!(d in b)||parseInt(e.revision)>b[d]))return"extra"}return d in ra&&!(d in b)?"pseudo":"app"}function G(){return navigator.mozApps&&navigator.mozApps.getAdditionalLanguages?navigator.mozApps.getAdditionalLanguages().catch(function(){return[]}):Promise.resolve([])}function H(a,b){var c=this,d=this.languages||[];return this.languages=Promise.all([a,d]).then(function(a){var d=a[0],e=a[1];return C(c.broadcast.bind(c,"translateDocument"),c.appVersion,c.defaultLanguage,c.availableLanguages,d,e,b)})}function I(a,b){var c=b.value;if("string"==typeof c)if(va.test(c)){var d=a.ownerDocument.createElement("template");d.innerHTML=c,J(a,d.content)}else a.textContent=c;for(var e in b.attrs){var f=O(e);L({name:f},a)&&a.setAttribute(f,b.attrs[e])}}function J(a,b){for(var c=b.ownerDocument.createDocumentFragment(),d=void 0,e=void 0,f=void 0;f=b.childNodes[0];)if(b.removeChild(f),f.nodeType!==f.TEXT_NODE){var g=N(f),h=M(a,f,g);if(h)J(h,f),c.appendChild(h);else if(K(f)){var i=f.ownerDocument.createElement(f.nodeName);J(i,f),c.appendChild(i)}else c.appendChild(b.ownerDocument.createTextNode(f.textContent))}else c.appendChild(f);if(a.textContent="",a.appendChild(c),b.attributes)for(d=0,e;e=b.attributes[d];d++)L(e,a)&&a.setAttribute(e.name,e.value)}function K(a){return-1!==wa.elements.indexOf(a.tagName.toLowerCase())}function L(a,b){var c=a.name.toLowerCase(),d=b.tagName.toLowerCase();if(-1!==wa.attributes.global.indexOf(c))return!0;if(!wa.attributes[d])return!1;if(-1!==wa.attributes[d].indexOf(c))return!0;if("input"===d&&"value"===c){var e=b.type.toLowerCase();if("submit"===e||"button"===e||"reset"===e)return!0}return!1}function M(a,b,c){for(var d=0,e=0,f=void 0;f=a.children[e];e++)if(f.nodeType===f.ELEMENT_NODE&&f.tagName===b.tagName){if(d===c)return f;d++}return null}function N(a){for(var b=0,c=void 0;c=a.previousElementSibling;)c.tagName===a.tagName&&b++;return b}function O(a){return"ariaValueText"===a?"aria-valuetext":a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()}).replace(/^-/,"")}function P(a){return Array.prototype.map.call(a.querySelectorAll('link[rel="localization"]'),function(a){return a.getAttribute("href")})}function Q(a,b,c){a.setAttribute("data-l10n-id",b),c&&a.setAttribute("data-l10n-args",JSON.stringify(c))}function R(a){return{id:a.getAttribute("data-l10n-id"),args:JSON.parse(a.getAttribute("data-l10n-args"))}}function S(a){var b=Array.from(a.querySelectorAll("[data-l10n-id]"));return"function"==typeof a.hasAttribute&&a.hasAttribute("data-l10n-id")&&b.push(a),b}function T(a,b,c){for(var d=new Set,e=c,f=Array.isArray(e),g=0,e=f?e:e[Symbol.iterator]();;){var h;if(f){if(g>=e.length)break;h=e[g++]}else{if(g=e.next(),g.done)break;h=g.value}var i=h;switch(i.type){case"attributes":d.add(i.target);break;case"childList":for(var j=i.addedNodes,k=Array.isArray(j),l=0,j=k?j:j[Symbol.iterator]();;){var m;if(k){if(l>=j.length)break;m=j[l++]}else{if(l=j.next(),l.done)break;m=l.value}var n=m;n.nodeType===n.ELEMENT_NODE&&(n.childElementCount?S(n).forEach(d.add.bind(d)):n.hasAttribute("data-l10n-id")&&d.add(n))}}}0!==d.size&&W(a,b,Array.from(d))}function U(a,b,c){return W(a,b,S(c))}function V(a,b,c){var d=c.map(function(a){var b=a.getAttribute("data-l10n-id"),c=a.getAttribute("data-l10n-args");return c?[b,JSON.parse(c.replace(xa,function(a){return ya[a]}))]:b});return a._resolveEntities(b,d)}function W(a,b,c){return V(a,b,c).then(function(b){return X(a,c,b)})}function X(a,b,c){a._disconnect();for(var d=0;dd;d++)c[d]=arguments[d];return b.apply(void 0,[e].concat(c))},this.emit=function(){for(var b=arguments.length,c=Array(b),d=0;b>d;d++)c[d]=arguments[d];return a.apply(void 0,[e].concat(c))}}return c.prototype.method=function(a){for(var b,c=arguments.length,d=Array(c>1?c-1:0),e=1;c>e;e++)d[e-1]=arguments[e];return(b=this.remote)[a].apply(b,d)},c}();e.prototype=Object.create(Error.prototype),e.prototype.constructor=e;var ea={extra:function(a,b,c,d){return navigator.mozApps.getLocalizationResource(a,b,c,d)},app:function(a,b,c,d){switch(d){case"text":return f("text/plain",c);case"json":return f("application/json",c);default:throw new e("Unknown file type: "+d)}}},fa=100,ga={parse:function(a,b){return this._source=b,this._index=0,this._length=b.length,this.entries=Object.create(null),this.emit=a,this.getResource()},getResource:function(){for(this.getWS();this._index"===c)throw this.error('Expected ">"');e=this.getAttributes()}else{var f=this.getRequiredWS();if(">"!==this._source[this._index]){if(!f)throw this.error('Expected ">"');e=this.getAttributes()}}if(++this._index,a in this.entries)throw this.error('Duplicate entry ID "'+a,"duplicateerror");e||b||"string"!=typeof d?this.entries[a]={value:d,attrs:e,index:b}:this.entries[a]=d},getValue:function(){var a=arguments.length<=0||void 0===arguments[0]?this._source[this._index]:arguments[0],b=arguments.length<=1||void 0===arguments[1]?!1:arguments[1];switch(a){case"'":case'"':return this.getString(a,1);case"{":return this.getHash()}if(!b)throw this.error("Unknown value type")},getWS:function(){for(var a=this._source.charCodeAt(this._index);32===a||10===a||9===a||13===a;)a=this._source.charCodeAt(++this._index)},getRequiredWS:function(){for(var a=this._index,b=this._source.charCodeAt(a);32===b||10===b||9===b||13===b;)b=this._source.charCodeAt(++this._index);return this._index!==a},getIdentifier:function(){var a=this._index,b=this._source.charCodeAt(this._index);if(!(b>=97&&122>=b||b>=65&&90>=b||95===b))throw this.error("Identifier has to start with [a-zA-Z_]");for(b=this._source.charCodeAt(++this._index);b>=97&&122>=b||b>=65&&90>=b||b>=48&&57>=b||95===b;)b=this._source.charCodeAt(++this._index);return this._source.slice(a,this._index)},getUnicodeChar:function(){for(var a=0;4>a;a++){var b=this._source.charCodeAt(++this._index);if(!(b>96&&103>b||b>64&&71>b||b>47&&58>b))throw this.error("Illegal unicode escape sequence")}return this._index++,String.fromCharCode(parseInt(this._source.slice(this._index-4,this._index),16))},stringRe:/"|'|{{|\\/g,getString:function(a,b){var c=[],d=0;this._index+=b;for(var e=this._index,f=e,g="";;){this.stringRe.lastIndex=this._index;var h=this.stringRe.exec(this._source);if(!h)throw this.error("Unclosed string literal");if('"'===h[0]||"'"===h[0]){if(h[0]!==a){this._index+=b;continue}this._index=h.index+b;break}if("{{"!==h[0]){if("\\"===h[0]){this._index=h.index+1;var i=this._source[this._index];if("u"===i)g+=this._source.slice(f,h.index)+this.getUnicodeChar();else if(i===a||"\\"===i)g+=this._source.slice(f,h.index)+i,this._index++;else{if(!this._source.startsWith("{{",this._index))throw this.error("Illegal escape sequence");g+=this._source.slice(f,h.index)+"{{",this._index+=2}f=this._index}}else{if(d>fa-1)throw this.error("Too many placeables, maximum allowed is "+fa);d++,(h.index>f||g.length>0)&&(c.push(g+this._source.slice(f,h.index)),g=""),this._index=h.index+2,this.getWS(),c.push(this.getExpression()),this.getWS(),this._index+=2,f=this._index}}return 0===c.length?g+this._source.slice(f,this._index-b):((this._index-b>f||g.length>0)&&c.push(g+this._source.slice(f,this._index-b)),c)},getAttributes:function(){for(var a=Object.create(null);;){this.getAttribute(a);var b=this.getRequiredWS(),c=this._source.charAt(this._index);if(">"===c)break;if(!b)throw this.error('Expected ">"')}return a},getAttribute:function(a){var b=this.getIdentifier(),c=void 0;if("["===this._source[this._index]&&(++this._index,this.getWS(),c=this.getItemList(this.getExpression,"]")),this.getWS(),":"!==this._source[this._index])throw this.error('Expected ":"');++this._index,this.getWS();var d=this.getValue();if(b in a)throw this.error('Duplicate attribute "'+b,"duplicateerror");c||"string"!=typeof d?a[b]={value:d,index:c}:a[b]=d},getHash:function(){var a=Object.create(null);++this._index,this.getWS();for(var b=void 0;;){var c=this.getHashItem(),d=c[0],e=c[1],f=c[2];if(a[d]=e,f){if(b)throw this.error("Default item redefinition forbidden");b=d}this.getWS();var g=","===this._source[this._index];if(g&&(++this._index,this.getWS()),"}"===this._source[this._index]){++this._index;break}if(!g)throw this.error('Expected "}"')}return b&&(a.__default=b),a},getHashItem:function(){var a=!1;"*"===this._source[this._index]&&(++this._index,a=!0);var b=this.getIdentifier();if(this.getWS(),":"!==this._source[this._index])throw this.error('Expected ":"');return++this._index,this.getWS(),[b,this.getValue(),a]},getComment:function(){this._index+=2;var a=this._index,b=this._source.indexOf("*/",a);if(-1===b)throw this.error("Comment without a closing tag");this._index=b+2},getExpression:function(){for(var a=this.getPrimaryExpression();;){var b=this._source[this._index];if("."===b||"["===b)++this._index,a=this.getPropertyExpression(a,"["===b);else{if("("!==b)break;++this._index,a=this.getCallExpression(a)}}return a},getPropertyExpression:function(a,b){var c=void 0;if(b){if(this.getWS(),c=this.getExpression(),this.getWS(),"]"!==this._source[this._index])throw this.error('Expected "]"');++this._index}else c=this.getIdentifier();return{type:"prop",expr:a,prop:c,cmpt:b}},getCallExpression:function(a){return this.getWS(),{type:"call",expr:a,args:this.getItemList(this.getExpression,")")}},getPrimaryExpression:function(){var a=this._source[this._index];switch(a){case"$":return++this._index,{type:"var",name:this.getIdentifier()};case"@":return++this._index,{type:"glob",name:this.getIdentifier()};default:return{type:"id",name:this.getIdentifier()}}},getItemList:function(a,b){var c=[],d=!1;for(this.getWS(),this._source[this._index]===b&&(++this._index,d=!0);!d;){c.push(a.call(this)),this.getWS();var e=this._source.charAt(this._index);switch(e){case",":++this._index,this.getWS();break;case b:++this._index,d=!0;break;default:throw this.error('Expected "," or "'+b+'"')}}return c},getJunkEntry:function(){var a=this._index,b=this._source.indexOf("<",a),c=this._source.indexOf("/*",a);-1===b&&(b=this._length),-1===c&&(c=this._length);var d=Math.min(b,c);this._index=d},error:function(a){var b=arguments.length<=1||void 0===arguments[1]?"parsererror":arguments[1],c=this._index,d=this._source.lastIndexOf("<",c-1),f=this._source.lastIndexOf(">",c-1);d=f>d?f+1:d;var g=this._source.slice(d,c+10),h=a+" at pos "+c+": `"+g+"`",i=new e(h);return this.emit&&this.emit(b,i),i}},ha=100,ia={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(a,b){this.patterns||this.init(),this.emit=a;var c={},d=b.match(this.patterns.entries);if(!d)return c;for(var e=0;e2)throw this.error('Error in ID: "'+d+'". Nested attributes are not supported.');var h;if(g.length>1){if(d=g[0],h=g[1],"$"===h[0])throw this.error('Attribute can\'t start with "$"')}else h=null;this.setEntityValue(d,h,e,this.unescapeString(b),c)},setEntityValue:function(a,b,c,d,e){var f=d.indexOf("{{")>-1?this.parseString(d):d,g="string"==typeof f,h=e,i="string"==typeof e[a];if(e[a]||!b&&!c&&g||(e[a]=Object.create(null),i=!1),b){if(i){var j=e[a];e[a]=Object.create(null),e[a].value=j}e[a].attrs||(e[a].attrs=Object.create(null)),e[a].attrs||g||(e[a].attrs[b]=Object.create(null)),h=e[a].attrs,a=b}if(c){if(i=!1,"string"==typeof h[a]){var j=h[a];h[a]=Object.create(null),h[a].index=this.parseIndex(j),h[a].value=Object.create(null)}h=h[a].value,a=c,g=!0}if(!g||e[a]&&!i)h[a]||(h[a]=Object.create(null)),h[a].value=f;else{if(a in h)throw this.error();h[a]=f}},parseString:function(a){var b=a.split(this.patterns.placeables),c=[],d=b.length,e=(d-1)/2;if(e>=ha)throw this.error("Too many placeables ("+e+", max allowed is "+ha+")");for(var f=0;fd;d++)c[d]=arguments[d];return b.fetchResource.apply(b,[b.appVersion].concat(c))}),this.requestLanguages(a)},a.prototype.registerView=function(a,b){var c=this;return this.interactive.then(function(){return c.ctxs.set(a,c.env.createContext(b)),!0})},a.prototype.unregisterView=function(a){return this.ctxs.delete(a)},a.prototype.resolveEntities=function(a,b,c){return this.ctxs.get(a).resolveEntities(b,c)},a.prototype.formatValues=function(a,b){var c=this;return this.languages.then(function(d){return c.ctxs.get(a).resolveValues(d,b)})},a.prototype.resolvedLanguages=function(){return this.languages},a.prototype.requestLanguages=function(a){return H.call(this,G(),a)},a.prototype.getName=function(a){return ra[a].name},a.prototype.processString=function(a,b){return ra[a].process(b)},a.prototype.handleEvent=function(a){return H.call(this,a.detail||G(),navigator.languages)},a}(),va=/<|&#?\w+;/,wa={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"]}},xa=/[&<>]/g,ya={"&":"&","<":"<",">":">"},za={attributes:!0,characterData:!1,childList:!0,subtree:!0,attributeFilter:["data-l10n-id","data-l10n-args"]},Aa=new WeakMap,Ba=function(){function a(b,c){var d=this;_classCallCheck(this,a),this._doc=c,this.pseudo={"fr-x-psaccent":Y(this,"fr-x-psaccent"),"ar-x-psbidi":Y(this,"ar-x-psbidi")},this._interactive=w().then(function(){return Z(d,b)});var e=new MutationObserver($.bind(this));this._observe=function(){return e.observe(c,za)},this._disconnect=function(){return e.disconnect()};var f=function(a){return _(d,a)};b.on("translateDocument",f),this.ready=this._interactive.then(function(a){return a.method("resolvedLanguages")}).then(f)}return a.prototype.requestLanguages=function(a,b){return this._interactive.then(function(c){return c.method("requestLanguages",a,b)})},a.prototype._resolveEntities=function(a,b){return this._interactive.then(function(c){return c.method("resolveEntities",c.id,a,b)})},a.prototype.formatValue=function(a,b){return this._interactive.then(function(c){return c.method("formatValues",c.id,[[a,b]])}).then(function(a){return a[0]})},a.prototype.formatValues=function(){for(var a=arguments.length,b=Array(a),c=0;a>c;c++)b[c]=arguments[c];return this._interactive.then(function(a){return a.method("formatValues",a.id,b)})},a.prototype.translateFragment=function(a){var b=this;return this._interactive.then(function(a){return a.method("resolvedLanguages")}).then(function(c){return U(b,c,a)})},a}();Ba.prototype.setAttributes=Q,Ba.prototype.getAttributes=R;var Ca=new ua(g,d,navigator.languages);window.addEventListener("languagechange",Ca),document.addEventListener("additionallanguageschange",Ca),document.l10n=new Ba(new da(Ca),document)}(); \ No newline at end of file diff --git a/bower_components/l20n/dist/gecko/bridge/l20n-client.js b/bower_components/l20n/dist/gecko/bridge/l20n-client.js deleted file mode 100755 index 55c90e0..0000000 --- a/bower_components/l20n/dist/gecko/bridge/l20n-client.js +++ /dev/null @@ -1,456 +0,0 @@ -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - /* global bridge, BroadcastChannel */ - - const Client = bridge.client; - const channel = new BroadcastChannel('l20n-channel'); - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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'], - // value is special-cased in isAttrAllowed - input: ['alt', 'placeholder'], - menuitem: ['label'], - menu: ['label'], - optgroup: ['label'], - option: ['label'], - track: ['label'], - img: ['alt'], - textarea: ['placeholder'], - th: ['abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while (childElement = translationElement.childNodes[0]) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement(childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild(translationElement.ownerDocument.createTextNode(childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; attr = translationElement.attributes[k]; k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while (child = element.previousElementSibling) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string.replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }).replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>' - }; - - function getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function _translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [id, JSON.parse(args.replace(reHtml, match => htmlEntities[match]))] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - // Intl.Locale - function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; - } - - const observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - const readiness = new WeakMap(); - - let View = (function () { - function View(client, doc) { - _classCallCheck(this, View); - - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then(() => init(this, client)); - - const observer = new MutationObserver(onMutations.bind(this)); - this._observe = () => observer.observe(doc, observerConfig); - this._disconnect = () => observer.disconnect(); - - const translateView = langs => translateDocument(this, langs); - client.on('translateDocument', translateView); - this.ready = this._interactive.then(client => client.method('resolvedLanguages')).then(translateView); - } - - _createClass(View, [{ - key: 'requestLanguages', - value: function requestLanguages(langs, global) { - return this._interactive.then(client => client.method('requestLanguages', langs, global)); - } - }, { - key: '_resolveEntities', - value: function _resolveEntities(langs, keys) { - return this._interactive.then(client => client.method('resolveEntities', client.id, langs, keys)); - } - }, { - key: 'formatValue', - value: function formatValue(id, args) { - return this._interactive.then(client => client.method('formatValues', client.id, [[id, args]])).then(values => values[0]); - } - }, { - key: 'formatValues', - value: function formatValues(...keys) { - return this._interactive.then(client => client.method('formatValues', client.id, keys)); - } - }, { - key: 'translateFragment', - value: function translateFragment(frag) { - return this._interactive.then(client => client.method('resolvedLanguages')).then(langs => _translateFragment(this, langs, frag)); - } - }]); - - return View; - })(); - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: () => view._interactive.then(client => client.method('getName', code)), - processString: str => view._interactive.then(client => client.method('processString', code, str)) - }; - } - - function init(view, client) { - view._observe(); - return client.method('registerView', client.id, getResourceLinks(view._doc.head)).then(() => client); - } - - function onMutations(mutations) { - return this._interactive.then(client => client.method('resolvedLanguages')).then(langs => translateMutations(this, langs, mutations)); - } - - function translateDocument(view, langs) { - const html = view._doc.documentElement; - - if (readiness.has(html)) { - return _translateFragment(view, langs, html).then(() => setAllAndEmit(html, langs)); - } - - const translated = - // has the document been already pre-translated? - langs[0].code === html.getAttribute('lang') ? Promise.resolve() : _translateFragment(view, langs, html).then(() => setLangDir(html, langs)); - - return translated.then(() => { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - const codes = langs.map(lang => lang.code); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - const code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false - })); - } - - const client = new Client({ - service: 'l20n', - endpoint: channel, - timeout: false - }); - - window.addEventListener('pageshow', () => client.connect()); - window.addEventListener('pagehide', () => client.disconnect()); - - document.l10n = new View(client, document); - - //Bug 1204660 - Temporary proxy for shared code. Will be removed once - // l10n.js migration is completed. - navigator.mozL10n = { - setAttributes: document.l10n.setAttributes, - getAttributes: document.l10n.getAttributes, - formatValue: (...args) => document.l10n.formatValue(...args), - translateFragment: (...args) => document.l10n.translateFragment(...args), - once: cb => document.l10n.ready.then(cb), - ready: cb => document.l10n.ready.then(() => { - document.addEventListener('DOMRetranslated', cb); - cb(); - }) - }; -})(); \ No newline at end of file diff --git a/bower_components/l20n/dist/gecko/bridge/l20n-service.js b/bower_components/l20n/dist/gecko/bridge/l20n-service.js deleted file mode 100755 index 484ce9e..0000000 --- a/bower_components/l20n/dist/gecko/bridge/l20n-service.js +++ /dev/null @@ -1,2090 +0,0 @@ -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - const Service = bridge.service; - const channel = new BroadcastChannel('l20n-channel'); - - function broadcast(type, data) { - return this.service.broadcast(type, data); - } - - 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 load(type, url) { - return new Promise(function (resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - const io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } - }; - - function fetchResource(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function (ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function () { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function () { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function () { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if (cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90 || // A-Z - 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 || // 0-9 - cc === 95) { - // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function () { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if (cc > 96 && cc < 103 || cc > 64 && cc < 71 || cc > 47 && cc < 58) { - // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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 () { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function (attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function () { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function () { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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 () { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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 () { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function (message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '⁨'; - const PDI = '⁩'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + value.length + ', max allowed is ' + MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function ([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function () { - return 'other'; - }; - } - return pluralRules[index]; - } - - let Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - _createClass(Context, [{ - key: '_formatTuple', - value: function _formatTuple(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]; - } - } - }, { - key: '_formatEntity', - value: function _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple(lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - }, { - key: '_formatValue', - value: function _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - }, { - key: 'fetch', - value: function fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(() => langs); - } - }, { - key: '_resolve', - value: function _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatEntity)); - } - }, { - key: 'resolveValues', - value: function resolveValues(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatValue)); - } - }, { - key: '_getEntity', - value: function _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - }, { - key: '_getNumberFormatter', - value: function _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - }, { - key: '_getMacro', - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - value: function _getMacro(lang, id) { - switch (id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - }]); - - return Context; - })(); - - function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; - } - - // Walk an entry node searching for content leaves - function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; key = keys[i]; i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; - } - - /* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - - function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz' - }; - - const mods = { - 'fr-x-psaccent': val => val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => val.replace(reWords, match => '‮' + match + '‬') - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = (map, val) => val.replace(reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function (part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; - } - - const 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') - } - }); - - function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach(listener => listener.apply(this, args)); - } - } - - function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); - } - - function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); - } - - const parsers = { - properties: PropertiesParser, - l20n: L20nParser - }; - - let Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - _createClass(Env, [{ - key: 'createContext', - value: function createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - }, { - key: 'destroyContext', - value: function destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(resId => deleteIfOrphan(this._resCache, lists, resId)); - } - }, { - key: '_parse', - value: function _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - }, { - key: '_create', - value: function _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry(entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - }, { - key: '_getResource', - value: function _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - const resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - }]); - - return Env; - })(); - - function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some(([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => key.startsWith(resId) ? cache.delete(key) : null); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - function prioritizeLocales(def, availableLangs, requested) { - let supportedLocale; - // Find the first locale in the requested list that is supported. - for (let i = 0; i < requested.length; i++) { - const locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - let availableLangs = Object.create(null); - let defaultLang = null; - let appVersion = null; - - // XXX take last found instead of first? - const metas = head.querySelectorAll('meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"]'); - for (let meta of metas) { - const name = meta.getAttribute('name'); - const content = meta.getAttribute('content').trim(); - switch (name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap(availableLangs, content); - break; - case 'defaultLanguage': - const [lang, rev] = getLangRevisionTuple(content); - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang, - availableLangs, - appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce((seq, cur) => { - const [lang, rev] = getLangRevisionTuple(cur); - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - const [lang, rev] = str.trim().split(':'); - // if revision is missing, use NaN - return [lang, parseInt(rev)]; - } - - function negotiateLanguages(fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, requestedLangs) { - - const allAvailableLangs = Object.keys(availableLangs).concat(additionalLangs || []).concat(Object.keys(pseudo)); - const newLangs = prioritizeLocales(defaultLang, allAvailableLangs, requestedLangs); - - const langs = newLangs.map(code => ({ - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code) - })); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && arr1.every((elem, i) => elem === arr2[i]); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (let i = 0, langpack; langpack = langpacks[i]; i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - const lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && (!(code in availableLangs) || parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if (code in pseudo && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - let Remote = (function () { - function Remote(fetchResource, broadcast, requestedLangs) { - _classCallCheck(this, Remote); - - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then(() => this.init(requestedLangs)); - } - - _createClass(Remote, [{ - key: 'init', - value: function init(requestedLangs) { - const meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env(this.defaultLanguage, (...args) => this.fetchResource(this.appVersion, ...args)); - - return this.requestLanguages(requestedLangs); - } - }, { - key: 'registerView', - value: function registerView(view, resources) { - return this.interactive.then(() => { - this.ctxs.set(view, this.env.createContext(resources)); - return true; - }); - } - }, { - key: 'unregisterView', - value: function unregisterView(view) { - return this.ctxs.delete(view); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - } - }, { - key: 'formatValues', - value: function formatValues(view, keys) { - return this.languages.then(langs => this.ctxs.get(view).resolveValues(langs, keys)); - } - }, { - key: 'resolvedLanguages', - value: function resolvedLanguages() { - return this.languages; - } - }, { - key: 'requestLanguages', - value: function requestLanguages(requestedLangs) { - return changeLanguages.call(this, getAdditionalLanguages(), requestedLangs); - } - }, { - key: 'getName', - value: function getName(code) { - return pseudo[code].name; - } - }, { - key: 'processString', - value: function processString(code, str) { - return pseudo[code].process(str); - } - }, { - key: 'handleEvent', - value: function handleEvent(evt) { - return changeLanguages.call(this, evt.detail || getAdditionalLanguages(), navigator.languages); - } - }]); - - return Remote; - })(); - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch(() => []); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - const prevLangs = this.languages || []; - return this.languages = Promise.all([additionalLangs, prevLangs]).then(([additionalLangs, prevLangs]) => negotiateLanguages(this.broadcast.bind(this, 'translateDocument'), this.appVersion, this.defaultLanguage, this.availableLanguages, additionalLangs, prevLangs, requestedLangs)); - } - - const remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - remote.service = new Service('l20n').method('registerView', (...args) => remote.registerView(...args)).method('resolvedLanguages', (...args) => remote.resolvedLanguages(...args)).method('requestLanguages', (...args) => remote.requestLanguages(...args)).method('resolveEntities', (...args) => remote.resolveEntities(...args)).method('formatValues', (...args) => remote.formatValues(...args)).method('getName', (...args) => remote.getName(...args)).method('processString', (...args) => remote.processString(...args)).on('disconnect', clientId => remote.unregisterView(clientId)).listen(channel); -})(); -// a-z -// a-z -// A-Z -// a-f -// A-F \ No newline at end of file diff --git a/bower_components/l20n/dist/gecko/gaia/build/l20n.js b/bower_components/l20n/dist/gecko/gaia/build/l20n.js deleted file mode 100755 index 1fe0fc3..0000000 --- a/bower_components/l20n/dist/gecko/gaia/build/l20n.js +++ /dev/null @@ -1,2847 +0,0 @@ -'use strict'; - -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -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, lang) { - // We need to decode URI because build system DOM reader - // may replace `{locale}` with `%7Blocale%7D`. See bug 1098188 - const url = decodeURI(res).replace('{locale}', lang.code); - const { file, content } = htmloptimizer.getFileByRelativePath(url); - if (!file) { - return Promise.reject(new L10nError('Not found: ' + url)); - } - - const parsed = res.endsWith('.json') ? JSON.parse(content) : content; - return Promise.resolve(parsed); -} - -// Walk an entry node searching for content leaves -function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const newEntry = Object.create(null); - - if (entry.value) { - newEntry.value = walkValue$1(entry.value, fn); - } - - if (entry.index) { - newEntry.index = entry.index; - } - - if (entry.attrs) { - newEntry.attrs = Object.create(null); - for (let key in entry.attrs) { - newEntry.attrs[key] = walkEntry(entry.attrs[key], fn); - } - } - - return newEntry; -} - -function walkValue$1(value, fn) { - if (typeof value === 'string') { - return fn(value); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; key = keys[i]; i++) { - newValue[key] = walkValue$1(value[key], fn); - } - - return newValue; -} - -/* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - -function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz' - }; - - const mods = { - 'fr-x-psaccent': val => val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => val.replace(reWords, match => '‮' + match + '‬') - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = (map, val) => val.replace(reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function (part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; -} - -const pseudo$1 = 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') - } -}); - -const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function (ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function () { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function () { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function () { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if (cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90 || // A-Z - 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 || // 0-9 - cc === 95) { - // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function () { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if (cc > 96 && cc < 103 || cc > 64 && cc < 71 || cc > 47 && cc < 58) { - // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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 () { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function (attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function () { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function () { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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 () { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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 () { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function (message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } -}; - -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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } -}; - -const KNOWN_MACROS$1 = ['plural']; -const MAX_PLACEABLE_LENGTH$1 = 2500; - -// Unicode bidi isolation characters -const FSI$1 = '⁨'; -const PDI$1 = '⁩'; - -const 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier$1(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI$1 + '{{ ' + id + ' }}' + PDI$1]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - 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 ([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable$1(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector$1(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier$1(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? resolveIdentifier$1(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector$1(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue$1(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue$1(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); -} - -const 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 -}; - -// utility functions for plural rules methods -function isIn(n, list) { - return list.indexOf(n) !== -1; -} -function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; -} - -// list of all plural rules methods: -// map an integer to the plural form name to use -const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function () { - return 'other'; - }; - } - return pluralRules[index]; -} - -let Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - _createClass(Context, [{ - key: '_formatTuple', - value: 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._env.emit('resolveerror', err, this); - return [{ error: err }, err.id]; - } - } - }, { - key: '_formatEntity', - value: function _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple(lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - }, { - key: '_formatValue', - value: function _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - }, { - key: 'fetch', - value: function fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(() => langs); - } - }, { - key: '_resolve', - value: function _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatEntity)); - } - }, { - key: 'resolveValues', - value: function resolveValues(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatValue)); - } - }, { - key: '_getEntity', - value: function _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - }, { - key: '_getNumberFormatter', - value: function _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - }, { - key: '_getMacro', - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - value: function _getMacro(lang, id) { - switch (id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - }]); - - return Context; -})(); - -function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; -} - -function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach(listener => listener.apply(this, args)); - } -} - -function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); -} - -function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); -} - -const parsers = { - properties: PropertiesParser, - l20n: L20nParser -}; - -let Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - _createClass(Env, [{ - key: 'createContext', - value: function createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - }, { - key: 'destroyContext', - value: function destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(resId => deleteIfOrphan(this._resCache, lists, resId)); - } - }, { - key: '_parse', - value: function _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError$1(lang, err)); - return parser.parse.call(parser, emit, data); - } - }, { - key: '_create', - value: function _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry(entries[key], pseudo$1[lang.code].process); - } - return pseudoentries; - } - }, { - key: '_getResource', - value: function _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - const resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - }]); - - return Env; -})(); - -function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some(([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => key.startsWith(resId) ? cache.delete(key) : null); - } -} - -function amendError$1(lang, err) { - err.lang = lang; - return err; -} - -const KNOWN_MACROS = ['plural']; -const MAX_PLACEABLE_LENGTH = 2500; - -// Matches characters outside of the Latin-1 character set -const nonLatin1 = /[^\x01-\xFF]/; - -// Unicode bidi isolation characters -const FSI = '⁨'; -const PDI = '⁩'; - -const resolutionChain = new WeakSet(); - -function createEntry(node) { - const keys = Object.keys(node); - - // the most common scenario: a simple string with no arguments - if (typeof node.$v === 'string' && keys.length === 2) { - return node.$v; - } - - let attrs; - - for (let i = 0, key; key = keys[i]; i++) { - // skip $i (id), $v (value), $x (index) - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let res; - - try { - res = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, '{{ ' + id + ' }}']; - } - - const value = res[1]; - - if (typeof value === 'number') { - return res; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - 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)) { - // When dealing with non-Latin-1 text - // we wrap substitutions in bidi isolate characters - // to avoid bidi issues. - res[1] = FSI + value + PDI; - } - - return res; - } - - return [{}, '{{ ' + id + ' }}']; -} - -function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function ([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else if (cur.t === 'idOrVar') { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.v); - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector(ctx, lang, args, expr, index) { - const selectorName = index[0].v; - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[1] ? resolveIdentifier(ctx, lang, args, index[1])[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (expr.hasOwnProperty(selector)) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try 'other' - if ('other' in expr) { - return resolveValue(locals, ctx, lang, args, expr.other); - } - - throw new L10nError('Unresolvable value'); -} - -function LegacyContext(env) { - Context.call(this, env); -} - -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, key; - - 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; - 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, v; - - 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; - } - - // Hash value - 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; - } - - // simple value - 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] }]; - } - } -}; - -// Recursively walk an AST node searching for content leaves -function walkContent(node, fn) { - if (typeof node === 'string') { - return fn(node); - } - - if (node.t === 'idOrVar') { - return node; - } - - const rv = Array.isArray(node) ? [] : {}; - const keys = Object.keys(node); - - for (let i = 0, key; key = keys[i]; i++) { - // don't change identifier ($i) nor indices ($x) - if (key === '$i' || key === '$x') { - rv[key] = node[key]; - } else { - rv[key] = walkContent(node[key], fn); - } - } - return rv; -} - -// XXX babel's inheritance code triggers JavaScript warnings about modifying -// the prototype object so we use regular prototypal inheritance here -function LegacyEnv(defaultLang, fetchResource) { - Env.call(this, defaultLang, fetchResource); -} - -LegacyEnv.prototype = Object.create(Env.prototype); - -LegacyEnv.prototype.createContext = function (resIds) { - const ctx = new LegacyContext(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; -}; - -LegacyEnv.prototype._parse = function (syntax, lang, data) { - const emit = (type, err) => this.emit(type, amendError$1(lang, err)); - return PropertiesParser$1.parse.call(PropertiesParser$1, emit, data); -}; - -LegacyEnv.prototype._create = function (lang, ast) { - const entries = Object.create(null); - const create = lang.src === 'pseudo' ? createPseudoEntry : createEntry; - - for (let i = 0, node; node = ast[i]; i++) { - const 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$1[lang.code].process)); -} - -// match the opening angle bracket (<) in HTML tags, and HTML entities like -// &, &, &. -const reOverlay = /<|&#?\w+;/; - -const 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'], - // value is special-cased in isAttrAllowed - input: ['alt', 'placeholder'], - menuitem: ['label'], - menu: ['label'], - optgroup: ['label'], - option: ['label'], - track: ['label'], - img: ['alt'], - textarea: ['placeholder'], - th: ['abbr'] - } -}; - -function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } -} - -// The goal of overlay is to move the children of `translationElement` -// into `sourceElement` such that `sourceElement`'s own children are not -// replaced, but onle have their text nodes and their attributes modified. -// -// We want to make it possible for localizers to apply text-level semantics to -// the translations and make use of HTML entities. At the same time, we -// don't trust translations so we need to filter unsafe elements and -// attribtues out and we don't want to break the Web by replacing elements to -// which third-party code might have created references (e.g. two-way -// bindings in MVC frameworks). -function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while (childElement = translationElement.childNodes[0]) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement(childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild(translationElement.ownerDocument.createTextNode(childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; attr = translationElement.attributes[k]; k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } -} - -// XXX the allowed list should be amendable; https://bugzil.la/922573 -function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; -} - -function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; -} - -// Get n-th immediate child of context that is of the same type as element. -// XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: -// 1) :scope is widely supported in more browsers and 2) it works with -// DocumentFragments. -function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; -} - -// Get the index of the element among siblings of the same type. -function getIndexOfType(element) { - let index = 0; - let child; - while (child = element.previousElementSibling) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; -} - -function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string.replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }).replace(/^-/, ''); -} - -const reHtml = /[&<>]/g; -const htmlEntities = { - '&': '&', - '<': '<', - '>': '>' -}; - -function getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), el => el.getAttribute('href')); -} - -function getTranslatables(element) { - const 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, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); -} - -function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [id, JSON.parse(args.replace(reHtml, match => htmlEntities[match]))] : id; - }); - - return view._resolveEntities(langs, keys); -} - -function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(translations => applyTranslations(view, elements, translations)); -} - -function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); -} - -// Polyfill NodeList.prototype[Symbol.iterator] for Chrome. -// See https://code.google.com/p/chromium/issues/detail?id=401699 -if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; -} - -// Intl.Locale -function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; -} - -function serializeContext(ctx, lang) { - const cache = ctx._env._resCache; - const resIds = Array.from(ctx._env._resLists.get(ctx)); - return resIds.reduceRight(([errorsSeq, entriesSeq], cur) => { - const sourceRes = cache.get(cur + 'en-USapp'); - const langRes = cache.get(cur + lang.code + lang.src); - const [errors, entries] = serializeEntries(lang, langRes instanceof L10nError ? {} : langRes, sourceRes instanceof L10nError ? {} : sourceRes); - return [errorsSeq.concat(errors), Object.assign(entriesSeq, entries)]; - }, [[], Object.create(null)]); -} - -function serializeEntries(lang, langEntries, sourceEntries) { - const errors = []; - const entries = Object.create(null); - - for (let id in sourceEntries) { - const sourceEntry = sourceEntries[id]; - const 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' || // a simple string - typeof entity.value === 'string' || // a simple string, entity with attrs - Array.isArray(entity.value) || // a complex string - typeof entity.value === 'object' && // a dict with an index - entity.index !== null; -} - -function areAttrsEqual(attrs1, attrs2) { - const keys1 = Object.keys(attrs1 || Object.create(null)); - const keys2 = Object.keys(attrs2 || Object.create(null)); - - if (keys1.length !== keys2.length) { - return false; - } - - for (let 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, lang) { - const cache = ctx._env._resCache; - const resIds = Array.from(ctx._env._resLists.get(ctx)); - return resIds.reduce(([errorsSeq, entriesSeq], cur) => { - const sourceRes = cache.get(cur + 'en-USapp'); - const langRes = cache.get(cur + lang.code + lang.src); - const [errors, entries] = serializeEntries$1(lang, langRes instanceof L10nError ? {} : langRes, sourceRes instanceof L10nError ? {} : sourceRes); - return [errorsSeq.concat(errors), entriesSeq.concat(entries)]; - }, [[], []]); -} - -function serializeEntries$1(lang, langEntries, sourceEntries) { - const errors = []; - const entries = Object.keys(sourceEntries).map(id => { - const sourceEntry = sourceEntries[id]; - const 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 }; - } - - const node = { - $i: id - }; - - if (entry.value !== null) { - node.$v = entry.value; - } - - if (entry.index !== null) { - node.$x = entry.index; - } - - for (let key in entry.attrs) { - node[key] = serializeAttribute(entry.attrs[key]); - } - - return node; -} - -function serializeAttribute(attr) { - if (typeof attr === 'string') { - return attr; - } - - const 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' || // a simple string - typeof entity.value === 'string' || // a simple string, entity with attrs - Array.isArray(entity.value) || // a complex string - typeof entity.value === 'object' && // a dict with an index - entity.index !== null; -} - -function areAttrsEqual$1(attrs1, attrs2) { - const keys1 = Object.keys(attrs1 || Object.create(null)); - const keys2 = Object.keys(attrs2 || Object.create(null)); - - if (keys1.length !== keys2.length) { - return false; - } - - for (let 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; -} - -let View = (function () { - function View(htmloptimizer, fetchResource) { - _classCallCheck(this, View); - - this.htmloptimizer = htmloptimizer; - this.doc = htmloptimizer.document; - - this.isEnabled = this.doc.querySelector('link[rel="localization"]'); - // XXX we should check if the app uses l10n.js instead, but due to lazy - // loading we can't rely on querySelector. - this.isLegacy = !this.doc.querySelector('script[src*="l20n"]'); - - const EnvClass = this.isLegacy ? LegacyEnv : Env; - this.env = new EnvClass(htmloptimizer.config.GAIA_DEFAULT_LOCALE, fetchResource); - this.ctx = this.env.createContext(getResourceLinks(this.doc.head)); - - // add the url of the currently processed webapp to all errors - this.env.addEventListener('*', amendError.bind(this)); - - this.stopBuildError = null; - const log = logError.bind(this); - const stop = stopBuild.bind(this); - - // stop the build if these errors happen for en-US - // XXX tv_apps break the build https://bugzil.la/1179833 - // this.env.addEventListener('fetcherror', stop); - this.env.addEventListener('parseerror', stop); - this.env.addEventListener('duplicateerror', stop); - this.env.addEventListener('notfounderror', stop); - // XXX sms breaks the build https://bugzil.la/1178187 - // this.env.addEventListener('resolveerror', stop); - - this.env.addEventListener('deprecatewarning', log); - - // if LOCALE_BASEDIR is set alert about missing strings - if (htmloptimizer.config.LOCALE_BASEDIR !== '') { - this.env.addEventListener('fetcherror', log); - this.env.addEventListener('parseerror', log); - this.env.addEventListener('duplicateerror', log); - } - } - - _createClass(View, [{ - key: '_observe', - value: function _observe() {} - }, { - key: '_disconnect', - value: function _disconnect() {} - }, { - key: '_resolveEntities', - value: function _resolveEntities(langs, keys) { - return this.ctx.resolveEntities(langs, keys); - } - }, { - key: 'translateDocument', - value: function translateDocument(code) { - const dir = getDirection(code); - const langs = [{ code, src: 'app' }]; - const setDocLang = () => { - this.doc.documentElement.lang = code; - this.doc.documentElement.dir = dir; - }; - return this.ctx.fetch(langs).then(langs => translateFragment(this, langs, this.doc.documentElement)).then(setDocLang); - } - }, { - key: 'serializeResources', - value: function serializeResources(code) { - const lang = { - code, - src: code in pseudo$1 ? 'pseudo' : 'app' - }; - return fetchContext(this.ctx, lang).then(() => { - const [errors, entries] = this.isLegacy ? serializeLegacyContext(this.ctx, lang) : serializeContext(this.ctx, lang); - - if (errors.length) { - const notFoundErrors = errors.filter(err => err.message.indexOf('not found') > -1).map(err => err.id); - const malformedErrors = errors.filter(err => err.message.indexOf('malformed') > -1).map(err => err.id); - - if (notFoundErrors.length) { - this.htmloptimizer.dump('[l10n] [' + lang.code + ']: ' + notFoundErrors.length + ' missing compared to en-US: ' + notFoundErrors.join(', ')); - } - if (malformedErrors.length) { - this.htmloptimizer.dump('[l10n] [' + lang.code + ']: ' + malformedErrors.length + ' malformed compared to en-US: ' + malformedErrors.join(', ')); - } - } - - return entries; - }); - } - }, { - key: 'checkError', - value: 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 fetchContext(ctx, lang) { - const sourceLang = { code: 'en-US', src: 'app' }; - return Promise.all([sourceLang, lang].map(lang => ctx.fetch([lang]))); -} - -function getView(htmloptimizer) { - const htmlFetch = (...args) => fetchResource(htmloptimizer, ...args); - return new View(htmloptimizer, htmlFetch); -} - -exports.getView = getView; -exports.pseudo = pseudo$1; -exports.walkValue = walkValue$1; -// a-z -// a-z -// A-Z -// a-f -// A-F \ No newline at end of file diff --git a/bower_components/l20n/dist/gecko/gaia/l20n.js b/bower_components/l20n/dist/gecko/gaia/l20n.js deleted file mode 100755 index adf5f96..0000000 --- a/bower_components/l20n/dist/gecko/gaia/l20n.js +++ /dev/null @@ -1,2521 +0,0 @@ -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach(listener => listener.apply(this, args)); - } - } - - function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); - } - - function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); - } - - let Client = (function () { - function Client(remote) { - _classCallCheck(this, Client); - - this.id = this; - this.remote = remote; - - const listeners = {}; - this.on = (...args) => addEventListener(listeners, ...args); - this.emit = (...args) => emit(listeners, ...args); - } - - _createClass(Client, [{ - key: 'method', - value: function method(name, ...args) { - return this.remote[name](...args); - } - }]); - - return Client; - })(); - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach(client => client.emit(type, data)); - } - - 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 load(type, url) { - return new Promise(function (resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - const io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } - }; - - function fetchResource(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function (ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function () { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function () { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function () { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if (cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90 || // A-Z - 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 || // 0-9 - cc === 95) { - // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function () { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if (cc > 96 && cc < 103 || cc > 64 && cc < 71 || cc > 47 && cc < 58) { - // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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 () { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function (attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function () { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function () { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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 () { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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 () { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function (message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '⁨'; - const PDI = '⁩'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + value.length + ', max allowed is ' + MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function ([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function () { - return 'other'; - }; - } - return pluralRules[index]; - } - - let Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - _createClass(Context, [{ - key: '_formatTuple', - value: function _formatTuple(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]; - } - } - }, { - key: '_formatEntity', - value: function _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple(lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - }, { - key: '_formatValue', - value: function _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - }, { - key: 'fetch', - value: function fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(() => langs); - } - }, { - key: '_resolve', - value: function _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatEntity)); - } - }, { - key: 'resolveValues', - value: function resolveValues(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatValue)); - } - }, { - key: '_getEntity', - value: function _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - }, { - key: '_getNumberFormatter', - value: function _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - }, { - key: '_getMacro', - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - value: function _getMacro(lang, id) { - switch (id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - }]); - - return Context; - })(); - - function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; - } - - // Walk an entry node searching for content leaves - function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; key = keys[i]; i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; - } - - /* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - - function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz' - }; - - const mods = { - 'fr-x-psaccent': val => val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => val.replace(reWords, match => '‮' + match + '‬') - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = (map, val) => val.replace(reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function (part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; - } - - const 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') - } - }); - - const parsers = { - properties: PropertiesParser, - l20n: L20nParser - }; - - let Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - _createClass(Env, [{ - key: 'createContext', - value: function createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - }, { - key: 'destroyContext', - value: function destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(resId => deleteIfOrphan(this._resCache, lists, resId)); - } - }, { - key: '_parse', - value: function _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - }, { - key: '_create', - value: function _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry(entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - }, { - key: '_getResource', - value: function _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - const resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - }]); - - return Env; - })(); - - function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some(([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => key.startsWith(resId) ? cache.delete(key) : null); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - // Intl.Locale - function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; - } - - function prioritizeLocales(def, availableLangs, requested) { - let supportedLocale; - // Find the first locale in the requested list that is supported. - for (let i = 0; i < requested.length; i++) { - const locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - let availableLangs = Object.create(null); - let defaultLang = null; - let appVersion = null; - - // XXX take last found instead of first? - const metas = head.querySelectorAll('meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"]'); - for (let meta of metas) { - const name = meta.getAttribute('name'); - const content = meta.getAttribute('content').trim(); - switch (name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap(availableLangs, content); - break; - case 'defaultLanguage': - const [lang, rev] = getLangRevisionTuple(content); - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang, - availableLangs, - appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce((seq, cur) => { - const [lang, rev] = getLangRevisionTuple(cur); - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - const [lang, rev] = str.trim().split(':'); - // if revision is missing, use NaN - return [lang, parseInt(rev)]; - } - - function negotiateLanguages(fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, requestedLangs) { - - const allAvailableLangs = Object.keys(availableLangs).concat(additionalLangs || []).concat(Object.keys(pseudo)); - const newLangs = prioritizeLocales(defaultLang, allAvailableLangs, requestedLangs); - - const langs = newLangs.map(code => ({ - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code) - })); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && arr1.every((elem, i) => elem === arr2[i]); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (let i = 0, langpack; langpack = langpacks[i]; i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - const lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && (!(code in availableLangs) || parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if (code in pseudo && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - let Remote = (function () { - function Remote(fetchResource, broadcast, requestedLangs) { - _classCallCheck(this, Remote); - - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then(() => this.init(requestedLangs)); - } - - _createClass(Remote, [{ - key: 'init', - value: function init(requestedLangs) { - const meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env(this.defaultLanguage, (...args) => this.fetchResource(this.appVersion, ...args)); - - return this.requestLanguages(requestedLangs); - } - }, { - key: 'registerView', - value: function registerView(view, resources) { - return this.interactive.then(() => { - this.ctxs.set(view, this.env.createContext(resources)); - return true; - }); - } - }, { - key: 'unregisterView', - value: function unregisterView(view) { - return this.ctxs.delete(view); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - } - }, { - key: 'formatValues', - value: function formatValues(view, keys) { - return this.languages.then(langs => this.ctxs.get(view).resolveValues(langs, keys)); - } - }, { - key: 'resolvedLanguages', - value: function resolvedLanguages() { - return this.languages; - } - }, { - key: 'requestLanguages', - value: function requestLanguages(requestedLangs) { - return changeLanguages.call(this, getAdditionalLanguages(), requestedLangs); - } - }, { - key: 'getName', - value: function getName(code) { - return pseudo[code].name; - } - }, { - key: 'processString', - value: function processString(code, str) { - return pseudo[code].process(str); - } - }, { - key: 'handleEvent', - value: function handleEvent(evt) { - return changeLanguages.call(this, evt.detail || getAdditionalLanguages(), navigator.languages); - } - }]); - - return Remote; - })(); - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch(() => []); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - const prevLangs = this.languages || []; - return this.languages = Promise.all([additionalLangs, prevLangs]).then(([additionalLangs, prevLangs]) => negotiateLanguages(this.broadcast.bind(this, 'translateDocument'), this.appVersion, this.defaultLanguage, this.availableLanguages, additionalLangs, prevLangs, requestedLangs)); - } - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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'], - // value is special-cased in isAttrAllowed - input: ['alt', 'placeholder'], - menuitem: ['label'], - menu: ['label'], - optgroup: ['label'], - option: ['label'], - track: ['label'], - img: ['alt'], - textarea: ['placeholder'], - th: ['abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while (childElement = translationElement.childNodes[0]) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement(childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild(translationElement.ownerDocument.createTextNode(childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; attr = translationElement.attributes[k]; k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while (child = element.previousElementSibling) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string.replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }).replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>' - }; - - function getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function _translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [id, JSON.parse(args.replace(reHtml, match => htmlEntities[match]))] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - const observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - const readiness = new WeakMap(); - - let View = (function () { - function View(client, doc) { - _classCallCheck(this, View); - - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then(() => init(this, client)); - - const observer = new MutationObserver(onMutations.bind(this)); - this._observe = () => observer.observe(doc, observerConfig); - this._disconnect = () => observer.disconnect(); - - const translateView = langs => translateDocument(this, langs); - client.on('translateDocument', translateView); - this.ready = this._interactive.then(client => client.method('resolvedLanguages')).then(translateView); - } - - _createClass(View, [{ - key: 'requestLanguages', - value: function requestLanguages(langs, global) { - return this._interactive.then(client => client.method('requestLanguages', langs, global)); - } - }, { - key: '_resolveEntities', - value: function _resolveEntities(langs, keys) { - return this._interactive.then(client => client.method('resolveEntities', client.id, langs, keys)); - } - }, { - key: 'formatValue', - value: function formatValue(id, args) { - return this._interactive.then(client => client.method('formatValues', client.id, [[id, args]])).then(values => values[0]); - } - }, { - key: 'formatValues', - value: function formatValues(...keys) { - return this._interactive.then(client => client.method('formatValues', client.id, keys)); - } - }, { - key: 'translateFragment', - value: function translateFragment(frag) { - return this._interactive.then(client => client.method('resolvedLanguages')).then(langs => _translateFragment(this, langs, frag)); - } - }]); - - return View; - })(); - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: () => view._interactive.then(client => client.method('getName', code)), - processString: str => view._interactive.then(client => client.method('processString', code, str)) - }; - } - - function init(view, client) { - view._observe(); - return client.method('registerView', client.id, getResourceLinks(view._doc.head)).then(() => client); - } - - function onMutations(mutations) { - return this._interactive.then(client => client.method('resolvedLanguages')).then(langs => translateMutations(this, langs, mutations)); - } - - function translateDocument(view, langs) { - const html = view._doc.documentElement; - - if (readiness.has(html)) { - return _translateFragment(view, langs, html).then(() => setAllAndEmit(html, langs)); - } - - const translated = - // has the document been already pre-translated? - langs[0].code === html.getAttribute('lang') ? Promise.resolve() : _translateFragment(view, langs, html).then(() => setLangDir(html, langs)); - - return translated.then(() => { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - const codes = langs.map(lang => lang.code); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - const code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false - })); - } - - const remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - document.l10n = new View(new Client(remote), document); - - //Bug 1204660 - Temporary proxy for shared code. Will be removed once - // l10n.js migration is completed. - navigator.mozL10n = { - setAttributes: document.l10n.setAttributes, - getAttributes: document.l10n.getAttributes, - formatValue: (...args) => document.l10n.formatValue(...args), - translateFragment: (...args) => document.l10n.translateFragment(...args), - once: cb => document.l10n.ready.then(cb), - ready: cb => document.l10n.ready.then(() => { - document.addEventListener('DOMRetranslated', cb); - cb(); - }) - }; -})(); -// a-z -// a-z -// A-Z -// a-f -// A-F \ No newline at end of file diff --git a/bower_components/l20n/dist/gecko/web/l20n-common.js b/bower_components/l20n/dist/gecko/web/l20n-common.js deleted file mode 100755 index 0ec2283..0000000 --- a/bower_components/l20n/dist/gecko/web/l20n-common.js +++ /dev/null @@ -1,1860 +0,0 @@ -'use strict'; - -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -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 load(type, url) { - return new Promise(function (resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); -} - -const io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } -}; - -function fetchResource$1(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); -} - -const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function (ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function () { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function () { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function () { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if (cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90 || // A-Z - 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 || // 0-9 - cc === 95) { - // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function () { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if (cc > 96 && cc < 103 || cc > 64 && cc < 71 || cc > 47 && cc < 58) { - // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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 () { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function (attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function () { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function () { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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 () { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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 () { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function (message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } -}; - -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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } -}; - -const KNOWN_MACROS = ['plural']; -const MAX_PLACEABLE_LENGTH = 2500; - -// Unicode bidi isolation characters -const FSI = '⁨'; -const PDI = '⁩'; - -const resolutionChain = new WeakSet(); - -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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + value.length + ', max allowed is ' + MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; -} - -function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function ([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); -} - -function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); -} - -const 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 -}; - -// utility functions for plural rules methods -function isIn(n, list) { - return list.indexOf(n) !== -1; -} -function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; -} - -// list of all plural rules methods: -// map an integer to the plural form name to use -const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function () { - return 'other'; - }; - } - return pluralRules[index]; -} - -let Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - _createClass(Context, [{ - key: '_formatTuple', - value: function _formatTuple(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]; - } - } - }, { - key: '_formatEntity', - value: function _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple(lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - }, { - key: '_formatValue', - value: function _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - }, { - key: 'fetch', - value: function fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(() => langs); - } - }, { - key: '_resolve', - value: function _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatEntity)); - } - }, { - key: 'resolveValues', - value: function resolveValues(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatValue)); - } - }, { - key: '_getEntity', - value: function _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - }, { - key: '_getNumberFormatter', - value: function _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - }, { - key: '_getMacro', - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - value: function _getMacro(lang, id) { - switch (id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - }]); - - return Context; -})(); - -function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; -} - -// Walk an entry node searching for content leaves -function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; key = keys[i]; i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; -} - -/* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - -function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz' - }; - - const mods = { - 'fr-x-psaccent': val => val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => val.replace(reWords, match => '‮' + match + '‬') - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = (map, val) => val.replace(reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function (part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; -} - -const 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') - } -}); - -function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach(listener => listener.apply(this, args)); - } -} - -function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); -} - -function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); -} - -const parsers = { - properties: PropertiesParser, - l20n: L20nParser -}; - -let Env$1 = (function () { - function Env$1(defaultLang, fetchResource) { - _classCallCheck(this, Env$1); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - _createClass(Env$1, [{ - key: 'createContext', - value: function createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - }, { - key: 'destroyContext', - value: function destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(resId => deleteIfOrphan(this._resCache, lists, resId)); - } - }, { - key: '_parse', - value: function _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - }, { - key: '_create', - value: function _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry(entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - }, { - key: '_getResource', - value: function _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - const resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - }]); - - return Env$1; -})(); - -function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some(([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => key.startsWith(resId) ? cache.delete(key) : null); - } -} - -function amendError(lang, err) { - err.lang = lang; - return err; -} - -exports.fetchResource = fetchResource$1; -exports.Env = Env$1; -// a-z -// a-z -// A-Z -// a-f -// A-F \ No newline at end of file diff --git a/bower_components/l20n/dist/gecko/web/l20n.js b/bower_components/l20n/dist/gecko/web/l20n.js deleted file mode 100755 index 1022d76..0000000 --- a/bower_components/l20n/dist/gecko/web/l20n.js +++ /dev/null @@ -1,2507 +0,0 @@ -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -(function () { - 'use strict'; - - function emit(listeners, ...args) { - const type = args.shift(); - - if (listeners['*']) { - listeners['*'].slice().forEach(listener => listener.apply(this, args)); - } - - if (listeners[type]) { - listeners[type].slice().forEach(listener => listener.apply(this, args)); - } - } - - function addEventListener(listeners, type, listener) { - if (!(type in listeners)) { - listeners[type] = []; - } - listeners[type].push(listener); - } - - function removeEventListener(listeners, type, listener) { - const typeListeners = listeners[type]; - const pos = typeListeners.indexOf(listener); - if (pos === -1) { - return; - } - - typeListeners.splice(pos, 1); - } - - let Client = (function () { - function Client(remote) { - _classCallCheck(this, Client); - - this.id = this; - this.remote = remote; - - const listeners = {}; - this.on = (...args) => addEventListener(listeners, ...args); - this.emit = (...args) => emit(listeners, ...args); - } - - _createClass(Client, [{ - key: 'method', - value: function method(name, ...args) { - return this.remote[name](...args); - } - }]); - - return Client; - })(); - - function broadcast(type, data) { - Array.from(this.ctxs.keys()).forEach(client => client.emit(type, data)); - } - - 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 load(type, url) { - return new Promise(function (resolve, reject) { - const xhr = new XMLHttpRequest(); - - if (xhr.overrideMimeType) { - xhr.overrideMimeType(type); - } - - xhr.open('GET', url, true); - - if (type === 'application/json') { - xhr.responseType = 'json'; - } - - xhr.addEventListener('load', function io_onload(e) { - if (e.target.status === 200 || e.target.status === 0) { - // Sinon.JS's FakeXHR doesn't have the response property - resolve(e.target.response || e.target.responseText); - } else { - reject(new L10nError('Not found: ' + url)); - } - }); - xhr.addEventListener('error', reject); - xhr.addEventListener('timeout', reject); - - // the app: protocol throws on 404, see https://bugzil.la/827243 - try { - xhr.send(null); - } catch (e) { - if (e.name === 'NS_ERROR_FILE_NOT_FOUND') { - // the app: protocol throws on 404, see https://bugzil.la/827243 - reject(new L10nError('Not found: ' + url)); - } else { - throw e; - } - } - }); - } - - const io = { - extra: function (code, ver, path, type) { - return navigator.mozApps.getLocalizationResource(code, ver, path, type); - }, - app: function (code, ver, path, type) { - switch (type) { - case 'text': - return load('text/plain', path); - case 'json': - return load('application/json', path); - default: - throw new L10nError('Unknown file type: ' + type); - } - } - }; - - function fetchResource(ver, res, lang) { - const url = res.replace('{locale}', lang.code); - const type = res.endsWith('.json') ? 'json' : 'text'; - return io[lang.src](lang.code, ver, url, type); - } - - const 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) { - // we want to recover, but we don't need it in entries - 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; - const 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'); - } - - const ch = this._source[this._index]; - const value = this.getValue(ch, index === undefined); - let attrs; - - if (value === undefined) { - if (ch === '>') { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } else { - const ws1 = this.getRequiredWS(); - if (this._source[this._index] !== '>') { - if (!ws1) { - throw this.error('Expected ">"'); - } - attrs = this.getAttributes(); - } - } - - // skip '>' - ++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, - attrs, - index - }; - } - }, - - getValue: function (ch = this._source[this._index], optional = false) { - switch (ch) { - case '\'': - case '"': - return this.getString(ch, 1); - case '{': - return this.getHash(); - } - - if (!optional) { - throw this.error('Unknown value type'); - } - - return; - }, - - getWS: function () { - let cc = this._source.charCodeAt(this._index); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - }, - - getRequiredWS: function () { - const pos = this._index; - let cc = this._source.charCodeAt(pos); - // space, \n, \t, \r - while (cc === 32 || cc === 10 || cc === 9 || cc === 13) { - cc = this._source.charCodeAt(++this._index); - } - return this._index !== pos; - }, - - getIdentifier: function () { - const start = this._index; - let cc = this._source.charCodeAt(this._index); - - if (cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90 || // A-Z - 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 || // 0-9 - cc === 95) { - // _ - cc = this._source.charCodeAt(++this._index); - } - - return this._source.slice(start, this._index); - }, - - getUnicodeChar: function () { - for (let i = 0; i < 4; i++) { - let cc = this._source.charCodeAt(++this._index); - if (cc > 96 && cc < 103 || cc > 64 && cc < 71 || cc > 47 && cc < 58) { - // 0-9 - 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) { - const body = []; - let placeables = 0; - - this._index += opcharLen; - const start = this._index; - - let bufStart = start; - let buf = ''; - - while (true) { - this.stringRe.lastIndex = this._index; - const 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; - const 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 () { - const attrs = Object.create(null); - - while (true) { - this.getAttribute(attrs); - const ws1 = this.getRequiredWS(); - const ch = this._source.charAt(this._index); - if (ch === '>') { - break; - } else if (!ws1) { - throw this.error('Expected ">"'); - } - } - return attrs; - }, - - getAttribute: function (attrs) { - const key = this.getIdentifier(); - let index; - - 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(); - const value = this.getValue(); - - if (key in attrs) { - throw this.error('Duplicate attribute "' + key, 'duplicateerror'); - } - - if (!index && typeof value === 'string') { - attrs[key] = value; - } else { - attrs[key] = { - value, - index - }; - } - }, - - getHash: function () { - const items = Object.create(null); - - ++this._index; - this.getWS(); - - let defKey; - - while (true) { - const [key, value, def] = this.getHashItem(); - items[key] = value; - - if (def) { - if (defKey) { - throw this.error('Default item redefinition forbidden'); - } - defKey = key; - } - this.getWS(); - - const 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; - } - - return items; - }, - - getHashItem: function () { - let defItem = false; - if (this._source[this._index] === '*') { - ++this._index; - defItem = true; - } - - const 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; - const start = this._index; - const end = this._source.indexOf('*/', start); - - if (end === -1) { - throw this.error('Comment without a closing tag'); - } - - this._index = end + 2; - }, - - getExpression: function () { - let exp = this.getPrimaryExpression(); - - while (true) { - let 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) { - let exp; - - 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 () { - const 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) { - const items = []; - let closed = false; - - this.getWS(); - - if (this._source[this._index] === closeChar) { - ++this._index; - closed = true; - } - - while (!closed) { - items.push(callback.call(this)); - this.getWS(); - let 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 () { - const pos = this._index; - let nextEntity = this._source.indexOf('<', pos); - let nextComment = this._source.indexOf('/*', pos); - - if (nextEntity === -1) { - nextEntity = this._length; - } - if (nextComment === -1) { - nextComment = this._length; - } - - let nextEntry = Math.min(nextEntity, nextComment); - - this._index = nextEntry; - }, - - error: function (message, type = 'parsererror') { - const pos = this._index; - - let start = this._source.lastIndexOf('<', pos - 1); - const lastClose = this._source.lastIndexOf('>', pos - 1); - start = lastClose > start ? lastClose + 1 : start; - const context = this._source.slice(start, pos + 10); - - const msg = message + ' at pos ' + pos + ': `' + context + '`'; - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - 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, key; - - 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; - 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) { - const 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') { - const 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 && (!entries[id] || isSimpleNode)) { - if (id in root) { - throw this.error(); - } - 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, type = 'parsererror') { - const err = new L10nError(msg); - if (this.emit) { - this.emit(type, err); - } - return err; - } - }; - - const KNOWN_MACROS = ['plural']; - const MAX_PLACEABLE_LENGTH = 2500; - - // Unicode bidi isolation characters - const FSI = '⁨'; - const PDI = '⁩'; - - const resolutionChain = new WeakSet(); - - 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); - - let rv; - // if format fails, we want the exception to bubble up and stop the whole - // resolving process; however, we still need to remove the entity from the - // resolution chain - 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); - } - } - - // XXX: special case for Node.js where still: - // '__proto__' in Object.create(null) => true - if (id === '__proto__') { - throw new L10nError('Illegal id: ' + id); - } - - const 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) { - let newLocals, value; - - try { - [newLocals, value] = resolveIdentifier(ctx, lang, args, id); - } catch (err) { - return [{ error: err }, FSI + '{{ ' + id + ' }}' + PDI]; - } - - if (typeof value === 'number') { - const formatter = ctx._getNumberFormatter(lang); - return [newLocals, formatter.format(value)]; - } - - if (typeof value === 'string') { - // prevent Billion Laughs attacks - if (value.length >= MAX_PLACEABLE_LENGTH) { - throw new L10nError('Too many characters in placeable (' + value.length + ', max allowed is ' + MAX_PLACEABLE_LENGTH + ')'); - } - return [newLocals, FSI + value + PDI]; - } - - return [{}, FSI + '{{ ' + id + ' }}' + PDI]; - } - - function interpolate(locals, ctx, lang, args, arr) { - return arr.reduce(function ([localsSeq, valueSeq], cur) { - if (typeof cur === 'string') { - return [localsSeq, valueSeq + cur]; - } else { - const [, value] = subPlaceable(locals, ctx, lang, args, cur.name); - // wrap the substitution in bidi isolate characters - return [localsSeq, valueSeq + value]; - } - }, [locals, '']); - } - - function resolveSelector(ctx, lang, args, expr, index) { - //XXX: Dehardcode!!! - let selectorName; - if (index[0].type === 'call' && index[0].expr.type === 'prop' && index[0].expr.expr.name === 'cldr') { - selectorName = 'plural'; - } else { - selectorName = index[0].name; - } - const selector = resolveIdentifier(ctx, lang, args, selectorName)[1]; - - if (typeof selector !== 'function') { - // selector is a simple reference to an entity or args - return selector; - } - - const argValue = index[0].args ? resolveIdentifier(ctx, lang, args, index[0].args[0].name)[1] : undefined; - - if (selectorName === 'plural') { - // special cases for zero, one, two if they are defined on the hash - 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)) { - return interpolate(locals, ctx, lang, args, expr); - } - - // otherwise, it's a dict - if (index) { - // try to use the index in order to select the right dict member - const selector = resolveSelector(ctx, lang, args, expr, index); - if (selector in expr) { - return resolveValue(locals, ctx, lang, args, expr[selector]); - } - } - - // if there was no index or no selector was found, try the default - // XXX 'other' is an artifact from Gaia - const defaultKey = expr.__default || 'other'; - if (defaultKey in expr) { - return resolveValue(locals, ctx, lang, args, expr[defaultKey]); - } - - throw new L10nError('Unresolvable value'); - } - - const 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 - }; - - // utility functions for plural rules methods - function isIn(n, list) { - return list.indexOf(n) !== -1; - } - function isBetween(n, start, end) { - return typeof n === typeof start && start <= n && n <= end; - } - - // list of all plural rules methods: - // map an integer to the plural form name to use - const 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) { - // return a function that gives the plural form name for a given integer - const index = locales2rules[code.replace(/-.*$/, '')]; - if (!(index in pluralRules)) { - return function () { - return 'other'; - }; - } - return pluralRules[index]; - } - - let Context = (function () { - function Context(env) { - _classCallCheck(this, Context); - - this._env = env; - this._numberFormatters = null; - } - - _createClass(Context, [{ - key: '_formatTuple', - value: function _formatTuple(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]; - } - } - }, { - key: '_formatEntity', - value: function _formatEntity(lang, args, entity, id) { - const [, value] = this._formatTuple(lang, args, entity, id); - - const formatted = { - value, - attrs: null - }; - - if (entity.attrs) { - formatted.attrs = Object.create(null); - for (let key in entity.attrs) { - /* jshint -W089 */ - const [, attrValue] = this._formatTuple(lang, args, entity.attrs[key], id, key); - formatted.attrs[key] = attrValue; - } - } - - return formatted; - } - }, { - key: '_formatValue', - value: function _formatValue(lang, args, entity, id) { - return this._formatTuple(lang, args, entity, id)[1]; - } - }, { - key: 'fetch', - value: function fetch(langs) { - if (langs.length === 0) { - return Promise.resolve(langs); - } - - const resIds = Array.from(this._env._resLists.get(this)); - - return Promise.all(resIds.map(this._env._getResource.bind(this._env, langs[0]))).then(() => langs); - } - }, { - key: '_resolve', - value: function _resolve(langs, keys, formatter, prevResolved) { - const lang = langs[0]; - - if (!lang) { - return reportMissing.call(this, keys, formatter, prevResolved); - } - - let hasUnresolved = false; - - const resolved = keys.map((key, i) => { - if (prevResolved && prevResolved[i] !== undefined) { - return prevResolved[i]; - } - const [id, args] = Array.isArray(key) ? key : [key, undefined]; - const entity = this._getEntity(lang, id); - - if (entity) { - return formatter.call(this, lang, args, entity, id); - } - - this._env.emit('notfounderror', new L10nError('"' + id + '"' + ' not found in ' + lang.code, id, lang), this); - hasUnresolved = true; - }); - - if (!hasUnresolved) { - return resolved; - } - - return this.fetch(langs.slice(1)).then(nextLangs => this._resolve(nextLangs, keys, formatter, resolved)); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatEntity)); - } - }, { - key: 'resolveValues', - value: function resolveValues(langs, keys) { - return this.fetch(langs).then(langs => this._resolve(langs, keys, this._formatValue)); - } - }, { - key: '_getEntity', - value: function _getEntity(lang, id) { - const cache = this._env._resCache; - const resIds = Array.from(this._env._resLists.get(this)); - - // Look for `id` in every resource in order. - for (let i = 0, resId; resId = resIds[i]; i++) { - const resource = cache.get(resId + lang.code + lang.src); - if (resource instanceof L10nError) { - continue; - } - if (id in resource) { - return resource[id]; - } - } - return undefined; - } - }, { - key: '_getNumberFormatter', - value: function _getNumberFormatter(lang) { - if (!this._numberFormatters) { - this._numberFormatters = new Map(); - } - if (!this._numberFormatters.has(lang)) { - const formatter = Intl.NumberFormat(lang, { - useGrouping: false - }); - this._numberFormatters.set(lang, formatter); - return formatter; - } - return this._numberFormatters.get(lang); - } - }, { - key: '_getMacro', - - // XXX in the future macros will be stored in localization resources together - // with regular entities and this method will not be needed anymore - value: function _getMacro(lang, id) { - switch (id) { - case 'plural': - return getPluralRule(lang.code); - default: - return undefined; - } - } - }]); - - return Context; - })(); - - function reportMissing(keys, formatter, resolved) { - const missingIds = new Set(); - - keys.forEach((key, i) => { - if (resolved && resolved[i] !== undefined) { - return; - } - const id = Array.isArray(key) ? key[0] : key; - missingIds.add(id); - resolved[i] = formatter === this._formatValue ? id : { value: id, attrs: null }; - }); - - this._env.emit('notfounderror', new L10nError('"' + Array.from(missingIds).join(', ') + '"' + ' not found in any language', missingIds), this); - - return resolved; - } - - // Walk an entry node searching for content leaves - function walkEntry(entry, fn) { - if (typeof entry === 'string') { - return fn(entry); - } - - const 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 (let 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); - } - - // skip expressions in placeables - if (value.type) { - return value; - } - - const newValue = Array.isArray(value) ? [] : Object.create(null); - const keys = Object.keys(value); - - for (let i = 0, key; key = keys[i]; i++) { - newValue[key] = walkValue(value[key], fn); - } - - return newValue; - } - - /* Pseudolocalizations - * - * pseudo is a dict of strategies to be used to modify the English - * context in order to create pseudolocalizations. These can be used by - * developers to test the localizability of their code without having to - * actually speak a foreign language. - * - * Currently, the following pseudolocales are supported: - * - * fr-x-psaccent - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ - * - * In Accented English all English letters are replaced by accented - * Unicode counterparts which don't impair the readability of the content. - * This allows developers to quickly test if any given string is being - * correctly displayed in its 'translated' form. Additionally, simple - * heuristics are used to make certain words longer to better simulate the - * experience of international users. - * - * ar-x-psbidi - ɥsıʅƃuƎ ıpıԐ - * - * Bidi English is a fake RTL locale. All words are surrounded by - * Unicode formatting marks forcing the RTL directionality of characters. - * In addition, to make the reversed text easier to read, individual - * letters are flipped. - * - * Note: The name above is hardcoded to be RTL in case code editors have - * trouble with the RLO and PDF Unicode marks. In reality, it should be - * surrounded by those marks as well. - * - * See https://bugzil.la/900182 for more information. - * - */ - - function createGetter(id, name) { - let _pseudo = null; - - return function getPseudo() { - if (_pseudo) { - return _pseudo; - } - - const reAlphas = /[a-zA-Z]/g; - const reVowels = /[aeiouAEIOU]/g; - const reWords = /[^\W0-9_]+/g; - // strftime tokens (%a, %Eb), template {vars}, HTML entities (‪) - // and HTML tags. - const reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/; - - const charMaps = { - 'fr-x-psaccent': 'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ', - 'ar-x-psbidi': - // XXX Use pɟפ˥ʎ as replacements for ᗡℲ⅁⅂⅄. https://bugzil.la/1007340 - '∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz' - }; - - const mods = { - 'fr-x-psaccent': val => val.replace(reVowels, match => match + match.toLowerCase()), - - // Surround each word with Unicode formatting codes, RLO and PDF: - // U+202E: RIGHT-TO-LEFT OVERRIDE (RLO) - // U+202C: POP DIRECTIONAL FORMATTING (PDF) - // See http://www.w3.org/International/questions/qa-bidi-controls - 'ar-x-psbidi': val => val.replace(reWords, match => '‮' + match + '‬') - }; - - // Replace each Latin letter with a Unicode character from map - const replaceChars = (map, val) => val.replace(reAlphas, match => map.charAt(match.charCodeAt(0) - 65)); - - const transform = val => replaceChars(charMaps[id], mods[id](val)); - - // apply fn to translatable parts of val - const apply = (fn, val) => { - if (!val) { - return val; - } - - const parts = val.split(reExcluded); - const modified = parts.map(function (part) { - if (reExcluded.test(part)) { - return part; - } - return fn(part); - }); - return modified.join(''); - }; - - return _pseudo = { - name: transform(name), - process: str => apply(transform, str) - }; - }; - } - - const 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') - } - }); - - const parsers = { - properties: PropertiesParser, - l20n: L20nParser - }; - - let Env = (function () { - function Env(defaultLang, fetchResource) { - _classCallCheck(this, Env); - - this.defaultLang = defaultLang; - this.fetchResource = fetchResource; - - this._resLists = new Map(); - this._resCache = new Map(); - - const listeners = {}; - this.emit = emit.bind(this, listeners); - this.addEventListener = addEventListener.bind(this, listeners); - this.removeEventListener = removeEventListener.bind(this, listeners); - } - - _createClass(Env, [{ - key: 'createContext', - value: function createContext(resIds) { - const ctx = new Context(this); - this._resLists.set(ctx, new Set(resIds)); - return ctx; - } - }, { - key: 'destroyContext', - value: function destroyContext(ctx) { - const lists = this._resLists; - const resList = lists.get(ctx); - - lists.delete(ctx); - resList.forEach(resId => deleteIfOrphan(this._resCache, lists, resId)); - } - }, { - key: '_parse', - value: function _parse(syntax, lang, data) { - const parser = parsers[syntax]; - if (!parser) { - return data; - } - - const emit = (type, err) => this.emit(type, amendError(lang, err)); - return parser.parse.call(parser, emit, data); - } - }, { - key: '_create', - value: function _create(lang, entries) { - if (lang.src !== 'pseudo') { - return entries; - } - - const pseudoentries = Object.create(null); - for (let key in entries) { - pseudoentries[key] = walkEntry(entries[key], pseudo[lang.code].process); - } - return pseudoentries; - } - }, { - key: '_getResource', - value: function _getResource(lang, res) { - const cache = this._resCache; - const id = res + lang.code + lang.src; - - if (cache.has(id)) { - return cache.get(id); - } - - const syntax = res.substr(res.lastIndexOf('.') + 1); - - const saveEntries = data => { - const entries = this._parse(syntax, lang, data); - cache.set(id, this._create(lang, entries)); - }; - - const recover = err => { - err.lang = lang; - this.emit('fetcherror', err); - cache.set(id, err); - }; - - const langToFetch = lang.src === 'pseudo' ? { code: this.defaultLang, src: 'app' } : lang; - - const resource = this.fetchResource(res, langToFetch).then(saveEntries, recover); - - cache.set(id, resource); - - return resource; - } - }]); - - return Env; - })(); - - function deleteIfOrphan(cache, lists, resId) { - const isNeeded = Array.from(lists).some(([ctx, resIds]) => resIds.has(resId)); - - if (!isNeeded) { - cache.forEach((val, key) => key.startsWith(resId) ? cache.delete(key) : null); - } - } - - function amendError(lang, err) { - err.lang = lang; - return err; - } - - // Polyfill NodeList.prototype[Symbol.iterator] for Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=401699 - if (typeof NodeList === 'function' && !NodeList.prototype[Symbol.iterator]) { - NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } - - // A document.ready shim - // https://github.com/whatwg/html/issues/127 - function documentReady() { - if (document.readyState !== 'loading') { - return Promise.resolve(); - } - - return new Promise(resolve => { - document.addEventListener('readystatechange', function onrsc() { - document.removeEventListener('readystatechange', onrsc); - resolve(); - }); - }); - } - - // Intl.Locale - function getDirection(code) { - const tag = code.split('-')[0]; - return ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(tag) >= 0 ? 'rtl' : 'ltr'; - } - - function prioritizeLocales(def, availableLangs, requested) { - let supportedLocale; - // Find the first locale in the requested list that is supported. - for (let i = 0; i < requested.length; i++) { - const locale = requested[i]; - if (availableLangs.indexOf(locale) !== -1) { - supportedLocale = locale; - break; - } - } - if (!supportedLocale || supportedLocale === def) { - return [def]; - } - - return [supportedLocale, def]; - } - - function getMeta(head) { - let availableLangs = Object.create(null); - let defaultLang = null; - let appVersion = null; - - // XXX take last found instead of first? - const metas = head.querySelectorAll('meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"]'); - for (let meta of metas) { - const name = meta.getAttribute('name'); - const content = meta.getAttribute('content').trim(); - switch (name) { - case 'availableLanguages': - availableLangs = getLangRevisionMap(availableLangs, content); - break; - case 'defaultLanguage': - const [lang, rev] = getLangRevisionTuple(content); - defaultLang = lang; - if (!(lang in availableLangs)) { - availableLangs[lang] = rev; - } - break; - case 'appVersion': - appVersion = content; - } - } - - return { - defaultLang, - availableLangs, - appVersion - }; - } - - function getLangRevisionMap(seq, str) { - return str.split(',').reduce((seq, cur) => { - const [lang, rev] = getLangRevisionTuple(cur); - seq[lang] = rev; - return seq; - }, seq); - } - - function getLangRevisionTuple(str) { - const [lang, rev] = str.trim().split(':'); - // if revision is missing, use NaN - return [lang, parseInt(rev)]; - } - - function negotiateLanguages(fn, appVersion, defaultLang, availableLangs, additionalLangs, prevLangs, requestedLangs) { - - const allAvailableLangs = Object.keys(availableLangs).concat(additionalLangs || []).concat(Object.keys(pseudo)); - const newLangs = prioritizeLocales(defaultLang, allAvailableLangs, requestedLangs); - - const langs = newLangs.map(code => ({ - code: code, - src: getLangSource(appVersion, availableLangs, additionalLangs, code) - })); - - if (!arrEqual(prevLangs, newLangs)) { - fn(langs); - } - - return langs; - } - - function arrEqual(arr1, arr2) { - return arr1.length === arr2.length && arr1.every((elem, i) => elem === arr2[i]); - } - - function getMatchingLangpack(appVersion, langpacks) { - for (let i = 0, langpack; langpack = langpacks[i]; i++) { - if (langpack.target === appVersion) { - return langpack; - } - } - return null; - } - - function getLangSource(appVersion, availableLangs, additionalLangs, code) { - if (additionalLangs && additionalLangs[code]) { - const lp = getMatchingLangpack(appVersion, additionalLangs[code]); - if (lp && (!(code in availableLangs) || parseInt(lp.revision) > availableLangs[code])) { - return 'extra'; - } - } - - if (code in pseudo && !(code in availableLangs)) { - return 'pseudo'; - } - - return 'app'; - } - - let Remote = (function () { - function Remote(fetchResource, broadcast, requestedLangs) { - _classCallCheck(this, Remote); - - this.fetchResource = fetchResource; - this.broadcast = broadcast; - this.ctxs = new Map(); - this.interactive = documentReady().then(() => this.init(requestedLangs)); - } - - _createClass(Remote, [{ - key: 'init', - value: function init(requestedLangs) { - const meta = getMeta(document.head); - this.defaultLanguage = meta.defaultLang; - this.availableLanguages = meta.availableLangs; - this.appVersion = meta.appVersion; - - this.env = new Env(this.defaultLanguage, (...args) => this.fetchResource(this.appVersion, ...args)); - - return this.requestLanguages(requestedLangs); - } - }, { - key: 'registerView', - value: function registerView(view, resources) { - return this.interactive.then(() => { - this.ctxs.set(view, this.env.createContext(resources)); - return true; - }); - } - }, { - key: 'unregisterView', - value: function unregisterView(view) { - return this.ctxs.delete(view); - } - }, { - key: 'resolveEntities', - value: function resolveEntities(view, langs, keys) { - return this.ctxs.get(view).resolveEntities(langs, keys); - } - }, { - key: 'formatValues', - value: function formatValues(view, keys) { - return this.languages.then(langs => this.ctxs.get(view).resolveValues(langs, keys)); - } - }, { - key: 'resolvedLanguages', - value: function resolvedLanguages() { - return this.languages; - } - }, { - key: 'requestLanguages', - value: function requestLanguages(requestedLangs) { - return changeLanguages.call(this, getAdditionalLanguages(), requestedLangs); - } - }, { - key: 'getName', - value: function getName(code) { - return pseudo[code].name; - } - }, { - key: 'processString', - value: function processString(code, str) { - return pseudo[code].process(str); - } - }, { - key: 'handleEvent', - value: function handleEvent(evt) { - return changeLanguages.call(this, evt.detail || getAdditionalLanguages(), navigator.languages); - } - }]); - - return Remote; - })(); - - function getAdditionalLanguages() { - if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) { - return navigator.mozApps.getAdditionalLanguages().catch(() => []); - } - - return Promise.resolve([]); - } - - function changeLanguages(additionalLangs, requestedLangs) { - const prevLangs = this.languages || []; - return this.languages = Promise.all([additionalLangs, prevLangs]).then(([additionalLangs, prevLangs]) => negotiateLanguages(this.broadcast.bind(this, 'translateDocument'), this.appVersion, this.defaultLanguage, this.availableLanguages, additionalLangs, prevLangs, requestedLangs)); - } - - // match the opening angle bracket (<) in HTML tags, and HTML entities like - // &, &, &. - const reOverlay = /<|&#?\w+;/; - - const 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'], - // value is special-cased in isAttrAllowed - input: ['alt', 'placeholder'], - menuitem: ['label'], - menu: ['label'], - optgroup: ['label'], - option: ['label'], - track: ['label'], - img: ['alt'], - textarea: ['placeholder'], - th: ['abbr'] - } - }; - - function overlayElement(element, translation) { - const value = translation.value; - - if (typeof value === 'string') { - if (!reOverlay.test(value)) { - element.textContent = value; - } else { - // start with an inert template element and move its children into - // `element` but such that `element`'s own children are not replaced - const tmpl = element.ownerDocument.createElement('template'); - tmpl.innerHTML = value; - // overlay the node with the DocumentFragment - overlay(element, tmpl.content); - } - } - - for (let key in translation.attrs) { - const attrName = camelCaseToDashed(key); - if (isAttrAllowed({ name: attrName }, element)) { - element.setAttribute(attrName, translation.attrs[key]); - } - } - } - - // The goal of overlay is to move the children of `translationElement` - // into `sourceElement` such that `sourceElement`'s own children are not - // replaced, but onle have their text nodes and their attributes modified. - // - // We want to make it possible for localizers to apply text-level semantics to - // the translations and make use of HTML entities. At the same time, we - // don't trust translations so we need to filter unsafe elements and - // attribtues out and we don't want to break the Web by replacing elements to - // which third-party code might have created references (e.g. two-way - // bindings in MVC frameworks). - function overlay(sourceElement, translationElement) { - const result = translationElement.ownerDocument.createDocumentFragment(); - let k, attr; - - // take one node from translationElement at a time and check it against - // the allowed list or try to match it with a corresponding element - // in the source - let childElement; - while (childElement = translationElement.childNodes[0]) { - translationElement.removeChild(childElement); - - if (childElement.nodeType === childElement.TEXT_NODE) { - result.appendChild(childElement); - continue; - } - - const index = getIndexOfType(childElement); - const sourceChild = getNthElementOfType(sourceElement, childElement, index); - if (sourceChild) { - // there is a corresponding element in the source, let's use it - overlay(sourceChild, childElement); - result.appendChild(sourceChild); - continue; - } - - if (isElementAllowed(childElement)) { - const sanitizedChild = childElement.ownerDocument.createElement(childElement.nodeName); - overlay(sanitizedChild, childElement); - result.appendChild(sanitizedChild); - continue; - } - - // otherwise just take this child's textContent - result.appendChild(translationElement.ownerDocument.createTextNode(childElement.textContent)); - } - - // clear `sourceElement` and append `result` which by this time contains - // `sourceElement`'s original children, overlayed with translation - sourceElement.textContent = ''; - sourceElement.appendChild(result); - - // if we're overlaying a nested element, translate the allowed - // attributes; top-level attributes are handled in `translateElement` - // XXX attributes previously set here for another language should be - // cleared if a new language doesn't use them; https://bugzil.la/922577 - if (translationElement.attributes) { - for (k = 0, attr; attr = translationElement.attributes[k]; k++) { - if (isAttrAllowed(attr, sourceElement)) { - sourceElement.setAttribute(attr.name, attr.value); - } - } - } - } - - // XXX the allowed list should be amendable; https://bugzil.la/922573 - function isElementAllowed(element) { - return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1; - } - - function isAttrAllowed(attr, element) { - const attrName = attr.name.toLowerCase(); - const tagName = element.tagName.toLowerCase(); - // is it a globally safe attribute? - if (allowed.attributes.global.indexOf(attrName) !== -1) { - return true; - } - // are there no allowed attributes for this element? - if (!allowed.attributes[tagName]) { - return false; - } - // is it allowed on this element? - // XXX the allowed list should be amendable; https://bugzil.la/922573 - if (allowed.attributes[tagName].indexOf(attrName) !== -1) { - return true; - } - // special case for value on inputs with type button, reset, submit - if (tagName === 'input' && attrName === 'value') { - const type = element.type.toLowerCase(); - if (type === 'submit' || type === 'button' || type === 'reset') { - return true; - } - } - return false; - } - - // Get n-th immediate child of context that is of the same type as element. - // XXX Use querySelector(':scope > ELEMENT:nth-of-type(index)'), when: - // 1) :scope is widely supported in more browsers and 2) it works with - // DocumentFragments. - function getNthElementOfType(context, element, index) { - /* jshint boss:true */ - let nthOfType = 0; - for (let i = 0, child; child = context.children[i]; i++) { - if (child.nodeType === child.ELEMENT_NODE && child.tagName === element.tagName) { - if (nthOfType === index) { - return child; - } - nthOfType++; - } - } - return null; - } - - // Get the index of the element among siblings of the same type. - function getIndexOfType(element) { - let index = 0; - let child; - while (child = element.previousElementSibling) { - if (child.tagName === element.tagName) { - index++; - } - } - return index; - } - - function camelCaseToDashed(string) { - // XXX workaround for https://bugzil.la/1141934 - if (string === 'ariaValueText') { - return 'aria-valuetext'; - } - - return string.replace(/[A-Z]/g, function (match) { - return '-' + match.toLowerCase(); - }).replace(/^-/, ''); - } - - const reHtml = /[&<>]/g; - const htmlEntities = { - '&': '&', - '<': '<', - '>': '>' - }; - - function getResourceLinks(head) { - return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'), el => el.getAttribute('href')); - } - - function setAttributes(element, id, args) { - element.setAttribute('data-l10n-id', id); - if (args) { - element.setAttribute('data-l10n-args', JSON.stringify(args)); - } - } - - function getAttributes(element) { - return { - id: element.getAttribute('data-l10n-id'), - args: JSON.parse(element.getAttribute('data-l10n-args')) - }; - } - - function getTranslatables(element) { - const nodes = Array.from(element.querySelectorAll('[data-l10n-id]')); - - if (typeof element.hasAttribute === 'function' && element.hasAttribute('data-l10n-id')) { - nodes.push(element); - } - - return nodes; - } - - function translateMutations(view, langs, mutations) { - const targets = new Set(); - - for (let mutation of mutations) { - switch (mutation.type) { - case 'attributes': - targets.add(mutation.target); - break; - case 'childList': - for (let addedNode of mutation.addedNodes) { - if (addedNode.nodeType === addedNode.ELEMENT_NODE) { - if (addedNode.childElementCount) { - getTranslatables(addedNode).forEach(targets.add.bind(targets)); - } else { - if (addedNode.hasAttribute('data-l10n-id')) { - targets.add(addedNode); - } - } - } - } - break; - } - } - - if (targets.size === 0) { - return; - } - - translateElements(view, langs, Array.from(targets)); - } - - function _translateFragment(view, langs, frag) { - return translateElements(view, langs, getTranslatables(frag)); - } - - function getElementsTranslation(view, langs, elems) { - const keys = elems.map(elem => { - const id = elem.getAttribute('data-l10n-id'); - const args = elem.getAttribute('data-l10n-args'); - return args ? [id, JSON.parse(args.replace(reHtml, match => htmlEntities[match]))] : id; - }); - - return view._resolveEntities(langs, keys); - } - - function translateElements(view, langs, elements) { - return getElementsTranslation(view, langs, elements).then(translations => applyTranslations(view, elements, translations)); - } - - function applyTranslations(view, elems, translations) { - view._disconnect(); - for (let i = 0; i < elems.length; i++) { - overlayElement(elems[i], translations[i]); - } - view._observe(); - } - - const observerConfig = { - attributes: true, - characterData: false, - childList: true, - subtree: true, - attributeFilter: ['data-l10n-id', 'data-l10n-args'] - }; - - const readiness = new WeakMap(); - - let View = (function () { - function View(client, doc) { - _classCallCheck(this, View); - - this._doc = doc; - this.pseudo = { - 'fr-x-psaccent': createPseudo(this, 'fr-x-psaccent'), - 'ar-x-psbidi': createPseudo(this, 'ar-x-psbidi') - }; - - this._interactive = documentReady().then(() => init(this, client)); - - const observer = new MutationObserver(onMutations.bind(this)); - this._observe = () => observer.observe(doc, observerConfig); - this._disconnect = () => observer.disconnect(); - - const translateView = langs => translateDocument(this, langs); - client.on('translateDocument', translateView); - this.ready = this._interactive.then(client => client.method('resolvedLanguages')).then(translateView); - } - - _createClass(View, [{ - key: 'requestLanguages', - value: function requestLanguages(langs, global) { - return this._interactive.then(client => client.method('requestLanguages', langs, global)); - } - }, { - key: '_resolveEntities', - value: function _resolveEntities(langs, keys) { - return this._interactive.then(client => client.method('resolveEntities', client.id, langs, keys)); - } - }, { - key: 'formatValue', - value: function formatValue(id, args) { - return this._interactive.then(client => client.method('formatValues', client.id, [[id, args]])).then(values => values[0]); - } - }, { - key: 'formatValues', - value: function formatValues(...keys) { - return this._interactive.then(client => client.method('formatValues', client.id, keys)); - } - }, { - key: 'translateFragment', - value: function translateFragment(frag) { - return this._interactive.then(client => client.method('resolvedLanguages')).then(langs => _translateFragment(this, langs, frag)); - } - }]); - - return View; - })(); - - View.prototype.setAttributes = setAttributes; - View.prototype.getAttributes = getAttributes; - - function createPseudo(view, code) { - return { - getName: () => view._interactive.then(client => client.method('getName', code)), - processString: str => view._interactive.then(client => client.method('processString', code, str)) - }; - } - - function init(view, client) { - view._observe(); - return client.method('registerView', client.id, getResourceLinks(view._doc.head)).then(() => client); - } - - function onMutations(mutations) { - return this._interactive.then(client => client.method('resolvedLanguages')).then(langs => translateMutations(this, langs, mutations)); - } - - function translateDocument(view, langs) { - const html = view._doc.documentElement; - - if (readiness.has(html)) { - return _translateFragment(view, langs, html).then(() => setAllAndEmit(html, langs)); - } - - const translated = - // has the document been already pre-translated? - langs[0].code === html.getAttribute('lang') ? Promise.resolve() : _translateFragment(view, langs, html).then(() => setLangDir(html, langs)); - - return translated.then(() => { - setLangs(html, langs); - readiness.set(html, true); - }); - } - - function setLangs(html, langs) { - const codes = langs.map(lang => lang.code); - html.setAttribute('langs', codes.join(' ')); - } - - function setLangDir(html, langs) { - const code = langs[0].code; - html.setAttribute('lang', code); - html.setAttribute('dir', getDirection(code)); - } - - function setAllAndEmit(html, langs) { - setLangDir(html, langs); - setLangs(html, langs); - html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated', { - bubbles: false, - cancelable: false - })); - } - - const remote = new Remote(fetchResource, broadcast, navigator.languages); - window.addEventListener('languagechange', remote); - document.addEventListener('additionallanguageschange', remote); - - document.l10n = new View(new Client(remote), document); -})(); -// a-z -// a-z -// A-Z -// a-f -// A-F \ No newline at end of file diff --git a/controllets/filters-controllet/filters-controllet.html b/controllets/filters-controllet/filters-controllet.html index ab96ea9..4af0cdd 100755 --- a/controllets/filters-controllet/filters-controllet.html +++ b/controllets/filters-controllet/filters-controllet.html @@ -256,23 +256,21 @@ ready : function() { $(this.$.panel).perfectScrollbar(); -// this._showFiltersPanel(); +// this._showFiltersPanel();gi + this.logicalOperator = "AND"; }, attached : function() { - this.logicalOperator = this.filters[0].logicalOperator; - this._translate(); }, setFields : function(fields) { -// this.fields = this._copy(fields); - this.fields = fields.slice(); + this.fields = this._copy(fields); this.fields = fields; }, setFilters : function(filters) { - this.filters = filters.slice(); + this.filters = this._copy(filters); this.logicalOperator = this.filters[0].logicalOperator; this._fire(); }, @@ -416,15 +414,15 @@ return this.filters[0].logicalOperator; }, -// _copy : function(o) { -// var out, v, key; -// out = Array.isArray(o) ? [] : {}; -// for (key in o) { -// v = o[key]; -// out[key] = (typeof v === "object") ? this._copy(v) : v; -// } -// return out; -// } + _copy : function(o) { + var out, v, key; + out = Array.isArray(o) ? [] : {}; + for (key in o) { + v = o[key]; + out[key] = (typeof v === "object") ? this._copy(v) : v; + } + return out; + } }); diff --git a/controllets/select-data-controllet/select-data-controllet.html b/controllets/select-data-controllet/select-data-controllet.html index deb5a9f..a189931 100755 --- a/controllets/select-data-controllet/select-data-controllet.html +++ b/controllets/select-data-controllet/select-data-controllet.html @@ -130,7 +130,7 @@ getData : function() { //return datatable.getData //this.fields = "*" - var data = alasql_selectData(this.data, this.fields, this.filters); + var data = alasql_QUERY(this.data, this.fields, this.filters, null, null); var converter = new DataTypeConverter(); var result = converter.inferJsonDataType(data, ["*"]); result = converter.cast(result); @@ -170,14 +170,14 @@ _updateFields : function(e) { this.selectedFields = e.detail.selectedFields; var fields = utility_getSelectedFields(this.fields, this.selectedFields); - var data = alasql_selectData(this.data, fields, this.filters); + var data = alasql_QUERY(this.data, fields, this.filters, null, null); this.$.data_table.setData(data); }, _updateFilters : function(e) { this.filters = e.detail.filters; var fields = utility_getSelectedFields(this.fields, this.selectedFields); - var data = alasql_selectData(this.data, fields, this.filters); + var data = alasql_QUERY(this.data, fields, this.filters, null, null); this.$.data_table.setData(data); }, @@ -201,7 +201,7 @@ result = converter.cast(result); that.fields = utility_getFields(result.types); data = result.dataset; - that.data = alasql_selectData(data, that.fields); + that.data = alasql_QUERY(data, that.fields, null, null, null); that.$.select_fields.setFields(that.fields); that.$.filters.setFields(that.fields); @@ -222,7 +222,7 @@ result = converter.cast(result); this.fields = utility_getFields(result.types); data = result.dataset; - this.data = alasql_selectData(data, this.fields); + this.data = alasql_QUERY(data, this.fields, null, null, null); this.$.select_fields.setFields(this.fields); this.$.filters.setFields(this.fields); diff --git a/controllets/select-visualization-controllet/demo/index.html b/controllets/select-visualization-controllet/demo/index.html index f2b25b2..6188783 100755 --- a/controllets/select-visualization-controllet/demo/index.html +++ b/controllets/select-visualization-controllet/demo/index.html @@ -25,7 +25,7 @@ - + diff --git a/controllets/select-visualization-controllet/select-visualization-controllet.html b/controllets/select-visualization-controllet/select-visualization-controllet.html index ac4cb64..f435202 100755 --- a/controllets/select-visualization-controllet/select-visualization-controllet.html +++ b/controllets/select-visualization-controllet/select-visualization-controllet.html @@ -359,14 +359,14 @@ for (var key in params) { this.params[key] = params[key]; } //use cache - var data = alasql_complexSelectData(this.data, this.selectedFields, [], inputs.getAggregators(), inputs.getOrders()); + var data = alasql_QUERY(this.data, this.selectedFields, null, inputs.getAggregators(), inputs.getOrders()); var converter = new DataTypeConverter(); var result = converter.inferJsonDataType(data, ["*"]); result = converter.cast(result); data = result.dataset; - data = transformData(data, this.selectedFields, true); + data = alasql_transformData(data, this.selectedFields, true); this.params["data"] = JSON.stringify(data).replace(/'/g, "'"); // diff --git a/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior.js b/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior.js index b502641..beb406f 100755 --- a/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior.js +++ b/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior.js @@ -83,9 +83,19 @@ var AjaxJsonAlasqlBehavior = { * @method selectData */ selectData : function() { - var fields = this._component.fields = JSON.parse(this._component.fields); + var f = Object.create(providerFactory); + var provider = f.getProvider(this._component.dataUrl); + var data = provider.selectData(this.properties.json_results.value); - //var selectedFields = JSON.parse(this._component.getAttribute("selectedFields")); + var converter = new DataTypeConverter(); + + var result = converter.inferJsonDataType(data, ["*"]); + result = converter.cast(result); + this.data = result.dataset; + }, + + filterData : function() { + var fields = this._component.fields = JSON.parse(this._component.fields); var filters = JSON.parse(this._component.getAttribute("filters")); var aggregators = JSON.parse(this._component.getAttribute("aggregators")); @@ -98,49 +108,33 @@ var AjaxJsonAlasqlBehavior = { orders = orders[0]; } - var f = Object.create(providerFactory); - var provider = f.getProvider(this._component.dataUrl); - var data = provider.selectData(this.properties.json_results.value); - - //if(selectedFields) { - // fields = []; - // var inputs = []; - // for (var i=0; i < selectedFields.length; i++) { - // if (selectedFields[i]) { - // fields.push(selectedFields[i].field); - // inputs.push(selectedFields[i].input); - // } - // } - //} - var converter = new DataTypeConverter(); + var data = alasql_QUERY(this.data, fields, filters, null, orders); var result = converter.inferJsonDataType(data, ["*"]); result = converter.cast(result); data = result.dataset; - data = alasql_selectData(data, fields, filters);//funziona senza? - - result = converter.inferJsonDataType(data, ["*"]); - result = converter.cast(result); - data = result.dataset; - - data = alasql_complexSelectData(data, fields, [], aggregators, orders); - - result = converter.inferJsonDataType(data, ["*"]); - result = converter.cast(result); - data = result.dataset; - - this.data = transformData(data, fields, true); - - this._deleteWaitImage(); - }, + if(aggregators && aggregators.length) { + data = alasql_QUERY(data, fields, null, aggregators, orders); + result = converter.inferJsonDataType(data, ["*"]); + result = converter.cast(result); + data = result.dataset; + } - /** - * Delete a image after loading a datalet - */ - _deleteWaitImage : function() { - $("img[src$='spin.svg']").remove(); + this.data = alasql_transformData(data, fields, true); } + //var selectedFields = JSON.parse(this._component.getAttribute("selectedFields")); + //if(selectedFields) { + // fields = []; + // var inputs = []; + // for (var i=0; i < selectedFields.length; i++) { + // if (selectedFields[i]) { + // fields.push(selectedFields[i].field); + // inputs.push(selectedFields[i].input); + // } + // } + //} + }; \ No newline at end of file diff --git a/datalets/base-datalet/static/js/WorkcycleBehavior.js b/datalets/base-datalet/static/js/WorkcycleBehavior.js index c78d1a0..7734c15 100755 --- a/datalets/base-datalet/static/js/WorkcycleBehavior.js +++ b/datalets/base-datalet/static/js/WorkcycleBehavior.js @@ -87,7 +87,11 @@ var WorkcycleBehavior = { this.selectData(); this.filterData(); this.transformData(); - this.presentData(); + var that = this; + this._component.async(function () { + that.presentData(); + $("img[src$='spin.svg']").remove(); + }, 100); }, /** @@ -102,12 +106,12 @@ var WorkcycleBehavior = { this.requestData(); }else{ this.data = this._component.data; - - this.filterData(); this.transformData(); - this.presentData(); - - this._deleteWaitImage(); + var that = this; + this._component.async(function () { + that.presentData(); + $("img[src$='spin.svg']").remove(); + }, 100); } } diff --git a/datalets/highcharts-datalet/highcharts-datalet.html b/datalets/highcharts-datalet/highcharts-datalet.html index ec10497..3503ccc 100755 --- a/datalets/highcharts-datalet/highcharts-datalet.html +++ b/datalets/highcharts-datalet/highcharts-datalet.html @@ -61,7 +61,6 @@ Example : - @@ -69,75 +68,15 @@ Example : var HighchartsBehavior = { properties: { - categories: { type: Array, value: [] }, - series: { type: Array, value: [] - }, - -// series_type:{ -// type: String, -// value: "line"//spline,time -// } - + } }, - /** - * Normalizes a number in agreement with javascript's conventions. Delete all NaN characters. Exception: number representing lat & long remain unchanged. - */ -// jNumConverter: function(num) { -// //lat-long -// if(num.charAt(num.length-7) == "." && (num.match(/[\.,]/g) || []).length == 1) -// return num; -// -// num = num.replace(/[^0-9\.]/, ''); -// -// var jNum = ""; -// for (var i = 0; i < num.length; i++) { -// if(num[i].match(/[\.,]/g)) -// if (i == num.length - 3) -// jNum += "."; -// else -// ; -// else if (!isNaN(num[i])) -// jNum += num[i]; -// } -// -// return jNum; -// }, - /** - * Populate the categories and the series array. - * - * @method transformData - * - * @param {Event} e - */ -// transformData: function () { -// -// if(this.data.length == 0) return; -// -// this.properties.categories.value = this.data[0].data; -// -// for (var i = 1; i < this.data.length; i++) -// { -// this.data[i].data.every(function (element, index, array) { -// try { -// var e = HighchartsBehavior.jNumConverter(element); -// (isNaN(element)) ? array[index] = parseFloat(HighchartsBehavior.jNumConverter(element)) : -// array[index] = parseFloat(element); -// }catch(e){ -// //console.log("Parsing data error. Highchart-datalet.selectData"); -// } -// return true; -// }); -// -// this.properties.series.value.push(this.data[i]); -// } -// }, transformData: function () { var selectedFields = JSON.parse(this._component.getAttribute("selectedFields")); @@ -183,29 +122,7 @@ Example : this.properties.series.value = series; } - }, - - -// setParameters: function(params) -// { -// this._component.title = params['title']; -// this._component.description = params['description']; -// -// var chart = $(this._component.$.charts.$.container).highcharts(); -// -// chart.setTitle({text: params['title']}); -// chart.xAxis[0].setTitle({text: params['x-axis-label']}); -// chart.yAxis[0].setTitle({text: params['y-axis-label']}); -// -// chart.tooltip.options.formatter = function() { -// return this.x + '

' + this.series.name + ': ' + this.y + ' ' + params['suffix'] + ''; -// } -// -// //theme default -// Highcharts.setOptions(Highcharts[params['theme']]); -// -// this._component.stack = params['stack']; -// } + } }; -- libgit2 0.21.4