diff --git a/alasql-utility/alasql-utility.js b/alasql-utility/alasql-utility.js
index 6c530d5..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 [];
 
@@ -80,7 +61,7 @@ function _alasql_WHERE (filters) {
     for (var i=0; i < filters.length; i++) {
         if(filters[i]["operation"] == "contains")
             where += filters[i]["field"] + " like '%" + filters[i]["value"] + "%' " + logicalOperator + " ";
-        else if(filters[i]["operation"] == "not contains")
+        else if(filters[i]["operation"] == "notContains")
             where += filters[i]["field"] + " not like '%" + filters[i]["value"] + "%' " + logicalOperator + " ";
         else if(filters[i]["operation"] == "start")
             where += filters[i]["field"] + " like '" + filters[i]["value"] + "%' " + logicalOperator + " ";
@@ -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 <l10n-drivers@mozilla.org>",
-    "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 <l10n-drivers@mozilla.org>",
-    "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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;',
-  };
-
-  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 (&#x202a;)
-      // 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 (&#x202a;)
-    // 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
-// &amp;, &#0038;, &#x0026;.
-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 = {
-  '&': '&amp;',
-  '<': '&lt;',
-  '>': '&gt;',
-};
-
-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 (&#x202a;)
-      // 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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;',
-  };
-
-  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 (&#x202a;)
-    // 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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;',
-  };
-
-  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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;',
-  };
-
-  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 (&#x202a;)
-      // 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 (&#x202a;)
-    // 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 (&#x202a;)
-      // 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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;',
-  };
-
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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 = {
-  '&': '&amp;',
-  '<': '&lt;',
-  '>': '&gt;'
-};
-
-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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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<c.length;e++){var f=c[e];if(-1!==b.indexOf(f)){d=f;break}}return d&&d!==a?[d,a]:[a]}function z(a){for(var b=Object.create(null),c=null,d=null,e=a.querySelectorAll('meta[name="availableLanguages"],meta[name="defaultLanguage"],meta[name="appVersion"]'),f=e,g=Array.isArray(f),h=0,f=g?f:f[Symbol.iterator]();;){var i;if(g){if(h>=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;d<b.length;d++)I(b[d],c[d]);a._observe()}function Y(a,b){return{getName:function(){return a._interactive.then(function(a){return a.method("getName",b)})},processString:function(c){return a._interactive.then(function(a){return a.method("processString",b,c)})}}}function Z(a,b){return a._observe(),b.method("registerView",b.id,P(a._doc.head)).then(function(){return b})}function $(a){var b=this;return this._interactive.then(function(a){return a.method("resolvedLanguages")}).then(function(c){return T(b,c,a)})}function _(a,b){var c=a._doc.documentElement;if(Aa.has(c))return U(a,b,c).then(function(){return ca(c,b)});var d=b[0].code===c.getAttribute("lang")?Promise.resolve():U(a,b,c).then(function(){return ba(c,b)});return d.then(function(){aa(c,b),Aa.set(c,!0)})}function aa(a,b){var c=b.map(function(a){return a.code});a.setAttribute("langs",c.join(" "))}function ba(a,b){var c=b[0].code;a.setAttribute("lang",c),a.setAttribute("dir",x(c))}function ca(a,b){ba(a,b),aa(a,b),a.parentNode.dispatchEvent(new CustomEvent("DOMRetranslated",{bubbles:!1,cancelable:!1}))}var da=function(){function c(d){_classCallCheck(this,c),this.id=this,this.remote=d;var e={};this.on=function(){for(var a=arguments.length,c=Array(a),d=0;a>d;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<this._length;){try{this.getEntry()}catch(a){if(!(a instanceof e))throw a;if(this.getJunkEntry(),!this.emit)throw a}this._index<this._length&&this.getWS()}return this.entries},getEntry:function(){if("<"===this._source[this._index]){++this._index;var a=this.getIdentifier();return"["===this._source[this._index]?(++this._index,this.getEntity(a,this.getItemList(this.getExpression,"]"))):this.getEntity(a)}if(this._source.startsWith("/*",this._index))return this.getComment();throw this.error("Invalid entry")},getEntity:function(a,b){if(!this.getRequiredWS())throw this.error("Expected white space");var c=this._source[this._index],d=this.getValue(c,void 0===b),e=void 0;if(void 0===d){if(">"===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;e<d.length;e++){var f=d[e];if(!this.patterns.comment.test(f)){for(;this.patterns.multiline.test(f)&&e<d.length;)f=f.slice(0,-1)+d[++e].trim();var g=f.match(this.patterns.entity);if(g)try{this.parseEntity(g[1],g[2],c)}catch(h){if(!this.emit)throw h}}}return c},parseEntity:function(a,b,c){var d,e,f=a.indexOf("[");-1!==f?(d=a.substr(0,f),e=a.substring(f+1,a.length-1)):(d=a,e=null);var g=d.split(".");if(g.length>2)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;f<b.length;f++)0!==b[f].length&&(f%2===1?c.push({type:"idOrVar",name:b[f]}):c.push(b[f]));return c},unescapeString:function(a){return-1!==a.lastIndexOf("\\")&&(a=a.replace(this.patterns.controlChars,"$1")),a.replace(this.patterns.unicode,function(a,b){return String.fromCodePoint(parseInt(b,16))})},parseIndex:function(a){var b=a.match(this.patterns.index);if(!b)throw new e("Malformed index");return b[2]?[{type:"call",expr:{type:"prop",expr:{type:"glob",name:"cldr"},prop:"plural",cmpt:!1},args:[{type:"idOrVar",name:b[2]}]}]:[{type:"idOrVar",name:b[1]}]},error:function(a){var b=arguments.length<=1||void 0===arguments[1]?"parsererror":arguments[1],c=new e(a);return this.emit&&this.emit(b,c),c}},ja=["plural"],ka=2500,la="⁨",ma="⁩",na=new WeakSet,oa={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},pa={0:function(){return"other"},1:function(a){return o(a%100,3,10)?"few":0===a?"zero":o(a%100,11,99)?"many":2===a?"two":1===a?"one":"other"},2:function(a){return 0!==a&&a%10===0?"many":2===a?"two":1===a?"one":"other"},3:function(a){return 1===a?"one":"other"},4:function(a){return o(a,0,1)?"one":"other"},5:function(a){return o(a,0,2)&&2!==a?"one":"other"},6:function(a){return 0===a?"zero":a%10===1&&a%100!==11?"one":"other"},7:function(a){return 2===a?"two":1===a?"one":"other"},8:function(a){return o(a,3,6)?"few":o(a,7,10)?"many":2===a?"two":1===a?"one":"other"},9:function(a){return 0===a||1!==a&&o(a%100,1,19)?"few":1===a?"one":"other"},10:function(a){return o(a%10,2,9)&&!o(a%100,11,19)?"few":a%10!==1||o(a%100,11,19)?"other":"one"},11:function(a){return o(a%10,2,4)&&!o(a%100,12,14)?"few":a%10===0||o(a%10,5,9)||o(a%100,11,14)?"many":a%10===1&&a%100!==11?"one":"other"},12:function(a){return o(a,2,4)?"few":1===a?"one":"other"},13:function(a){return o(a%10,2,4)&&!o(a%100,12,14)?"few":1!==a&&o(a%10,0,1)||o(a%10,5,9)||o(a%100,12,14)?"many":1===a?"one":"other"},14:function(a){return o(a%100,3,4)?"few":a%100===2?"two":a%100===1?"one":"other"},15:function(a){return 0===a||o(a%100,2,10)?"few":o(a%100,11,19)?"many":1===a?"one":"other"},16:function(a){return a%10===1&&11!==a?"one":"other"},17:function(a){return 3===a?"few":0===a?"zero":6===a?"many":2===a?"two":1===a?"one":"other"},18:function(a){return 0===a?"zero":o(a,0,2)&&0!==a&&2!==a?"one":"other"},19:function(a){return o(a,2,10)?"few":o(a,0,1)?"one":"other"},20:function(a){return!o(a%10,3,4)&&a%10!==9||o(a%100,10,19)||o(a%100,70,79)||o(a%100,90,99)?a%1e6===0&&0!==a?"many":a%10!==2||n(a%100,[12,72,92])?a%10!==1||n(a%100,[11,71,91])?"other":"one":"two":"few"},21:function(a){return 0===a?"zero":1===a?"one":"other"},22:function(a){return o(a,0,1)||o(a,11,99)?"one":"other"},23:function(a){return o(a%10,1,2)||a%20===0?"one":"other"},24:function(a){return o(a,3,10)||o(a,13,19)?"few":n(a,[2,12])?"two":n(a,[1,11])?"one":"other"}},qa=function(){function a(b){_classCallCheck(this,a),this._env=b,this._numberFormatters=null}return a.prototype._formatTuple=function(a,b,c,d,e){try{return h(this,a,b,c)}catch(f){return f.id=e?d+"::"+e:d,f.lang=a,this._env.emit("resolveerror",f,this),[{error:f},f.id]}},a.prototype._formatEntity=function(a,b,c,d){var e=this._formatTuple(a,b,c,d),f=e[1],g={value:f,attrs:null};if(c.attrs){g.attrs=Object.create(null);for(var h in c.attrs){var i=this._formatTuple(a,b,c.attrs[h],d,h),j=i[1];g.attrs[h]=j}}return g},a.prototype._formatValue=function(a,b,c,d){return this._formatTuple(a,b,c,d)[1]},a.prototype.fetch=function(a){if(0===a.length)return Promise.resolve(a);var b=Array.from(this._env._resLists.get(this));return Promise.all(b.map(this._env._getResource.bind(this._env,a[0]))).then(function(){return a})},a.prototype._resolve=function(a,b,c,d){var f=this,g=a[0];if(!g)return q.call(this,b,c,d);var h=!1,i=b.map(function(a,b){if(d&&void 0!==d[b])return d[b];var i=Array.isArray(a)?a:[a,void 0],j=i[0],k=i[1],l=f._getEntity(g,j);return l?c.call(f,g,k,l,j):(f._env.emit("notfounderror",new e('"'+j+'" not found in '+g.code,j,g),f),void(h=!0))});return h?this.fetch(a.slice(1)).then(function(a){return f._resolve(a,b,c,i)}):i},a.prototype.resolveEntities=function(a,b){var c=this;return this.fetch(a).then(function(a){return c._resolve(a,b,c._formatEntity)})},a.prototype.resolveValues=function(a,b){var c=this;return this.fetch(a).then(function(a){return c._resolve(a,b,c._formatValue)})},a.prototype._getEntity=function(a,b){for(var c=this._env._resCache,d=Array.from(this._env._resLists.get(this)),f=0,g=void 0;g=d[f];f++){var h=c.get(g+a.code+a.src);if(!(h instanceof e)&&b in h)return h[b]}return void 0},a.prototype._getNumberFormatter=function(a){if(this._numberFormatters||(this._numberFormatters=new Map),!this._numberFormatters.has(a)){var b=Intl.NumberFormat(a,{useGrouping:!1});return this._numberFormatters.set(a,b),b}return this._numberFormatters.get(a)},a.prototype._getMacro=function(a,b){switch(b){case"plural":return p(a.code);default:return void 0}},a}(),ra=Object.defineProperties(Object.create(null),{"fr-x-psaccent":{enumerable:!0,get:t("fr-x-psaccent","Runtime Accented")},"ar-x-psbidi":{enumerable:!0,get:t("ar-x-psbidi","Runtime Bidi")}}),sa={properties:ia,l20n:ga},ta=function(){function d(e,f){_classCallCheck(this,d),this.defaultLang=e,this.fetchResource=f,this._resLists=new Map,this._resCache=new Map;var g={};this.emit=a.bind(this,g),this.addEventListener=b.bind(this,g),this.removeEventListener=c.bind(this,g)}return d.prototype.createContext=function(a){var b=new qa(this);return this._resLists.set(b,new Set(a)),b},d.prototype.destroyContext=function(a){var b=this,c=this._resLists,d=c.get(a);c.delete(a),d.forEach(function(a){return u(b._resCache,c,a)})},d.prototype._parse=function(a,b,c){var d=this,e=sa[a];if(!e)return c;var f=function(a,c){return d.emit(a,v(b,c))};return e.parse.call(e,f,c)},d.prototype._create=function(a,b){if("pseudo"!==a.src)return b;var c=Object.create(null);for(var d in b)c[d]=r(b[d],ra[a.code].process);return c},d.prototype._getResource=function(a,b){var c=this,d=this._resCache,e=b+a.code+a.src;if(d.has(e))return d.get(e);var f=b.substr(b.lastIndexOf(".")+1),g=function(b){var g=c._parse(f,a,b);d.set(e,c._create(a,g))},h=function(b){b.lang=a,c.emit("fetcherror",b),d.set(e,b)},i="pseudo"===a.src?{code:this.defaultLang,src:"app"}:a,j=this.fetchResource(b,i).then(g,h);return d.set(e,j),j},d}();"function"!=typeof NodeList||NodeList.prototype[Symbol.iterator]||(NodeList.prototype[Symbol.iterator]=Array.prototype[Symbol.iterator]);var ua=function(){function a(b,c,d){var e=this;_classCallCheck(this,a),this.fetchResource=b,this.broadcast=c,this.ctxs=new Map,this.interactive=w().then(function(){return e.init(d)})}return a.prototype.init=function(a){var b=this,c=z(document.head);return this.defaultLanguage=c.defaultLang,this.availableLanguages=c.availableLangs,this.appVersion=c.appVersion,this.env=new ta(this.defaultLanguage,function(){for(var a=arguments.length,c=Array(a),d=0;a>d;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={"&":"&amp;","<":"&lt;",">":"&gt;"},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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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 (&#x202a;)
-      // 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 (&#x202a;)
-    // 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
-// &amp;, &#0038;, &#x0026;.
-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 = {
-  '&': '&amp;',
-  '<': '&lt;',
-  '>': '&gt;'
-};
-
-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 (&#x202a;)
-      // 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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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 (&#x202a;)
-    // 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 (&#x202a;)
-      // 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
-  // &amp;, &#0038;, &#x0026;.
-  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 = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;'
-  };
-
-  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/data-table-controllet/data-table-controllet.html b/controllets/data-table-controllet/data-table-controllet.html
index 4d6d7b8..9729380 100644
--- a/controllets/data-table-controllet/data-table-controllet.html
+++ b/controllets/data-table-controllet/data-table-controllet.html
@@ -45,6 +45,13 @@
                 cursor: pointer;
             }
 
+            paper-icon-button.warning {
+                height: 24px;
+                width: 24px;
+                color: #FFEB3B;
+                cursor: help;
+            }
+
             paper-icon-button.order:hover {
                color: #FFFFFF;
             }
@@ -99,7 +106,8 @@
                 height: 24px;
                 padding: 12px;
                 text-align: center;
-                max-width: 224px;
+                max-width: 256px;
+                min-width: 156px;
                 overflow: hidden;
                 white-space: nowrap;
                 text-overflow: ellipsis;
@@ -113,18 +121,26 @@
                 cursor: help;
             }
 
+            #data_table_container th,
+            #data_table_container th * {
+                line-height: 22px;/**/
+            }
+
+            #data_table_container th .th_label {
+                display: inline-block;
+                max-width: calc(100% - 48px - 8px);
+                vertical-align: middle;
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;
+                line-height: 24px;/**/
+            }
+
             #data_table_container td {
                 font-size: 14px;
                 padding: 4px;
             }
 
-            #data_table_container tfoot td {
-                height: 48px;
-                padding: 0px;
-                font-size: 16px;
-                background: #B6B6B6;
-            }
-
             #data_table_container #footer {
                 width: 100%;
                 height: 48px;
@@ -185,7 +201,12 @@
 
         <paper-material id="data_table_container" elevation="5">
 
-            <div id="header"><span id="selected_data"></span></div>
+            <div id="header">
+                <span id="selected_data"></span>
+                <template is="dom-if" if={{hasWarnings}}>
+                    <paper-icon-button class="warning" icon="warning"></paper-icon-button>
+                </template>
+            </div>
 
             <paper-tooltip id="tooltip_wornings" for="header" offset="56">
                 <template is="dom-repeat" items="{{fields}}">
@@ -202,7 +223,10 @@
                     <tr>
                         <template is="dom-repeat" items="{{fields}}">
                             <th id="id_{{index}}">
-                                {{item.name}}
+                                <div class="th_label">{{item.name}}</div>
+                                <template is="dom-if" if={{item.errorsDescription.length}}>
+                                    <paper-icon-button class="warning" icon="warning"></paper-icon-button>
+                                </template>
                                 <paper-icon-button id="{{index}}" class="order" on-click="_order" icon="unfold-more"></paper-icon-button>
                             </th>
                         </template>
@@ -319,7 +343,6 @@
                 var result = converter.inferJsonDataType(data, ["*"]);
                 result = converter.cast(result);
                 this.fields = ArrayUtils.toFieldsArray(result.types);
-//                console.log(this.fields);
                 this.data = result.dataset;
 
                 this.filter = "";
@@ -333,11 +356,16 @@
                 this.shownNext = Math.min(this.next, this.length);
                 this.shownData = this.data.slice(this.prev-1, this.next);
 
-                this._resize();
+                this.hasWarnings = this._hasWarnings();
 
                 this.async(function() {
-                    this.$.tooltip_wornings.show();
-                }, 100);
+                    if(this.hasWarnings)
+                        this.$.tooltip_wornings.show();
+                    else
+                        this.$.tooltip_wornings.hide();
+                }, 1);
+
+                this._resize();
             },
 
             reset : function(){
@@ -435,7 +463,6 @@
             },
 
             _sort_by : function(field, reverse, primer){
-
                 var key = primer ?
                         function(x) {return primer(x[field])} :
                         function(x) {return x[field]};
@@ -460,6 +487,13 @@
                 return "id_" + index;
             },
 
+            _hasWarnings: function() {
+                for (var i in this.fields)
+                    if (this.fields[i].errorsDescription.length)
+                        return true;
+                return false;
+            },
+
             _isObject: function(item) {
                 if(item.value instanceof Array)
                     return false;
diff --git a/controllets/document-card-controllet/spreadsheet-card-controllet.html b/controllets/document-card-controllet/spreadsheet-card-controllet.html
index 78fec9a..b19a7f9 100644
--- a/controllets/document-card-controllet/spreadsheet-card-controllet.html
+++ b/controllets/document-card-controllet/spreadsheet-card-controllet.html
@@ -109,6 +109,10 @@
                 min-width: 30px;
                 padding: 5px;
             }
+            
+            div#es-function-icon{
+                display: none;
+            }
 
         </style>
 
diff --git a/controllets/filters-controllet/filters-controllet.html b/controllets/filters-controllet/filters-controllet.html
index 646d2cc..5fb42a5 100755
--- a/controllets/filters-controllet/filters-controllet.html
+++ b/controllets/filters-controllet/filters-controllet.html
@@ -73,6 +73,7 @@
 
             paper-item {
                 min-width: 128px;
+                white-space: nowrap;
             }
 
             paper-item.iron-selected {
@@ -208,7 +209,7 @@
                     <template is="dom-if" if="{{index}}"><!--excludes logicalOperator-->
                         <div class="row2">
                             <div class="filter">
-                                {{item.field}}&nbsp;&nbsp;&nbsp;<span class="highlighted">{{item.operation}}</span>&nbsp;&nbsp;&nbsp;"{{item.value}}"&nbsp;&nbsp;&nbsp;<span class="highlighted">{{_getLogicalOperator(index)}}</span>
+                                {{item.field}}&nbsp;&nbsp;&nbsp;<span class="highlighted">{{_getOperationlName(item.operation)}}</span>&nbsp;&nbsp;&nbsp;"{{item.value}}"&nbsp;&nbsp;&nbsp;<span class="highlighted">{{_getLogicalOperator(index)}}</span>
                             </div>
                             <div class="remove_container">
                                 <paper-icon-button on-click="_deleteFilter" icon="cancel" class="cancel"></paper-icon-button>
@@ -238,7 +239,7 @@
 
                 operations : {
                     type : Array,
-                    value : ["=", "!=", ">", ">=", "<", "<=", "contains", "not contains", "start", "ends"]
+                    value : ["=", "!=", ">", ">=", "<", "<=", "contains", "notContains", "start", "ends"]
                 },
 
                 filters : {
@@ -255,25 +256,25 @@
 
             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.logicalOperator = this.filters[0].logicalOperator;
-                this._fire();
+                if(filters && filters.length > 0) {
+                    this.filters = this._copy(filters);
+                    this.logicalOperator = this.filters[0].logicalOperator;
+                    this._fire();
+                }
             },
 
             getFilters : function() {
@@ -364,15 +365,16 @@
             },
 
             _getOperationlName: function(operation) {
-                if(operation.indexOf("not") > -1)
-                    return ln["notContains_" + ln["localization"]];
-                if(operation.indexOf("contains") > -1)
-                    return ln["contains_" + ln["localization"]];
-                if(operation.indexOf("start") > -1)
-                    return ln["start_" + ln["localization"]];
-                if(operation.indexOf("ends") > -1)
-                    return ln["ends_" + ln["localization"]];
-                return operation;
+                return ln[operation + "_" + ln["localization"]];
+//                if(operation.indexOf("not") > -1)
+//                    return ln["notContains_" + ln["localization"]];
+//                if(operation.indexOf("contains") > -1)
+//                    return ln["contains_" + ln["localization"]];
+//                if(operation.indexOf("start") > -1)
+//                    return ln["start_" + ln["localization"]];
+//                if(operation.indexOf("ends") > -1)
+//                    return ln["ends_" + ln["localization"]];
+//                return operation;
             },
 
 //            _changeLogicalOperator : function() {
@@ -414,15 +416,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/providers-utility-controllet/providers-utility-controllet.html b/controllets/providers-utility-controllet/providers-utility-controllet.html
index 03a98d2..d72280e 100644
--- a/controllets/providers-utility-controllet/providers-utility-controllet.html
+++ b/controllets/providers-utility-controllet/providers-utility-controllet.html
@@ -9,7 +9,7 @@
         getProvider: function(dataUrl) {
             if (dataUrl.indexOf("datastore_search?resource_id") > -1)
                 return new ckan_Provider();
-            else if (dataUrl.indexOf("search?dataset") > -1)
+            else if (dataUrl.indexOf("search?dataset") > -1 || dataUrl.indexOf("search/?dataset") > -1)
                 return new openDataSoft_Provider();
             else
                 return new generic_Provider();
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-data-controllet/select-data-controllet_old.html b/controllets/select-data-controllet/select-data-controllet_old.html
deleted file mode 100755
index 5faabb6..0000000
--- a/controllets/select-data-controllet/select-data-controllet_old.html
+++ /dev/null
@@ -1,206 +0,0 @@
-<link rel="import" href="../../bower_components/polymer/polymer.html" />
-
-<link rel="import" href="../../bower_components/paper-material/paper-material.html" />
-
-<link rel="import" href="../tree-view-controllet/tree-view-controllet.html" />
-<link rel="import" href="../multi-table-controllet/multi-table-controllet.html" />
-<link rel="import" href="../filters-controllet/filters-controllet.html" />
-
-<dom-module id="select-data-controllet">
-
-    <style is="custom-style">
-        .div_container {
-            display: flex;
-            flex-direction: row;
-        }
-        #material_tree_view {
-            position: relative;
-            width: 25%;
-            min-width: 200px;
-            height: 100vh;
-        }
-        #material_multi_table_filters {
-            position: relative;
-            width: 75%;
-
-            margin-left: 64px;
-            margin-top: 8px;
-        }
-
-        #div_multi_table {
-            height: 70%;
-            width: 100%;
-            position: relative;
-        }
-        #div_filters {
-            height: 30%;
-            width: 100%;
-            position: relative;
-        }
-    </style>
-
-    <template>
-
-        <div class="div_container">
-            <paper-material id="material_tree_view" elevation="5">
-                <tree-view-controllet id="tree_view" root-name={{rootName}} opened-path={{openedPath}} preselected-fields={{preselectedFields}}></tree-view-controllet>
-            </paper-material>
-            <paper-material id="material_multi_table_filters" elevation="5">
-                <div id="div_multi_table">
-                    <multi-table-controllet id="multi_table" data-url={{dataUrl}}></multi-table-controllet>
-                </div>
-                <div id="div_filters">
-                    <filters-controllet id="filters"></filters-controllet>
-                </div>
-            </paper-material>
-        </div>
-
-    </template>
-
-    <script>
-        Polymer({
-
-            is : 'select-data-controllet',
-
-            properties : {
-
-                rootName : {
-                    type  : String,
-                    value : "root"
-                },
-
-                preselectedFields : {
-                    type : Array,
-                    value : []
-                },
-
-                openedPath : {
-                    type : String,
-                    value : undefined
-                },
-
-                dataUrl : {
-                    type  : String,
-                    value : undefined,
-                    observer : '_init'
-                },
-
-                filters : {
-                    type : Array,
-                    value : []
-                },
-
-            },
-
-            listeners: {
-                'tree-view-controllet_selected-fields': '_updateSelectedFields',
-                'filters-controllet_filters': '_updateFilters'
-            },
-
-            ready : function() {
-                $(this.$.material_tree_view).perfectScrollbar();
-            },
-
-            attached : function(){
-                this._resize();
-                var that = this;
-                window.addEventListener("resize", function() { that._resize(); });
-
-                if(this.dataletPreset && this.dataletPreset["filters"] != undefined)
-                    this._preselectFilters();
-            },
-
-            _preselectFilters : function() {
-                this.async(function() {
-                    this.filters = JSON.parse(this.dataletPreset["filters"]);
-                    this.dataletPreset["filters"] = undefined;
-                    this.$.filters.filters = this.filters;
-                    this.$.multi_table.filters = this.filters;
-                    this._refreshTables();
-                }, 1);
-            },
-
-            getFilters : function() {
-                return this.$.filters.getFilters();
-            },
-
-            getFields : function() {
-                return this.$.tree_view.getFields();
-            },
-
-            getFlatFields : function() {
-                return this.$.tree_view.getFlatFields();
-            },
-
-            _init : function() {
-                var that = this;
-
-                $.ajax({
-                    url: this.dataUrl,
-                    dataType: "json",
-                    success: function(data){
-                        that.rootName = "data";
-                        var data = that._filterJson(data);
-                        that.$.tree_view.setAttribute("json-data", JSON.stringify(data));
-                        that.$.tree_view.selectedFields = [];
-                        that._updateSelectedFields();
-                        that.$.tree_view.ready();
-                        that.filters = [];
-                        that.$.filters.filters = [];
-                        that.$.multi_table.filters = [];
-                    }
-                });
-
-                this.$.multi_table.ready();
-            },
-
-            _filterJson : function(data){
-                //ckan
-                if(data.result != undefined && data.result.resource_id != undefined) {
-                    this.rootName = "result,records"
-                    return data.result.records;
-                }
-                //openDataSoft
-                else if(data.parameters != undefined && data.parameters.dataset != undefined) {
-                    this.rootName = "records,fields";
-                    return data.records[0].fields;
-                }
-                else{
-                    return data;
-                }
-            },
-
-            _updateSelectedFields : function() {
-                var fields = this.$.tree_view.getFields();
-                this.$.multi_table.setSelectedFields(fields);
-
-                var flatFields = this.$.tree_view.getFlatFields();
-                this.$.filters.setFields(flatFields);
-            },
-
-            _updateFilters : function(e) {
-                this.filters = e.detail.filters;
-                this.$.multi_table.filters = e.detail.filters;
-                this._refreshTables();
-            },
-
-            _refreshTables : function() {
-                this.$.multi_table.setSelectedFields([]);
-                this.async(function () {
-                    var fields = this.$.tree_view.getFields();
-                    this.$.multi_table.setSelectedFields(fields);
-                }, 0);
-            },
-
-            _resize : function(){
-                var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 16;
-                h = h - 64; //height with page scroller
-                $("#material_tree_view").height(h);
-                $("#material_multi_table_filters").height(h-8);
-            }
-
-        });
-
-    </script>
-
-</dom-module>
\ No newline at end of file
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 @@
 <body>
 
 <!--<select-visualization-controllet fields='["records,fields,annee", "records,fields,preteur", "records,fields,capital_restant_du"]' data-url="https://data.issy.com/api/records/1.0/search/?dataset=repartitiondeladetteparpreteursau3112n-feuille1&rows=56&sort=-annee" deep-url="http://172.16.15.38/DEEalerProvider/DEEP/" datalets-list-url="http://172.16.15.38/DEEalerProvider/DEEP/datalets-list"></select-visualization-controllet>-->
-<select-visualization-controllet fields='["result,records,x", "result,records,y", "result,records,cat"]' data-url="http://ckan.routetopa.eu/api/action/datastore_search?resource_id=b056c5e6-76af-4526-a35d-7dee664fb6ee&limit=99999" deep-url="http://172.16.15.38/DEEalerProvider/DEEP/" datalets-list-url="http://172.16.15.38/DEEalerProvider/DEEP/datalets-list"></select-visualization-controllet>
+<select-visualization-controllet fields='["x", "y", "cat"]' data-url="http://ckan.routetopa.eu/api/action/datastore_search?resource_id=b056c5e6-76af-4526-a35d-7dee664fb6ee&limit=99999" deep-url="http://172.16.15.38/DEEalerProvider/DEEP/" datalets-list-url="http://172.16.15.38/DEEalerProvider/DEEP/datalets-list"></select-visualization-controllet>
 <!--<select-datalet-inputs_series></select-datalet-inputs_series>-->
 <!--<select-datalet-options></select-datalet-options>-->
 
diff --git a/controllets/select-visualization-controllet/select-datalet-inputs_series_original.html b/controllets/select-visualization-controllet/select-datalet-inputs_series_original.html
deleted file mode 100644
index 9eda2da..0000000
--- a/controllets/select-visualization-controllet/select-datalet-inputs_series_original.html
+++ /dev/null
@@ -1,453 +0,0 @@
-<link rel="import" href="../../bower_components/polymer/polymer.html" />
-
-<link rel="import" href="../../bower_components/paper-material/paper-material.html" />
-
-<link rel="import" href="../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
-<link rel="import" href="../../bower_components/paper-menu/paper-menu.html">
-<link rel="import" href="../../bower_components/paper-item/paper-item.html">
-
-<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
-<link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
-<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
-
-<dom-module id="select-datalet-inputs_series">
-
-    <template>
-
-        <style is="custom-style">
-            :host {
-            --paper-dropdown-menu-icon: {
-                 color: #2196F3;
-             };
-            }
-
-            #inputs_series_container {
-                font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-                font-size: 16px;
-
-                height: 100%;
-                width: 258px;
-            }
-
-            .inputs{
-                position: relative;
-            }
-
-            .input_header {
-                height: 32px;
-                padding-top: 16px;
-                text-align: center;
-                font-weight: 700;
-                background-color: #B6B6B6;
-            }
-
-            #expert_container {
-                display: none;
-            }
-
-            #expert_header {
-                height: 32px;
-                padding-top: 16px;
-                margin-top: 8px;
-                text-align: center;
-                font-weight: 700;
-                color: #00BCD4;
-                cursor: pointer;
-            }
-
-            .info_button {
-                position: absolute;
-                top: 18px;
-                right: 0px;
-            }
-
-            paper-dropdown-menu {
-                height: 48px;
-                width: 210px;;
-                padding-left: 8px;
-                padding-bottom: 8px;
-                --paper-input-container-focus-color: #2196F3;
-            }
-
-            paper-item.iron-selected {
-                background-color: #2196F3;
-                color: #FFFFFF;
-            }
-
-            paper-icon-button {
-                color: #2196F3;
-                --paper-icon-button-ink-color: #2196F3;
-                margin: 0px;
-            }
-        </style>
-
-            <paper-material id="inputs_series_container" elevation="5">
-
-                <div class="input_header"><span id="inputs"></span></div>
-
-                <template is="dom-repeat" items="{{inputs}}" index-as="ddl_index">
-                    <div class="inputs">
-                        <paper-dropdown-menu id={{ddl_index}} label={{_getLabelName(item.name)}}>
-                            <paper-menu class="dropdown-content">
-                                <template is="dom-repeat" items={{fields}}>
-                                    <paper-item id={{index}} on-tap="_addInput">{{_fieldName(item)}}</paper-item>
-                                </template>
-                            </paper-menu>
-                        </paper-dropdown-menu>
-                        <div class="info_button">
-                            <paper-icon-button id="{{ddl_index}}" on-click="_setOrders" icon="unfold-more" title=""></paper-icon-button>
-                        </div>
-                    </div>
-                </template>
-
-                <div id="expert_header" on-click="_showExpertMode"><span id="groupBy"></span></div>
-
-                <div id="expert_container">
-
-                    <div class="inputs">
-                        <paper-dropdown-menu id="group_by" disabled>
-                            <paper-menu class="dropdown-content">
-                                <template is="dom-repeat" items={{groupableFields}}>
-                                    <paper-item id={{index}} on-tap="_setAggregatorsFields">{{_fieldName(item)}}</paper-item>
-                                </template>
-                            </paper-menu>
-                        </paper-dropdown-menu>
-                        <div class="info_button">
-                            <paper-icon-button id="gb" on-click="_setOrders" icon="unfold-more" title="" disabled></paper-icon-button>
-                        </div>
-                    </div>
-
-                    <template is="dom-repeat" items="{{aggregateFields}}" as="field">
-                        <div class="inputs">
-                            <paper-dropdown-menu id="calculate_{{index}}" label="{{_calculateLabel()}}" disabled>
-                                <paper-menu class="dropdown-content">
-                                    <template is="dom-repeat" items="{{functions}}">
-                                        <paper-item id={{index}} on-tap="_setAggregators">{{_operationName(index)}} : {{_fieldName(field)}}</paper-item>
-                                    </template>
-                                </paper-menu>
-                            </paper-dropdown-menu>
-                        </div>
-                    </template>
-
-                </div>
-
-            </paper-material>
-
-
-    </template>
-
-    <script>
-
-        Polymer({
-
-            is : 'select-datalet-inputs_series',
-
-            properties : {
-
-                //INPUT
-                fields : {type : Array, value : []},
-
-                //OUTPUT
-                selectedFields : {type : Array, value : ["", ""]},
-                aggregators : {type : Array, value : []},
-                orders : {type : Array, value : []},
-
-                expert : {
-                    type  : Boolean,
-                    value : false
-                },
-
-                functions : {
-                    type  : Array,
-                    value : ["COUNT", "SUM", "MIN", "MAX", "AVG", "FIRST", "LAST"]
-                },
-
-                inputs : {
-                    type : Array,
-                    value : []
-                },
-
-                groupableFields : {
-                    type : Array,
-                    value : []
-                },
-
-                aggregateFields : {
-                    type : Array,
-                    value : [""]
-                }
-
-            },
-
-            ready : function() {
-                $(this.$.inputs_series_container).perfectScrollbar();
-            },
-
-            attached : function() {
-                this._translate();
-            },
-
-            _preselectInputs : function(fields, aggregators, orders) {
-                var ddls = this.$.inputs_series_container.querySelectorAll("paper-dropdown-menu");
-
-//                this.async(function () {
-                    for (var i = 0; i < fields.length; i++) {
-                        var menu = $(ddls[i]).find("paper-menu")[0];
-                        menu.select(i+1);
-                    }
-                    this.selectedFields = this._copy(fields);
-//                }, 0);
-            },
-
-            getSelectedFields : function () {
-                var gb_menu = $(this.$.group_by).find("paper-menu")[0];
-                var c_menu = $("#calculate_0").find("paper-menu")[0];
-
-                if(gb_menu.selectedItem && c_menu.selectedItem && gb_menu.selectedItem.id != 0) {
-                    var groupByField = this.groupableFields[gb_menu.selected];
-                    if (groupByField != this.selectedFields[0])
-                        return [this.selectedFields[0], groupByField, this.selectedFields[1]];
-                }
-                return this.selectedFields;
-            },
-
-            getAggregators : function () {
-                return this.aggregators;
-            },
-
-            getOrders : function () {
-                return this.orders;
-            },
-
-            setFields : function(fields) {
-                var temp = this._copy(fields);
-                temp.unshift("");
-
-                if (JSON.stringify(this.fields) != JSON.stringify(temp)) {
-                    this.fields = this._copy(temp);
-                    this._reset();
-                }
-            },
-
-            setInputs : function(inputs) {
-                this.inputs = this._copy(inputs);
-            },
-
-            fireReady : function() {
-                if(this.selectedFields[0] != "" && this.selectedFields[1] != "")
-                    this.fire('select_visualization_inputs_ready', {isReady: true});
-                else
-                    this.fire('select_visualization_inputs_ready', {isReady: false});
-            },
-
-            _reset : function() {
-                //this.fields = []; --> setFields
-                //this.inputs = []; --> setInputs
-                this.selectedFields = ["", ""];
-                this.aggregators = [];
-                //this.orders = []; --> _resetOrders
-                this.expert = true;
-                this._showExpertMode();//expert --> false
-                this.groupableFields = [];
-                this.aggregateFields = [""];
-
-                this.async(function () {
-                    //reset ddl
-                    var ddls = this.$.inputs_series_container.getElementsByTagName("paper-dropdown-menu");
-                    for (var i = 0; i < ddls.length; i++)
-                        $(ddls[i]).find("paper-menu")[0].select(-1);
-                    this.$.group_by.setAttribute("disabled", "");
-                    $("#calculate_0")[0].setAttribute("disabled", "");
-                    //reset orders
-                    this._resetOrders();
-                }, 1);
-            },
-
-            _translate : function(){
-                this.$.inputs.innerHTML = ln["inputs_" + ln["localization"]];
-                this.$.groupBy.innerHTML = ln["expertGroupBy_" + ln["localization"]];
-
-                this.$.group_by.setAttribute("label", ln["groupBy_" + ln["localization"]]);
-            },
-
-            _showExpertMode : function() {
-                if(!this.expert) {
-                    this.$.expert_header.style.color = "#000000";
-                    this.$.expert_header.style.background = "#B6B6B6";
-                    this.$.expert_container.style.display = "block";
-                }
-                else {
-                    this.$.expert_header.style.color = "#00BCD4";
-                    this.$.expert_header.style.background = "#FFFFFF";
-                    this.$.expert_container.style.display = "none";
-                }
-
-                this.expert = !this.expert;
-            },
-
-            _addInput : function(e){
-                var selectedFields = this._copy(this.selectedFields);
-
-                var ddl_index = $(e.target).parents("paper-dropdown-menu")[0].id;
-                var index = e.target.id;
-                selectedFields[ddl_index] = this.fields[index];
-
-                this.selectedFields = this._copy(selectedFields);
-
-                if(this.selectedFields[0] == "" || this.selectedFields[1] == "") {
-                    this.$.group_by.setAttribute("disabled", "");
-                    $("#calculate_0")[0].setAttribute("disabled", "");
-
-                    this.groupableFields = [];
-                    this.aggregateFields = [""];
-                    var menu = $("#calculate_0").find("paper-menu")[0];
-                    menu.select(-1);
-                }
-                else {
-                    this.$.group_by.removeAttribute("disabled");
-                    $("#calculate_0")[0].removeAttribute("disabled");
-
-                    var groupableFields = [];
-                    for (var i = 0; i < this.fields.length; i++) {
-                        if(this.fields[i] != this.selectedFields[1])
-                            groupableFields.push(this.fields[i]);
-                    }
-                    this.groupableFields = this._copy(groupableFields);
-                    this.aggregateFields = [this.selectedFields[1]];
-
-                    this.async(function () {
-                        var menu = $(this.$.group_by).find("paper-menu")[0];
-                        menu.select(-1);
-                        var menu = $("#calculate_0").find("paper-menu")[0];
-                        //var selected =  menu.selected;
-                        menu.select(-1);
-                        //menu.select(selected);
-                    }, 0);
-                }
-                this._setAggregators();
-            },
-
-            _setAggregatorsFields : function(){
-                this.async(function() {
-                    var c_menu = $("#calculate_0").find("paper-menu")[0];
-                    var selected =  c_menu.selected > 0 ? c_menu.selected : 0;
-                    c_menu.select(-1);
-                    c_menu.select(selected);
-
-                    this._setAggregators();
-                }, 0);
-            },
-
-            _setAggregators : function(){
-                this.async(function() {
-                    this.aggregators = [];
-
-                    this._resetOrders();
-
-                    $("#gb")[0].setAttribute("disabled", "");
-
-                    var gb_menu = $(this.$.group_by).find("paper-menu")[0];
-                    var c_menu = $("#calculate_0").find("paper-menu")[0];
-
-                    if(gb_menu.selectedItem && c_menu.selectedItem && gb_menu.selectedItem.id != 0) {
-                        var groupByField = this.groupableFields[gb_menu.selected];
-                        var operationIndex = c_menu.selectedItem.id;
-
-                        if (groupByField == this.selectedFields[0]){// X Axis field
-                            this.aggregators.push({"field": groupByField, "operation": "GROUP BY"});
-                        }
-                        else{
-                            $("#gb")[0].removeAttribute("disabled");
-                            this.aggregators.push({"field": this.selectedFields[0], "operation": "GROUP BY"});
-                            this.aggregators.push({"field": groupByField, "operation": "GROUP BY"});
-                        }
-
-                        this.aggregators.push({"field": this.selectedFields[1], "operation": this.functions[operationIndex]});
-                    }
-
-                    this.fireReady();
-                }, 0);
-            },
-
-            _setOrders : function(e){
-                var t = e.target;
-                if(t.tagName.indexOf("IRON-ICON") > -1)
-                    t = $(e.target).parents("paper-icon-button")[0];
-
-                var icon = t.getAttribute("icon");
-                var id = t.getAttribute("id");
-                var field;
-
-                if(id == "gb"){
-                    var gb_menu = $(this.$.group_by).find("paper-menu")[0];
-                    field = this.groupableFields[gb_menu.selected];
-                }
-                else {
-                    field = this.selectedFields[id];
-                }
-
-                if(icon.indexOf("unfold-more") > -1){
-                    t.setAttribute("icon", "expand-less");
-                    t.setAttribute("title", ln["sortAscending_" + ln["localization"]]);
-                    var orders = this.orders.filter(function (el) { return el.field !== field; });
-                    orders.unshift({"field": field, "operation": "ASC"});
-                    this.orders = this._copy(orders);
-                }
-                else if(icon.indexOf("expand-less") > -1){
-                    t.setAttribute("icon", "expand-more");
-                    t.setAttribute("title", ln["sortDescending_" + ln["localization"]]);
-                    var orders = this.orders.filter(function (el) { return el.field !== field; });
-                    orders.unshift({"field": field, "operation": "DESC"});
-                    this.orders = this._copy(orders);
-                }
-                else if(icon.indexOf("expand-more") > -1){
-                    t.setAttribute("icon", "unfold-more");
-                    t.setAttribute("title", ln["unsort_" + ln["localization"]]);
-                    var orders = this.orders.filter(function (el) { return el.field !== field; });
-                    this.orders = this._copy(orders);
-                }
-
-                this.fireReady();
-            },
-
-            _resetOrders : function(){
-                var icons = this.$.inputs_series_container.querySelectorAll("paper-icon-button");
-                for (var i = 0; i < icons.length; i++){
-                    icons[i].setAttribute("icon", "unfold-more");
-                    icons[i].setAttribute("title", ln["unsort_" + ln["localization"]]);
-                }
-                this.orders = [];
-            },
-
-            _fieldName : function(field) {
-                return field.substring(field.lastIndexOf(",")+1, field.length);
-            },
-
-            _getLabelName: function(key) {
-                return ln[key + "_" +ln["localization"]];
-            },
-
-            _operationName: function(index) {
-                var key = this.functions[index];
-                return ln[key + "_" +ln["localization"]];
-            },
-
-            _calculateLabel: function() {
-                return ln["calculate" + "_" +ln["localization"]];
-            },
-
-            _copy : function(o) {
-                var out, v, key;
-                out = Array.isArray(o) ? new Array(o.length) : {};
-                for (key in o) {
-                    v = o[key];
-                    out[key] = (typeof v === "object") ? this._copy(v) : v;
-                }
-                return out;
-            }
-
-        });
-
-    </script>
-
-</dom-module>
\ No newline at end of file
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, "&#39;");
                 //
diff --git a/controllets/select-visualization-controllet/select-visualization-controllet_old.html b/controllets/select-visualization-controllet/select-visualization-controllet_old.html
deleted file mode 100755
index f6cf177..0000000
--- a/controllets/select-visualization-controllet/select-visualization-controllet_old.html
+++ /dev/null
@@ -1,427 +0,0 @@
-<link rel="import" href="../../bower_components/polymer/polymer.html" />
-
-<link rel="import" href="../../bower_components/paper-material/paper-material.html" />
-
-<link rel="import" href="../../bower_components/paper-button/paper-button.html">
-
-<link rel="import" href="../items-vslider-controllet/items-vslider-controllet.html" />
-<link rel="import" href="select-datalet-inputs_series.html" />
-<link rel="import" href="select-datalet-inputs.html" />
-<link rel="import" href="select-datalet-options.html" />
-
-<script src="../../../DEEPCLIENT/js/deepClient.js"></script>
-
-<dom-module id="select-visualization-controllet">
-
-    <template>
-
-        <style is="custom-style">
-            #select_visualization_container {
-                display: flex;
-                font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-                font-size: 16px;
-                margin-top: 8px;
-            }
-
-            #items_vslider_controllet {
-                position: relative;
-                min-width: 172px;
-            }
-
-            #datalet_selection_inputs {
-                position: relative;
-                margin-left: 32px;
-                min-width: 258px;
-            }
-
-            #datalet_selection_options {
-                position: relative;
-                margin-left: 32px;
-                min-width: 258px;
-            }
-
-            #datalet_preview_container {
-                position: relative;
-                width: 100%;
-                margin-left: 32px;
-            }
-
-            #datalet_selection_datalet {
-                position: relative;
-                height: 100vh;
-                width: 100%;
-            }
-
-            #datalet_selection_datalet_placeholder {
-                padding: 16px;
-            }
-
-            .input_header {
-                height: 32px;
-                padding-top: 16px;
-                text-align: center;
-                font-weight: 700;
-                background-color: #B6B6B6;
-            }
-
-            paper-button {
-                position: absolute;
-                bottom: 16px;
-                right: 11px;
-
-                height: 48px;
-                width: 172px;
-                background-color: #00BCD4;
-                color: white;
-                font-weight: 700;
-                padding: 16px;
-            }
-
-            paper-button:hover {
-                background-color: #00AABF;
-
-                box-shadow: 0px 8px 12px #888;
-                -webkit-box-shadow: 0px 8px 12px #888;
-                -moz-box-shadow: 0px 8px 12px #888;
-            }
-
-            paper-button[disabled] {
-                background-color: #B6B6B6;
-            }
-        </style>
-
-        <iron-ajax id="selectedDatalet_request" on-response="_handleSelectedDatalet"></iron-ajax>
-
-        <div id="select_visualization_container">
-
-            <div id="items_vslider_controllet">
-                <items-vslider-controllet id="vslider" datalets-list-url={{dataletsListUrl}} preselected-datalet={{preselectedDatalet}}></items-vslider-controllet>
-            </div>
-
-            <div id="datalet_selection_inputs">
-                <template is="dom-if" if="{{_checkType(type, 'multiseries')}}">
-                    <select-datalet-inputs_series id="inputs_series"></select-datalet-inputs_series>
-                </template>
-                <template is="dom-if" if="{{_checkType(type, 'default')}}">
-                    <select-datalet-inputs id="inputs"></select-datalet-inputs>
-                </template>
-                <template is="dom-if" if="{{_checkType(type, '')}}">
-                    <select-datalet-inputs></select-datalet-inputs>
-                </template>
-            </div>
-
-            <div id="datalet_selection_options">
-                <select-datalet-options id="options"></select-datalet-options>
-            </div>
-
-            <div id="datalet_preview_container">
-
-                <paper-material id="datalet_selection_datalet" elevation="5">
-                    <div class="input_header"><span id="dataletPreview"></span></div>
-                    <div id="datalet_selection_datalet_placeholder"></div>
-                </paper-material>
-
-                <paper-button id="add_button" disabled raised on-click="_addDatalet"></paper-button>
-
-            </div>
-
-        </div>
-
-    </template>
-
-    <script>
-
-        Polymer({
-
-            is : 'select-visualization-controllet',
-
-            properties : {
-
-                deepUrl : {
-                    type : String,
-                    value : undefined
-                },
-
-                dataletsListUrl : {
-                    type : String,
-                    value : undefined
-                },
-
-                selectedDatalet : {
-                    type  : String,
-                    value : undefined
-                },
-
-                preselectedDatalet : {
-                    type  : String,
-                    value : undefined
-                },
-
-                type : {
-                    type : String,
-                    value : ""
-                },
-
-                filters : {
-                    type : Array,
-                    value : []
-                },
-
-                fields : {
-                    type : Array,
-                    value : []
-                },
-
-                selectedFields : {
-                    type : Array,
-                    value : []
-                },
-
-                dataletPreset : {
-                    type  : Object,
-                    value : []
-                },
-
-                dataUrl : {
-                    type  : String,
-                    value : undefined
-                },
-
-                params:{
-                    type: Object,
-                    value: undefined
-                },
-
-                data : {
-                    type  : Object,
-                    value : undefined
-                },
-
-            },
-
-            listeners: {
-//                'select-fields-controllet_selected-fields': '_updateFields',
-//                'filters-controllet_filters': '_updateFilters',
-                'items-vslider-controllet_selected-datalet': '_selectDatalet',
-                'select_visualization_inputs_ready': '_loadDatalet',
-                'select_visualization_options_changed': '_tryLoadDatalet'
-            },
-
-            ready : function() {
-                this._resize();
-
-                $(this.$.datalet_selection_datalet).perfectScrollbar();
-
-//                this.params = {'data-url' : this.dataUrl};//not here
-            },
-
-            attached : function() {
-                this._resize();
-                var that = this;
-                window.addEventListener("resize", function() { that._resize(); });
-
-                this._translate();
-            },
-
-            setFilters : function(filters) {
-                this.filters = this._copy(filters);
-            },
-
-            setFields : function(fields) {
-//                if (this.fields.length > 0 && JSON.stringify(this.fields) != JSON.stringify(fields))
-//                    this.init();
-
-                this.fields = this._copy(fields);
-
-                var inputs = this._getInputs();
-                if (inputs)
-                    inputs.setFields(this.fields);
-            },
-
-            setData : function(data) {
-                this.data = this._copy(data);
-            },
-
-            init : function() {
-                this.$.vslider._reset();
-            },
-
-            show : function() {//show --> preselect
-//                if(this.preselectedDatalet)
-                if(this.dataletPreset)
-                    this._preselectDatalet();
-                
-//                if (this.$.datalet_selection_datalet_placeholder.innerHTML == "") {
-//                    var inputs = this._getInputs();
-//                    inputs.fireReady();
-//                }
-            },
-
-            _translate : function(){
-                if(this.preselectedDatalet)
-                    this.$.add_button.innerHTML = ln["modifyDatalet_" + ln["localization"]];
-                else
-                    this.$.add_button.innerHTML = ln["addDatalet_" + ln["localization"]];
-
-                this.$.dataletPreview.innerHTML = ln["dataletPreview_" + ln["localization"]];
-            },
-
-            _selectDatalet : function(e){
-
-                this.$.add_button.setAttribute("disabled", "true");
-
-                this.selectedDatalet = e.detail.datalet;
-
-                this.$.datalet_selection_datalet_placeholder.innerHTML = "";
-
-                if(!this.selectedDatalet){
-                    this.type = "";
-                    this.$.options.setOptions([]);
-                }
-                else{
-                    this.$.selectedDatalet_request.url = this.deepUrl + e.detail.datalet + "-datalet";
-                    this.$.selectedDatalet_request.generateRequest();
-                }
-            },
-
-            _handleSelectedDatalet : function(e){
-
-                if(this.type != e.detail.response.type) {
-
-                    this.type = e.detail.response.type;
-
-                    this.async(function () {
-                        var inputs = this._getInputs();
-                        inputs.setFields(this.fields);
-                        inputs.setInputs(e.detail.response.idm.inputs.input);// Cannot read property '0' of undefined
-                    }, 0);
-
-                    this.$.options.setOptions(e.detail.response.idm.inputs.layouts.input);
-                }
-                else {
-                    this.$.options.checkOptions(e.detail.response.idm.inputs.layouts.input);
-                }
-
-                this.async(function () {
-                    var inputs = this._getInputs();
-                    inputs.fireReady();
-                }, 0);
-
-//                this.async(function () {
-//                    if(this.preselectedDatalet)
-//                        this._preselectDatalet();
-//                }, 1);
-            },
-
-            _preselectDatalet : function() {
-                this.$.vslider._preselectDatalet();
-
-                this.async(function () {
-                    this.$.options._preselectOptions(this.dataletPreset);
-                }, 100);
-
-                this.async(function () {
-                    var inputs = this._getInputs();
-                    inputs._preselectInputs(this.fields, this.dataletPreset["aggregators"], this.dataletPreset["orders"]);
-                    this.dataletPreset = undefined;
-                }, 200);
-            },
-
-            _tryLoadDatalet : function(){
-                var inputs = this._getInputs();
-                inputs.fireReady();
-            },
-
-            _loadDatalet : function(e){
-                if(!e.detail.isReady) {
-                    this.$.datalet_selection_datalet_placeholder.innerHTML = "";
-                    return;
-                }
-
-                var inputs = this._getInputs();
-
-                this.selectedFields = inputs.getSelectedFields();
-
-                this.params = {'data-url' : this.dataUrl};//??????????????????
-//                this.params = {};
-
-                this.params["filters"] = JSON.stringify(this.filters);
-                this.params["aggregators"] = JSON.stringify(inputs.getAggregators());
-                this.params["orders"] = JSON.stringify(inputs.getOrders());
-
-                this.params["export_menu"] = "0";
-
-                var params = this.$.options.getParams();
-                for (var key in params) { this.params[key] = params[key]; }
-
-                var data = alasql_complexSelectData(this.data, this.selectedFields, this.filters, inputs.getAggregators(), inputs.getOrders());
-                data = transformData(data, this.selectedFields, true);
-                this.params["data"] = JSON.stringify(data).replace(/'/g, "&#39;");
-
-                var dataletParams ={
-                    component: this.selectedDatalet+"-datalet",
-                    fields: this.selectedFields,
-                    params: this.params,
-                    placeHolder: this.$.datalet_selection_datalet_placeholder,
-                };
-
-                ComponentService.deep_url = this.deepUrl;
-                ComponentService.getComponent(dataletParams);
-
-                this.$.add_button.removeAttribute("disabled");
-            },
-
-            _addDatalet : function(){
-                delete this.params["export_menu"];
-
-                var data = {
-                    dataUrl: this.dataUrl,
-                    datalet: this.selectedDatalet+"-datalet",
-                    fields: this.selectedFields,
-                    params: this.params,
-                    staticData: JSON.stringify(this.$.datalet_selection_datalet_placeholder.children[1].behavior.data)
-                }
-
-                this.fire('data-sevc-controllet.dataletCreated', {data : data});
-            },
-
-            _checkType: function(type, check){
-                if (type == "multiseries" || type == "")
-                    return (type == check);
-                else if(check == "default")
-                    return true;
-                return false;
-            },
-
-            _getInputs  : function(){
-                if(this.type == "multiseries")
-                    return this.$$("#inputs_series")
-                return this.$$("#inputs");
-            },
-
-            _copy : function(o) {
-                var out, v, key;
-                out = Array.isArray(o) ? new Array(o.length) : {};
-                for (key in o) {
-                    v = o[key];
-                    out[key] = (typeof v === "object") ? this._copy(v) : v;
-                }
-                return out;
-            },
-
-            _resize : function(){
-                var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 16;
-                h = h - 64 - 8; //height with page scroller
-
-                $("#items_vslider_controllet").height(h);//vslider controllet
-                $("#datalet_selection_inputs").height(h);
-                $("#datalet_selection_options").height(h);
-
-                $("#datalet_selection_datalet").height(h);
-            }
-
-        });
-
-    </script>
-
-</dom-module>
\ No newline at end of file
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-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old.js b/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old.js
deleted file mode 100755
index 380dbb8..0000000
--- a/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old.js
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
-@license
-    The MIT License (MIT)
-
-    Copyright (c) 2015 Dipartimento di Informatica - Universit� di Salerno - Italy
-
-    Permission is hereby granted, free of charge, to any person obtaining a copy
-    of this software and associated documentation files (the "Software"), to deal
-    in the Software without restriction, including without limitation the rights
-    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-    copies of the Software, and to permit persons to whom the Software is
-    furnished to do so, subject to the following conditions:
-
-    The above copyright notice and this permission notice shall be included in
-    all copies or substantial portions of the Software.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-    THE SOFTWARE.
-*/
-
-/**
- * Developed by :
- * ROUTE-TO-PA Project - grant No 645860. - www.routetopa.eu
- *
-*/
-
-
-var AjaxJsonAlasqlBehavior = {
-
-    properties: {
-
-        /**
-         * It contains the json data from async xhr call returned from core-ajax core component
-         *
-         * @attribute json_results
-         * @type object
-         * @default 'null'.
-         */
-        json_results: {
-            type: Object,
-            value: {}
-        }
-
-    },
-
-    /**
-     * Make an AJAX call to the dataset URL
-     *
-     * @method requestData
-     */
-    requestData: function(){
-
-        var comp = this;
-
-        $.ajax({
-            url: this._component.dataUrl,
-            dataType: "json",
-            success: function(e){
-                comp.handleResponse(e);
-            }
-        });
-    },
-
-    /**
-     * Called when core-ajax component receive the json data from called url.
-     *
-     * @method handleResponse
-     */
-    handleResponse: function(e) {
-        this.properties.json_results.value = e;
-        this.runWorkcycle();
-    },
-
-    /**
-     * selectData built a JSONPATH query based on the user selected fields then extract data from the JSON response.
-     * This method built an objects <name, data> for every user selected field and push it into the data array.
-     *
-     * @method selectData
-     */
-    selectData : function() {
-        var jsonData = [this.properties.json_results.value];
-
-        this._component.fields = JSON.parse(this._component.fields);
-
-        var filters = JSON.parse(this._component.getAttribute("filters"));
-        var aggregators = JSON.parse(this._component.getAttribute("aggregators"));
-        var orders = JSON.parse(this._component.getAttribute("orders"));
-
-        //preview my space
-        if(filters && filters[0] && filters[0].constructor == Array){
-            filters = filters[0];
-            aggregators = aggregators[0];
-            orders = orders[0];
-        }
-
-        var provider = this._getProvider(this._component.fields[0]);
-
-        var path = this._path(this._component.fields[0], provider);
-
-        var fields = [];
-        for (var i=0; i < this._component.fields.length; i++)
-            fields.push(this._fieldName(this._component.fields[i], provider));
-
-        //WHERE
-        var where = "";
-        if(filters && filters.length) {
-
-            for (var i=0; i < filters.length; i++)
-                filters[i]["field"] = this._fieldName(filters[i]["field"], provider);
-
-            where = "WHERE ";
-            for (var i=0; i < filters.length; i++) {
-                if(filters[i]["operation"] == "contains")
-                    where += filters[i]["field"] + " like '%" + filters[i]["value"] + "%' AND ";
-                else if(filters[i]["operation"] == "start")
-                    where += filters[i]["field"] + " like '" + filters[i]["value"] + "%' AND ";
-                else if(filters[i]["operation"] == "ends")
-                    where += filters[i]["field"] + " like '%" + filters[i]["value"] + "' AND ";
-                else
-                    where += filters[i]["field"] + " " + filters[i]["operation"] + " " + filters[i]["value"] + " AND ";
-            }
-            where = where.slice(0, -5);
-        }
-
-        provider="";
-
-        //ORDER BY
-        var orderBy = "";
-        if(orders && orders.length) {
-
-            for (var i=0; i < orders.length; i++)
-                orders[i]["field"] = this._fieldName(orders[i]["field"], provider);
-
-            orderBy = "ORDER BY ";
-            for (var i = 0; i < orders.length; i++)
-                orderBy += orders[i]["field"] + " " + orders[i]["operation"] + ", ";
-            orderBy = orderBy.slice(0, -2);
-        }
-
-        //SELECT
-        var select = "SELECT ";
-        for (var i = 0; i < fields.length; i++)
-            select += fields[i] + " as " + this._fieldName(this._component.fields[i], "") + ", ";
-        select = select.slice(0, -2);
-
-        var pureSelect = select;
-
-        /**/
-        var res = alasql("SELECT "+ path +" FROM ?", [jsonData]);
-        var records = res[0][path];
-        var obj = alasql(pureSelect + " FROM ?", [records]);
-        //console.log(obj);
-
-        var select = "SELECT ";
-        for (var i = 0; i < fields.length; i++) {
-            var key = Object.keys(obj[0])[i];
-            var v = obj[0][key];
-            if (!isNaN(v))
-                select += fields[i] + "::NUMBER as " + this._fieldName(this._component.fields[i], "") + ", ";
-            else
-                select += fields[i] + " as " + this._fieldName(this._component.fields[i], "") + ", ";
-
-            //omg!
-            //var key2 = "["+key+"]";
-            //if(fields[0].indexOf("->") > -1)
-            //    key2 = "fields->["+key+"]";
-            //if (!isNaN(v))
-            //    select += key2 + "::NUMBER as " + this._fieldName(key, "") + ", ";
-            //else
-            //    select += key2 + " as " + this._fieldName(key, "") + ", ";
-        }
-        select = select.slice(0, -2);
-
-        var pureSelect = select;
-        /**/
-
-        //GROUP BY
-        var groupBy = "";
-        if(aggregators && aggregators.length) {
-
-            for (var i=0; i < aggregators.length; i++)
-                aggregators[i]["field"] = this._fieldName(aggregators[i]["field"], provider);
-
-            groupBy = "GROUP BY " + aggregators[0]["field"];
-            select = "SELECT "  + aggregators[0]["field"];
-            for (var i = 1; i < aggregators.length; i++)
-                select += ", " + aggregators[i]["operation"] + "(" + aggregators[i]["field"] + ") as " + aggregators[i]["field"];
-        }
-
-        //QUERY
-        console.log('SELECT '+ path +' FROM ?');
-        var res = alasql("SELECT "+ path +" FROM ?", [jsonData]);
-
-        var records = res[0][path];
-
-        //console.log(select + ' FROM ? ' + where + ' ' + groupBy + ' ' + orderBy + '');
-        //var obj = alasql(select + " FROM ? " + where + " " + groupBy + " " + orderBy + "", [records]);
-
-        console.log(pureSelect + ' FROM ? ' + where);
-        var obj = alasql(pureSelect + " FROM ? " + where, [records]);
-
-        if (groupBy != "") {
-            console.log(select + ' FROM ? ' + groupBy + ' ' + orderBy + '');
-            var obj = alasql(select + " FROM ? " + groupBy + " " + orderBy + "", [obj]);
-        }
-
-        //multiserie test
-        //var query = "SELECT [preteur], [annee] as [annee], SUM([capital_restant_du]) as [capital_restant_du] FROM ? GROUP BY [preteur], [annee]"
-        //var obj = alasql(query, [obj]);
-        //console.log(obj);
-        //this.data = obj;
-
-
-        //PUSH DATA
-        if(!obj || obj.length == 0)
-            this.data = []
-        else
-            this._pushData(obj);
-
-        this._deleteWaitImage();
-    },
-
-    _pushData : function(obj) {
-        this.data = [];
-
-        for (var key in Object.keys(obj[0])){
-
-            var name = Object.keys(obj[0])[key];
-            var data = [];
-            var value;
-
-            for (var i in obj) {
-                data.push(obj[i][name]);
-                //value = obj[i][name];
-                //if(!isNaN(value) && value != "")
-                //    value = parseFloat(obj[i][name]);
-                //data.push(value);
-            }
-
-            this.data.push({
-                name: name,
-                data: data
-            });
-        }
-    },
-
-    _getProvider : function(field) {
-        if(field.indexOf("result,records") > -1)
-            return "ckan";
-        else if(field.indexOf("records,fields") > -1)
-            return "openDataSoft";
-        else
-            return "provider";
-    },
-
-    _fieldName : function(field, provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return "[" + field.substring(field.lastIndexOf(",") + 1, field.length) + "]";
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return "fields->["+field.substring(field.lastIndexOf(",")+1, field.length)+"]";
-        }
-        else {
-            return "["+field.substring(field.lastIndexOf(",")+1, field.length)+"]";
-        }
-    },
-
-    _path : function(field, provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return "result->records"
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return "records";
-        }
-        else {
-            return field.substring(0, field.lastIndexOf(",")).replace(",", "->");
-        }
-    },
-
-    /**
-     * Delete a image after loading a datalet
-     */
-    _deleteWaitImage : function() {
-        $("img[src$='spin.svg']").remove();
-    }
-
-};
\ No newline at end of file
diff --git a/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old2.js b/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old2.js
deleted file mode 100755
index d1ea02a..0000000
--- a/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old2.js
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
-@license
-    The MIT License (MIT)
-
-    Copyright (c) 2015 Dipartimento di Informatica - Universit� di Salerno - Italy
-
-    Permission is hereby granted, free of charge, to any person obtaining a copy
-    of this software and associated documentation files (the "Software"), to deal
-    in the Software without restriction, including without limitation the rights
-    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-    copies of the Software, and to permit persons to whom the Software is
-    furnished to do so, subject to the following conditions:
-
-    The above copyright notice and this permission notice shall be included in
-    all copies or substantial portions of the Software.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-    THE SOFTWARE.
-*/
-
-/**
- * Developed by :
- * ROUTE-TO-PA Project - grant No 645860. - www.routetopa.eu
- *
-*/
-
-
-var AjaxJsonAlasqlBehavior = {
-
-    properties: {
-
-        /**
-         * It contains the json data from async xhr call returned from core-ajax core component
-         *
-         * @attribute json_results
-         * @type object
-         * @default 'null'.
-         */
-        json_results: {
-            type: Object,
-            value: {}
-        }
-
-    },
-
-    /**
-     * Make an AJAX call to the dataset URL
-     *
-     * @method requestData
-     */
-    requestData: function(){
-
-        var comp = this;
-
-        $.ajax({
-            url: this._component.dataUrl,
-            dataType: "json",
-            success: function(e){
-                comp.handleResponse(e);
-            }
-        });
-    },
-
-    /**
-     * Called when core-ajax component receive the json data from called url.
-     *
-     * @method handleResponse
-     */
-    handleResponse: function(e) {
-        this.properties.json_results.value = e;
-        this.runWorkcycle();
-    },
-
-    /**
-     * selectData built a JSONPATH query based on the user selected fields then extract data from the JSON response.
-     * This method built an objects <name, data> for every user selected field and push it into the data array.
-     *
-     * @method selectData
-     */
-    selectData : function() {
-        this._component.fields = JSON.parse(this._component.fields);
-        var provider = this._getProvider(this._component.fields[0]);
-
-        var filters = JSON.parse(this._component.getAttribute("filters"));
-        var aggregators = JSON.parse(this._component.getAttribute("aggregators"));
-        var orders = JSON.parse(this._component.getAttribute("orders"));
-
-        //preview my space
-        if(filters && filters[0] && filters[0].constructor == Array){
-            filters = filters[0];
-            aggregators = aggregators[0];
-            orders = orders[0];
-        }
-
-        var fields = [];
-        for (var i=0; i < this._component.fields.length; i++)
-            fields.push(this._fieldName(this._component.fields[i], provider));
-
-        //if(aggregators)
-        //    for (var i=0; i < aggregators.length; i++)
-        //        aggregators[i]["field"] = this._fieldName(aggregators[i]["field"], provider);
-
-        //var jsonData = [this.properties.json_results.value];
-
-        //jsdatachecker
-        var _converter = new DataTypeConverter();
-        var path2 = this._arrayPath(provider);
-        var processingResult = _converter.inferJsonDataType(this.properties.json_results.value, path2);
-        var processingResults = _converter.convert(processingResult);
-        var jsonData = [processingResults.dataset];
-
-        //WHERE
-        var where = "";
-        if(filters && filters.length) {
-
-            for (var i=0; i < filters.length; i++)
-                filters[i]["field"] = this._fieldName(filters[i]["field"], provider);
-
-            where = "WHERE ";
-            for (var i=0; i < filters.length; i++) {
-                if(filters[i]["operation"] == "contains")
-                    where += filters[i]["field"] + " like '%" + filters[i]["value"] + "%' AND ";
-                else if(filters[i]["operation"] == "start")
-                    where += filters[i]["field"] + " like '" + filters[i]["value"] + "%' AND ";
-                else if(filters[i]["operation"] == "ends")
-                    where += filters[i]["field"] + " like '%" + filters[i]["value"] + "' AND ";
-                else
-                    where += filters[i]["field"] + " " + filters[i]["operation"] + " " + filters[i]["value"] + " AND ";
-            }
-            where = where.slice(0, -5);
-        }
-
-        //ORDER BY
-        var orderBy = "";
-        if(orders && orders.length) {
-
-            for (var i=0; i < orders.length; i++)
-                orders[i]["field"] = this._fieldName(orders[i]["field"], "");
-
-            orderBy = "ORDER BY ";
-            for (var i = 0; i < orders.length; i++)
-                orderBy += orders[i]["field"] + " " + orders[i]["operation"] + ", ";
-            orderBy = orderBy.slice(0, -2);
-        }
-
-        //SELECT
-        var pureSelect = "SELECT ";
-        for (var i = 0; i < fields.length; i++)
-            pureSelect += fields[i] + " as " + this._fieldName(this._component.fields[i], "") + ", ";
-        pureSelect = pureSelect.slice(0, -2);
-
-        //GROUP BY
-        //var groupBy = "";
-        //if(aggregators && aggregators.length) {
-        //
-        //    for (var i=0; i < aggregators.length; i++)
-        //        aggregators[i]["field"] = this._fieldName(aggregators[i]["field"], provider);
-        //
-        //    groupBy = "GROUP BY " + aggregators[0]["field"];
-        //    select = "SELECT "  + aggregators[0]["field"];
-        //    for (var i = 1; i < aggregators.length; i++)
-        //        select += ", " + aggregators[i]["operation"] + "(" + aggregators[i]["field"] + ") as " + aggregators[i]["field"];
-        //}
-
-        //var groupBy = "";
-        //if(aggregators && aggregators.length) {
-        //
-        //    groupBy = "GROUP BY " + this._fieldName(aggregators[0]["field"], "");
-        //    select = "SELECT "  + this._fieldName(aggregators[0]["field"], provider) + " as " + this._fieldName(aggregators[0]["field"], "");
-        //    for (var i = 1; i < aggregators.length; i++)
-        //        select += ", " + aggregators[i]["operation"] + "(" + this._fieldName(aggregators[i]["field"], provider) + ") as " + this._fieldName(aggregators[i]["field"], "");
-        //}
-
-        //GROUP BY
-        var groupBy = "";
-        if(aggregators && aggregators.length) {
-
-            for (var i=0; i < aggregators.length; i++)
-                aggregators[i]["field"] = this._fieldName(aggregators[i]["field"], "");
-
-            groupBy = "GROUP BY " + aggregators[0]["field"];
-            var select = "SELECT "  + aggregators[0]["field"];
-            for (var i = 1; i < aggregators.length; i++)
-                select += ", " + aggregators[i]["operation"] + "(" + aggregators[i]["field"] + ") as " + aggregators[i]["field"];
-        }
-
-        //QUERY
-        var path = this._path(this._component.fields[0], provider);
-
-        console.log('SELECT '+ path +' FROM ?');
-        var res = alasql("SELECT "+ path +" FROM ?", [jsonData]);
-
-        var records = res[0][path];
-
-        //console.log(select + ' FROM ? ' + where + ' ' + groupBy + ' ' + orderBy + '');
-        //var obj = alasql(select + " FROM ? " + where + " " + groupBy + " " + orderBy + "", [records]);
-
-        console.log(pureSelect + ' FROM ? ' + where);
-        var obj = alasql(pureSelect + " FROM ? " + where, [records]);
-
-        if (groupBy != "") {
-            console.log(select + ' FROM ? ' + groupBy + ' ' + orderBy + '');
-            obj = alasql(select + " FROM ? " + groupBy + " " + orderBy + "", [obj]);
-        }
-
-        //TEST
-        //var query = "SELECT [preteur], [annee] as [annee], SUM([capital_restant_du]) as [capital_restant_du] FROM ? GROUP BY [preteur], [annee]"
-        //var obj = alasql(query, [obj]);
-        //console.log(obj);
-        //this.data = obj;
-
-        //PUSH DATA
-        if(!obj || obj.length == 0)
-            this.data = []
-        else
-            this._pushData(obj, fields);
-
-        this._deleteWaitImage();
-    },
-
-    _pushData : function(obj, keys) {
-        this.data = [];
-
-        if (typeof keys == 'undefined')
-            keys = Object.keys(obj[0]);
-
-        for (var key in keys){
-
-            var name = keys[key].replace(/(\[|\]|fields->)/g, "");
-            var data = [];
-
-            for (var i in obj) {
-                data.push(obj[i][name]);
-            }
-
-            this.data.push({
-                name: name,
-                data: data
-            });
-        }
-    },
-
-    _getProvider : function(field) {
-        if(field.indexOf("result,records") > -1)
-            return "ckan";
-        else if(field.indexOf("records,fields") > -1)
-            return "openDataSoft";
-        else
-            return "provider";
-    },
-
-    _fieldName : function(field, provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return "[" + field.substring(field.lastIndexOf(",") + 1, field.length) + "]";
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return "fields->[" + field.substring(field.lastIndexOf(",") + 1, field.length)+ "]";
-        }
-        else {
-            return "[" + field.substring(field.lastIndexOf(",") + 1, field.length) + "]";
-        }
-    },
-
-    _path : function(field, provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return "result->records"
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return "records";
-        }
-        else {
-            return field.substring(0, field.lastIndexOf(",")).replace(",", "->");
-        }
-    },
-
-    _arrayPath : function(provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return ["result", "records", "*"];
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return ["records", "fields", "*"];
-        }
-        else {
-            return ["*"];
-        }
-    },
-
-    /**
-     * Delete a image after loading a datalet
-     */
-    _deleteWaitImage : function() {
-        $("img[src$='spin.svg']").remove();
-    }
-
-};
\ No newline at end of file
diff --git a/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old3.js b/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old3.js
deleted file mode 100755
index d39ff88..0000000
--- a/datalets/base-ajax-json-alasql-datalet/static/js/AjaxJsonAlasqlBehavior_old3.js
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
-@license
-    The MIT License (MIT)
-
-    Copyright (c) 2015 Dipartimento di Informatica - Universit� di Salerno - Italy
-
-    Permission is hereby granted, free of charge, to any person obtaining a copy
-    of this software and associated documentation files (the "Software"), to deal
-    in the Software without restriction, including without limitation the rights
-    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-    copies of the Software, and to permit persons to whom the Software is
-    furnished to do so, subject to the following conditions:
-
-    The above copyright notice and this permission notice shall be included in
-    all copies or substantial portions of the Software.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-    THE SOFTWARE.
-*/
-
-/**
- * Developed by :
- * ROUTE-TO-PA Project - grant No 645860. - www.routetopa.eu
- *
-*/
-
-
-var AjaxJsonAlasqlBehavior = {
-
-    properties: {
-
-        /**
-         * It contains the json data from async xhr call returned from core-ajax core component
-         *
-         * @attribute json_results
-         * @type object
-         * @default 'null'.
-         */
-        json_results: {
-            type: Object,
-            value: {}
-        }
-
-    },
-
-    /**
-     * Make an AJAX call to the dataset URL
-     *
-     * @method requestData
-     */
-    requestData: function(){
-
-        var comp = this;
-
-        $.ajax({
-            url: this._component.dataUrl,
-            dataType: "json",
-            success: function(e){
-                comp.handleResponse(e);
-            }
-        });
-    },
-
-    /**
-     * Called when core-ajax component receive the json data from called url.
-     *
-     * @method handleResponse
-     */
-    handleResponse: function(e) {
-        this.properties.json_results.value = e;
-        this.runWorkcycle();
-    },
-
-    /**
-     * selectData built a JSONPATH query based on the user selected fields then extract data from the JSON response.
-     * This method built an objects <name, data> for every user selected field and push it into the data array.
-     *
-     * @method selectData
-     */
-    selectData : function() {
-        this._component.fields = JSON.parse(this._component.fields);
-        var provider = this._getProvider(this._component.fields[0]);
-
-        var filters = JSON.parse(this._component.getAttribute("filters"));
-        var aggregators = JSON.parse(this._component.getAttribute("aggregators"));
-        var orders = JSON.parse(this._component.getAttribute("orders"));
-
-        //preview my space
-        if(filters && filters[0] && filters[0].constructor == Array){
-            filters = filters[0];
-            aggregators = aggregators[0];
-            orders = orders[0];
-        }
-
-        var fields = [];
-        for (var i=0; i < this._component.fields.length; i++)
-            fields.push(this._fieldName(this._component.fields[i], provider));
-
-        //jsdatachecker
-        var _converter = new DataTypeConverter();
-        var path2 = this._arrayPath(provider);
-        var processingResult = _converter.inferJsonDataType(this.properties.json_results.value, path2);
-        var processingResults = _converter.cast(processingResult);
-        var jsonData = [processingResults.dataset];
-
-        //WHERE
-        var where = "";
-        if(filters && filters.length) {
-
-            for (var i=0; i < filters.length; i++)
-                filters[i]["field"] = this._fieldName(filters[i]["field"], provider);
-
-            where = "WHERE ";
-            for (var i=0; i < filters.length; i++) {
-                if(filters[i]["operation"] == "contains")
-                    where += filters[i]["field"] + " like '%" + filters[i]["value"] + "%' AND ";
-                else if(filters[i]["operation"] == "not contains")
-                    where += filters[i]["field"] + " not like '%" + filters[i]["value"] + "%' AND ";
-                else if(filters[i]["operation"] == "start")
-                    where += filters[i]["field"] + " like '" + filters[i]["value"] + "%' AND ";
-                else if(filters[i]["operation"] == "ends")
-                    where += filters[i]["field"] + " like '%" + filters[i]["value"] + "' AND ";
-                else
-                    where += filters[i]["field"] + " " + filters[i]["operation"] + " " + filters[i]["value"] + " AND ";
-            }
-            where = where.slice(0, -5);
-        }
-
-        //ORDER BY
-        var orderBy = "";
-        if(orders && orders.length) {
-
-            for (var i=0; i < orders.length; i++)
-                orders[i]["field"] = this._fieldName(orders[i]["field"], "");
-
-            orderBy = "ORDER BY ";
-            for (var i = 0; i < orders.length; i++)
-                orderBy += orders[i]["field"] + " " + orders[i]["operation"] + ", ";
-            orderBy = orderBy.slice(0, -2);
-        }
-
-        //SELECT
-        var pureSelect = "SELECT ";
-        for (var i = 0; i < fields.length; i++)
-            pureSelect += fields[i] + " as " + this._fieldName(this._component.fields[i], "") + ", ";
-        pureSelect = pureSelect.slice(0, -2);
-
-        //GROUP BY
-        var groupBy = "";
-        var select = "";
-        if(aggregators && aggregators.length) {
-
-            for (var i=0; i < aggregators.length; i++)
-                aggregators[i]["field"] = this._fieldName(aggregators[i]["field"], "");
-
-            groupBy = "GROUP BY " + aggregators[0]["field"];
-            select = "SELECT "  + aggregators[0]["field"];
-            for (var i = 1; i < aggregators.length; i++)
-                select += ", " + aggregators[i]["operation"] + "(" + aggregators[i]["field"] + ") as " + aggregators[i]["field"];
-
-            if(aggregators[1] && aggregators[1]["operation"] == "GROUP BY") {
-                groupBy = "GROUP BY " + aggregators[0]["field"] + ", " + aggregators[1]["field"];
-                select = "SELECT "  + aggregators[0]["field"] + ", " + aggregators[1]["field"];
-                for (var i = 2; i < aggregators.length; i++)
-                    select += ", " + aggregators[i]["operation"] + "(" + aggregators[i]["field"] + ") as " + aggregators[i]["field"];
-            }
-        }
-
-        //QUERY
-        var path = this._path(this._component.fields[0], provider);
-        var query;
-
-        query = "SELECT "+ path +" FROM ?";
-        //console.log(query);
-        var res = alasql(query, [jsonData]);
-
-        var records = res[0][path];
-
-        query = pureSelect + " FROM ? " + where  + " " + orderBy;
-        //console.log(query);
-        var obj = alasql(query, [records]);
-
-        if (groupBy != "") {
-            query = select + " FROM ? " + groupBy + " " + orderBy;
-            //console.log(query);
-            obj = alasql(query, [obj]);
-        }
-
-        //PUSH DATA
-        if(!obj || obj.length == 0)
-            this.data = []
-        else
-            this._pushData(obj, fields);
-
-        this._deleteWaitImage();
-    },
-
-    _pushData : function(obj, keys) {
-        this.data = [];
-
-        if (typeof keys == 'undefined')
-            keys = Object.keys(obj[0]);
-
-        for (var key in keys){
-
-            var name = keys[key].replace(/(\[|\]|fields->)/g, "");
-            var data = [];
-
-            for (var i in obj) {
-                var v = obj[i][name];
-                if(!isNaN(v) && v % 1 != 0)
-                    v = Math.floor(v * 100) / 100;
-                data.push(v);
-
-                //data.push(obj[i][name]);
-            }
-
-            this.data.push({
-                name: name,
-                data: data
-            });
-        }
-    },
-
-    _getProvider : function(field) {
-        if(field.indexOf("result,records") > -1)
-            return "ckan";
-        else if(field.indexOf("records,fields") > -1)
-            return "openDataSoft";
-        else
-            return "provider";
-    },
-
-    _fieldName : function(field, provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return "[" + field.substring(field.lastIndexOf(",") + 1, field.length) + "]";
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return "fields->[" + field.substring(field.lastIndexOf(",") + 1, field.length)+ "]";
-        }
-        else {
-            return "[" + field.substring(field.lastIndexOf(",") + 1, field.length) + "]";
-        }
-    },
-
-    _path : function(field, provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return "result->records"
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return "records";
-        }
-        else {
-            return field.substring(0, field.lastIndexOf(",")).replace(",", "->");
-        }
-    },
-
-    _arrayPath : function(provider) {
-        if(provider.indexOf("ckan") > -1) {
-            return ["result", "records", "*"];
-        }
-        else if(provider.indexOf("openDataSoft") > -1) {
-            return ["records", "fields", "*"];
-        }
-        else {
-            return ["*"];
-        }
-    },
-
-    /**
-     * Delete a image after loading a datalet
-     */
-    _deleteWaitImage : function() {
-        $("img[src$='spin.svg']").remove();
-    }
-
-};
\ No newline at end of file
diff --git a/datalets/base-ajax-json-alasql-datalet/static/js/alasql.min.js b/datalets/base-ajax-json-alasql-datalet/static/js/alasql.min.js
deleted file mode 100755
index a1cc1d1..0000000
--- a/datalets/base-ajax-json-alasql-datalet/static/js/alasql.min.js
+++ /dev/null
@@ -1,10 +0,0 @@
-(function(root,factory){if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.alasql=factory()}})(this,function(){var alasql=function alasql(sql,params,cb,scope){if(typeof importScripts!="function"&&alasql.webworker){var id=alasql.lastid++;alasql.buffer[id]=cb;alasql.webworker.postMessage({id:id,sql:sql,params:params})}else{if(arguments.length==0){return new yy.Select({columns:[new yy.Column({columnid:"*"})],from:[new yy.ParamValue({param:0})]})}else if(arguments.length==1&&typeof sql=="object"&&sql instanceof Array){var select=new yy.Select({columns:[new yy.Column({columnid:"*"})],from:[new yy.ParamValue({param:0})]});select.preparams=[sql];return select}else{if(typeof sql=="string"&&sql[0]=="#"&&typeof document=="object"){sql=document.querySelector(sql).textContent}else if(typeof sql=="object"&&sql instanceof HTMElement){sql=sql.textContent}else if(typeof sql=="function"){sql=sql.toString().slice(14,-3)}return alasql.exec(sql,params,cb,scope)}}};alasql.version="0.1.9";alasql.debug=undefined;getAlaSQLPath();function getAlaSQLPath(){alasql.path="";if(typeof importScripts==="function"){alasql.path=""}else if(typeof exports!="undefined"){alasql.path=__dirname}else if(typeof Meteor=="object"&&Meteor.isClient){alasql.path="/packages/dist/"}else if(typeof Meteor=="object"&&Meteor.isServer){alasql.path="assets/packages/dist/"}else if(typeof document!="undefined"){var sc=document.getElementsByTagName("script");for(var i=0;i<sc.length;i++){if(sc[i].src.substr(-16).toLowerCase()=="alasql-worker.js"){alasql.path=sc[i].src.substr(0,sc[i].src.length-16);break}else if(sc[i].src.substr(-20).toLowerCase()=="alasql-worker.min.js"){alasql.path=sc[i].src.substr(0,sc[i].src.length-20);break}else if(sc[i].src.substr(-9).toLowerCase()=="alasql.js"){alasql.path=sc[i].src.substr(0,sc[i].src.length-9);break}else if(sc[i].src.substr(-13).toLowerCase()=="alasql.min.js"){alasql.path=sc[i].src.substr(0,sc[i].src.length-13);break}}}}var parser=function(){var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[2,10],$V1=[1,95],$V2=[1,96],$V3=[1,6],$V4=[1,38],$V5=[1,71],$V6=[1,68],$V7=[1,87],$V8=[1,86],$V9=[1,63],$Va=[1,94],$Vb=[1,78],$Vc=[1,76],$Vd=[1,60],$Ve=[1,64],$Vf=[1,58],$Vg=[1,62],$Vh=[1,56],$Vi=[1,66],$Vj=[1,57],$Vk=[1,61],$Vl=[1,75],$Vm=[1,69],$Vn=[1,77],$Vo=[1,79],$Vp=[1,80],$Vq=[1,73],$Vr=[1,74],$Vs=[1,72],$Vt=[1,81],$Vu=[1,82],$Vv=[1,83],$Vw=[1,84],$Vx=[1,85],$Vy=[1,91],$Vz=[1,59],$VA=[1,70],$VB=[1,65],$VC=[1,89],$VD=[1,90],$VE=[1,99],$VF=[1,100],$VG=[8,264,450,451],$VH=[8,264,268,450,451],$VI=[105,304,357],$VJ=[1,112],$VK=[1,111],$VL=[1,116],$VM=[1,143],$VN=[1,153],$VO=[1,156],$VP=[1,151],$VQ=[1,159],$VR=[1,163],$VS=[1,160],$VT=[1,148],$VU=[1,150],$VV=[1,152],$VW=[1,161],$VX=[1,145],$VY=[1,170],$VZ=[1,166],$V_=[1,167],$V$=[1,171],$V01=[1,172],$V11=[1,173],$V21=[1,174],$V31=[1,175],$V41=[1,176],$V51=[1,177],$V61=[1,178],$V71=[1,179],$V81=[1,154],$V91=[1,155],$Va1=[1,157],$Vb1=[1,158],$Vc1=[1,164],$Vd1=[1,162],$Ve1=[1,165],$Vf1=[1,149],$Vg1=[1,169],$Vh1=[1,180],$Vi1=[4,5],$Vj1=[1,186],$Vk1=[1,194],$Vl1=[8,65,71,93,104,136,142,143,157,172,205,218,264,268,450,451],$Vm1=[4,5,8,65,69,70,71,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,157,159,161,172,244,245,246,247,248,249,250,251,252,264,268,368,372,450,451],$Vn1=[4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$Vo1=[1,223],$Vp1=[1,230],$Vq1=[1,240],$Vr1=[1,245],$Vs1=[1,244],$Vt1=[4,5,8,65,70,71,85,93,104,106,107,112,116,119,126,128,136,142,143,153,154,155,157,172,205,218,232,233,234,235,237,244,245,246,247,248,249,250,251,252,254,255,256,257,258,260,261,264,268,270,368,372,450,451],$Vu1=[2,140],$Vv1=[1,256],$Vw1=[8,67,71,264,268,447,450,451],$Vx1=[4,5,8,65,70,71,85,93,104,106,107,112,116,119,126,128,136,138,142,143,153,154,155,157,159,161,169,172,205,218,232,233,234,235,237,244,245,246,247,248,249,250,251,252,254,255,256,257,258,260,261,264,268,270,368,372,450,451],$Vy1=[4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,167,172,180,182,194,195,196,197,198,199,200,201,202,203,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,257,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,294,298,307,319,320,321,324,325,336,338,344,347,348,349,350,351,352,353,355,356,364,365,366,368,372,374,376,382,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$Vz1=[4,5,8,47,65,81,99,120,130,163,233,264,287,290,291,298,344,347,348,351,353,355,356,364,365,366,383,385,386,388,389,390,391,392,396,397,400,401,447,449,450,451],$VA1=[2,444],$VB1=[1,270],$VC1=[2,784],$VD1=[8,71,81,107,112,120,163,256,264,268,419,450,451],$VE1=[8,67,264,268,450,451],$VF1=[2,504],$VG1=[1,296],$VH1=[4,5,130],$VI1=[1,302],$VJ1=[1,310],$VK1=[1,309],$VL1=[1,316],$VM1=[1,307],$VN1=[1,311],$VO1=[1,308],$VP1=[1,312],$VQ1=[1,314],$VR1=[1,326],$VS1=[1,323],$VT1=[1,324],$VU1=[1,304],$VV1=[1,306],$VW1=[1,303],$VX1=[1,305],$VY1=[1,313],$VZ1=[1,315],$V_1=[1,317],$V$1=[1,318],$V02=[1,319],$V12=[1,320],$V22=[1,321],$V32=[1,325],$V42=[1,327],$V52=[1,328],$V62=[1,329],$V72=[1,330],$V82=[2,262],$V92=[1,339],$Va2=[4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,202,203,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,257,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,294,298,307,319,320,324,325,344,347,348,351,353,355,356,364,365,366,368,372,374,376,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$Vb2=[2,323],$Vc2=[1,350],$Vd2=[1,360],$Ve2=[4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,202,203,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,374,376,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$Vf2=[1,376],$Vg2=[1,384],$Vh2=[1,383],$Vi2=[4,5,8,65,67,71,93,104,136,142,143,180,182,194,195,196,197,198,199,200,201,202,203,205,218,264,268,450,451],$Vj2=[8,65,67,71,93,104,136,142,143,180,182,194,195,196,197,198,199,200,201,202,203,205,218,264,268,450,451],$Vk2=[2,178],$Vl2=[1,403],$Vm2=[8,65,71,93,104,136,142,143,157,205,218,264,268,450,451],$Vn2=[2,141],$Vo2=[1,406],$Vp2=[4,5,90],$Vq2=[1,418],$Vr2=[1,435],$Vs2=[1,417],$Vt2=[1,416],$Vu2=[1,412],$Vv2=[1,413],$Vw2=[1,414],$Vx2=[1,415],$Vy2=[1,419],$Vz2=[1,420],$VA2=[1,421],$VB2=[1,422],$VC2=[1,423],$VD2=[1,424],$VE2=[1,425],$VF2=[1,426],$VG2=[1,427],$VH2=[1,428],$VI2=[1,429],$VJ2=[1,430],$VK2=[1,431],$VL2=[1,432],$VM2=[1,434],$VN2=[1,436],$VO2=[1,437],$VP2=[1,438],$VQ2=[1,439],$VR2=[1,440],$VS2=[1,441],$VT2=[1,442],$VU2=[1,445],$VV2=[1,446],$VW2=[1,447],$VX2=[1,448],$VY2=[1,449],$VZ2=[1,450],$V_2=[1,451],$V$2=[1,452],$V03=[1,453],$V13=[1,454],$V23=[1,455],$V33=[1,456],$V43=[67,81,163],$V53=[8,67,71,128,161,257,264,268,294,307,319,320,324,325,450,451],$V63=[1,473],$V73=[8,67,71,264,268,450,451],$V83=[1,474],$V93=[1,482],$Va3=[4,5,70,106,107,112,116,119,126,128,153,154,155,232,233,234,235,237,244,245,246,247,248,249,250,251,252,254,255,256,257,258,260,261,270,368,372],$Vb3=[8,65,71,85,93,104,136,142,143,157,172,205,218,264,268,450,451],$Vc3=[4,5,107,256],$Vd3=[1,504],$Ve3=[8,67,69,71,264,268,450,451],$Vf3=[8,67,69,71,107,112,114,119,126,264,268,368,372,450,451],$Vg3=[2,785],$Vh3=[8,67,69,71,107,114,119,126,264,268,368,372,450,451],$Vi3=[8,71,81,107,120,163,256,264,268,419,450,451],$Vj3=[1,523],$Vk3=[1,524],$Vl3=[138,140,286],$Vm3=[2,395],$Vn3=[1,570],$Vo3=[1,589],$Vp3=[4,5,8,47,65,67,69,70,71,81,85,90,93,99,104,105,106,107,109,110,112,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$Vq3=[2,338],$Vr3=[1,596],$Vs3=[264,266,268],$Vt3=[67,376],$Vu3=[67,374,376],$Vv3=[1,603],$Vw3=[4,5,8,47,65,67,69,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$Vx3=[67,374],$Vy3=[8,65,71,93,104,136,142,143,205,218,264,268,450,451],$Vz3=[1,637],$VA3=[8,65,71,264,268,450,451],$VB3=[1,643],$VC3=[1,644],$VD3=[1,645],$VE3=[4,5,8,65,67,69,70,71,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,172,244,245,246,247,248,249,250,251,252,264,268,368,372,450,451],$VF3=[1,695],$VG3=[1,694],$VH3=[1,708],$VI3=[8,65,67,71,85,93,104,136,142,143,157,172,205,218,264,268,450,451],$VJ3=[1,732],$VK3=[8,67,69,71,114,119,126,264,268,368,372,450,451],$VL3=[8,67,71,114,264,268,450,451],$VM3=[8,71,81,120,163,264,268,419,450,451],$VN3=[4,5,8,47,65,67,69,70,71,81,85,90,93,99,104,105,106,107,109,110,112,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$VO3=[4,5,8,47,65,67,69,70,71,81,85,90,93,99,104,105,106,107,108,109,110,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$VP3=[4,5,8,47,65,67,69,70,71,81,85,93,99,104,105,106,107,109,110,112,114,115,116,117,119,120,122,123,124,126,130,136,138,140,142,143,144,145,146,147,149,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,271,272,273,274,275,276,277,282,283,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$VQ3=[2,359],$VR3=[4,5,8,47,65,67,69,70,71,81,85,93,104,105,106,107,109,110,112,116,117,119,120,122,123,124,126,130,136,138,140,142,143,144,145,146,147,149,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,282,283,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$VS3=[2,260],$VT3=[4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,374,376,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$VU3=[1,788],$VV3=[8,71,264,268,450,451],$VW3=[1,797],$VX3=[8,65,71,93,136,142,143,205,218,264,268,450,451],$VY3=[8,65,67,71,93,104,136,142,143,157,172,205,218,264,268,450,451],$VZ3=[4,5,65,69,70,71,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,159,161,244,245,246,247,248,249,250,251,252,368,372],$V_3=[4,5,65,67,69,70,71,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,159,161,244,245,246,247,248,249,250,251,252,368,372],$V$3=[2,724],$V04=[4,5,65,67,69,70,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,159,161,244,245,246,247,248,249,250,251,252,368,372],$V14=[1,846],$V24=[8,67,71,104,264,266,268,413,450,451],$V34=[1,855],$V44=[1,854],$V54=[2,521],$V64=[1,871],$V74=[69,114],$V84=[8,67,69,71,114,119,264,268,368,372,450,451],$V94=[2,660],$Va4=[1,887],$Vb4=[1,888],$Vc4=[1,893],$Vd4=[1,894],$Ve4=[2,301],$Vf4=[1,909],$Vg4=[1,920],$Vh4=[8,67,71,264,266,268,413,450,451],$Vi4=[1,923],$Vj4=[8,65,67,71,93,104,136,142,143,180,182,194,195,196,197,198,199,200,201,205,218,264,268,450,451],$Vk4=[8,264,266,268,413,450,451],$Vl4=[8,65,71,93,136,142,143,218,264,268,450,451],$Vm4=[1,935],$Vn4=[1,939],$Vo4=[1,940],$Vp4=[1,942],$Vq4=[1,943],$Vr4=[1,944],$Vs4=[1,945],$Vt4=[1,946],$Vu4=[1,947],$Vv4=[1,948],$Vw4=[1,949],$Vx4=[1,973],$Vy4=[67,71],$Vz4=[97,99],$VA4=[1,1026],$VB4=[8,65,71,136,142,143,218,264,268,450,451],$VC4=[8,65,71,93,104,136,142,143,180,182,194,195,196,197,198,199,200,201,205,218,264,268,450,451],$VD4=[1,1064],$VE4=[1,1066],$VF4=[4,5,70,116,119,126,155,260,368,372],$VG4=[1,1080],$VH4=[8,65,67,71,136,142,143,218,264,268,450,451],$VI4=[1,1099],$VJ4=[1,1101],$VK4=[1,1098],$VL4=[1,1097],$VM4=[1,1096],$VN4=[1,1102],$VO4=[1,1093],$VP4=[1,1094],$VQ4=[1,1095],$VR4=[1,1114],$VS4=[4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,257,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,294,298,307,319,320,324,325,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$VT4=[1,1123],$VU4=[1,1131],$VV4=[1,1130],$VW4=[8,65,71,136,142,143,264,268,450,451],$VX4=[1,1141],$VY4=[1,1142],$VZ4=[1,1143],$V_4=[8,65,71,93,104,136,142,143,180,182,194,195,196,197,198,199,200,201,202,203,205,218,264,268,450,451],$V$4=[4,5,8,65,71,93,104,136,142,143,180,182,194,195,196,197,198,199,200,201,202,203,205,218,264,268,450,451],$V05=[1,1188],$V15=[1,1187],$V25=[1,1189],$V35=[161,167,319,320,321,324],$V45=[2,456],$V55=[1,1194],$V65=[1,1213],$V75=[1,1224],$V85=[8,65,67,71,93,136,142,143,212,218,264,268,450,451],$V95=[1,1244],$Va5=[4,8,264,268,294,307,450,451],$Vb5=[1,1363],$Vc5=[1,1373],$Vd5=[8,67,71,202,264,268,450,451];var parser={trace:function trace(){},yy:{},symbols_:{error:2,Literal:3,LITERAL:4,BRALITERAL:5,main:6,Statements:7,EOF:8,Statements_group0:9,AStatement:10,ExplainStatement:11,EXPLAIN:12,QUERY:13,PLAN:14,Statement:15,AlterTable:16,AttachDatabase:17,CreateDatabase:18,CreateIndex:19,CreateGraph:20,CreateTable:21,CreateView:22,CreateEdge:23,CreateVertex:24,Declare:25,Delete:26,DetachDatabase:27,DropDatabase:28,DropIndex:29,DropTable:30,DropView:31,If:32,Insert:33,Merge:34,RenameTable:35,Select:36,ShowCreateTable:37,ShowColumns:38,ShowDatabases:39,ShowIndex:40,ShowTables:41,TruncateTable:42,WithSelect:43,BeginTransaction:44,CommitTransaction:45,RollbackTransaction:46,EndTransaction:47,UseDatabase:48,Update:49,Help:50,JavaScript:51,Source:52,Assert:53,While:54,Continue:55,Break:56,BeginEnd:57,Print:58,Require:59,SetVariable:60,ExpressionStatement:61,AddRule:62,Query:63,Echo:64,WITH:65,WithTablesList:66,COMMA:67,WithTable:68,AS:69,LPAR:70,RPAR:71,SelectClause:72,Select_option0:73,IntoClause:74,FromClause:75,WhereClause:76,GroupClause:77,OrderClause:78,LimitClause:79,UnionClause:80,SEARCH:81,Select_repetition0:82,Select_option1:83,RemoveClause:84,REMOVE:85,RemoveClause_option0:86,RemoveColumnsList:87,RemoveColumn:88,Column:89,LIKE:90,StringValue:91,SearchSelector:92,ORDER:93,BY:94,OrderExpressionsList:95,SearchSelector_option0:96,ARROW:97,CARET:98,EQ:99,Expression:100,SearchSelector_repetition_plus0:101,SearchSelector_repetition_plus1:102,SearchSelector_option1:103,WHERE:104,CLASS:105,NUMBER:106,STRING:107,SLASH:108,VERTEX:109,EDGE:110,EXCLAMATION:111,SHARP:112,MODULO:113,GT:114,LT:115,DOLLAR:116,DOT:117,Json:118,AT:119,SET:120,SetColumnsList:121,TO:122,VALUE:123,ROW:124,ExprList:125,COLON:126,PlusStar:127,NOT:128,SearchSelector_repetition2:129,IF:130,SearchSelector_repetition3:131,Aggregator:132,SearchSelector_repetition4:133,SearchSelector_group0:134,SearchSelector_repetition5:135,UNION:136,SearchSelectorList:137,ALL:138,SearchSelector_repetition6:139,ANY:140,SearchSelector_repetition7:141,INTERSECT:142,EXCEPT:143,AND:144,OR:145,PATH:146,RETURN:147,ResultColumns:148,REPEAT:149,SearchSelector_repetition8:150,SearchSelectorList_repetition0:151,SearchSelectorList_repetition1:152,PLUS:153,STAR:154,QUESTION:155,SearchFrom:156,FROM:157,SelectModifier:158,DISTINCT:159,TopClause:160,UNIQUE:161,SelectClause_option0:162,SELECT:163,COLUMN:164,MATRIX:165,TEXTSTRING:166,INDEX:167,RECORDSET:168,TOP:169,NumValue:170,TopClause_option0:171,INTO:172,Table:173,FuncValue:174,ParamValue:175,VarValue:176,FromTablesList:177,JoinTablesList:178,ApplyClause:179,CROSS:180,APPLY:181,OUTER:182,FromTable:183,FromTable_option0:184,FromTable_option1:185,FromString:186,JoinTable:187,JoinMode:188,JoinTableAs:189,OnClause:190,JoinTableAs_option0:191,JoinTableAs_option1:192,JoinModeMode:193,NATURAL:194,JOIN:195,INNER:196,LEFT:197,RIGHT:198,FULL:199,SEMI:200,ANTI:201,ON:202,USING:203,ColumnsList:204,GROUP:205,GroupExpressionsList:206,HavingClause:207,GroupExpression:208,GROUPING:209,ROLLUP:210,CUBE:211,HAVING:212,CORRESPONDING:213,OrderExpression:214,DIRECTION:215,COLLATE:216,NOCASE:217,LIMIT:218,OffsetClause:219,OFFSET:220,ResultColumn:221,Star:222,AggrValue:223,Op:224,LogicValue:225,NullValue:226,ExistsValue:227,CaseValue:228,CastClause:229,NewClause:230,Expression_group0:231,CURRENT_TIMESTAMP:232,JAVASCRIPT:233,NEW:234,CAST:235,ColumnType:236,CONVERT:237,PrimitiveValue:238,OverClause:239,OVER:240,OverPartitionClause:241,OverOrderByClause:242,PARTITION:243,SUM:244,COUNT:245,MIN:246,MAX:247,AVG:248,FIRST:249,LAST:250,AGGR:251,ARRAY:252,FuncValue_option0:253,TRUE:254,FALSE:255,NSTRING:256,NULL:257,EXISTS:258,ParamValue_group0:259,BRAQUESTION:260,CASE:261,WhensList:262,ElseClause:263,END:264,When:265,WHEN:266,THEN:267,ELSE:268,NOT_LIKE:269,MINUS:270,GE:271,LE:272,EQEQ:273,EQEQEQ:274,NE:275,NEEQEQ:276,NEEQEQEQ:277,CondOp:278,AllSome:279,IN:280,ColFunc:281,BETWEEN:282,NOT_BETWEEN:283,IS:284,DOUBLECOLON:285,SOME:286,UPDATE:287,SetColumn:288,SetColumn_group0:289,DELETE:290,INSERT:291,Into:292,ValuesListsList:293,DEFAULT:294,ValuesList:295,Value:296,DateValue:297,CREATE:298,TemporaryClause:299,TableClass:300,IfNotExists:301,CreateTableDefClause:302,CreateTableOptionsClause:303,TABLE:304,CreateTableOptions:305,CreateTableOption:306,IDENTITY:307,TEMP:308,ColumnDefsList:309,ConstraintsList:310,Constraint:311,ConstraintName:312,PrimaryKey:313,ForeignKey:314,UniqueKey:315,IndexKey:316,Check:317,CONSTRAINT:318,CHECK:319,PRIMARY:320,KEY:321,PrimaryKey_option0:322,ColsList:323,FOREIGN:324,REFERENCES:325,ForeignKey_option0:326,OnForeignKeyClause:327,ParColsList:328,OnDeleteClause:329,OnUpdateClause:330,NO:331,ACTION:332,UniqueKey_option0:333,ColumnDef:334,ColumnConstraintsClause:335,ColumnConstraints:336,NumberMax:337,ENUM:338,ColumnConstraintsList:339,ColumnConstraint:340,ParLiteral:341,ColumnConstraint_option0:342,ColumnConstraint_option1:343,DROP:344,DropTable_group0:345,IfExists:346,ALTER:347,RENAME:348,ADD:349,MODIFY:350,ATTACH:351,DATABASE:352,DETACH:353,AsClause:354,USE:355,SHOW:356,VIEW:357,CreateView_option0:358,CreateView_option1:359,SubqueryRestriction:360,READ:361,ONLY:362,OPTION:363,HELP:364,SOURCE:365,ASSERT:366,JsonObject:367,ATLBRA:368,JsonArray:369,JsonValue:370,JsonPrimitiveValue:371,LCUR:372,JsonPropertiesList:373,RCUR:374,JsonElementsList:375,RBRA:376,JsonProperty:377,OnOff:378,AtDollar:379,SetPropsList:380,SetProp:381,OFF:382,COMMIT:383,TRANSACTION:384,ROLLBACK:385,BEGIN:386,ElseStatement:387,WHILE:388,CONTINUE:389,BREAK:390,PRINT:391,REQUIRE:392,StringValuesList:393,PluginsList:394,Plugin:395,ECHO:396,DECLARE:397,DeclaresList:398,DeclareItem:399,TRUNCATE:400,MERGE:401,MergeInto:402,MergeUsing:403,MergeOn:404,MergeMatchedList:405,OutputClause:406,MergeMatched:407,MergeNotMatched:408,MATCHED:409,MergeMatchedAction:410,MergeNotMatchedAction:411,TARGET:412,OUTPUT:413,CreateVertex_option0:414,CreateVertex_option1:415,CreateVertex_option2:416,CreateVertexSet:417,SharpValue:418,CONTENT:419,CreateEdge_option0:420,GRAPH:421,GraphList:422,GraphVertexEdge:423,GraphElement:424,GraphVertexEdge_option0:425,GraphVertexEdge_option1:426,GraphVertexEdge_group0:427,GraphVertexEdge_option2:428,GraphVertexEdge_option3:429,GraphVertexEdge_group1:430,GraphVar:431,GraphAsClause:432,GraphAtClause:433,GraphElement_option0:434,GraphElement_option1:435,GraphElement_option2:436,GraphElement_option3:437,ColonLiteral:438,SharpLiteral:439,DeleteVertex:440,DeleteVertex_option0:441,DeleteEdge:442,DeleteEdge_option0:443,DeleteEdge_option1:444,DeleteEdge_option2:445,Term:446,COLONDASH:447,TermsList:448,QUESTIONDASH:449,SEMICOLON:450,GO:451,PERCENT:452,FuncValue_option0_group0:453,$accept:0,$end:1},terminals_:{2:"error",4:"LITERAL",5:"BRALITERAL",8:"EOF",12:"EXPLAIN",13:"QUERY",14:"PLAN",47:"EndTransaction",65:"WITH",67:"COMMA",69:"AS",70:"LPAR",71:"RPAR",81:"SEARCH",85:"REMOVE",90:"LIKE",93:"ORDER",94:"BY",97:"ARROW",98:"CARET",99:"EQ",104:"WHERE",105:"CLASS",106:"NUMBER",107:"STRING",108:"SLASH",109:"VERTEX",110:"EDGE",111:"EXCLAMATION",112:"SHARP",113:"MODULO",114:"GT",115:"LT",116:"DOLLAR",117:"DOT",119:"AT",120:"SET",122:"TO",123:"VALUE",124:"ROW",126:"COLON",128:"NOT",130:"IF",136:"UNION",138:"ALL",140:"ANY",142:"INTERSECT",143:"EXCEPT",144:"AND",145:"OR",146:"PATH",147:"RETURN",149:"REPEAT",153:"PLUS",154:"STAR",155:"QUESTION",157:"FROM",159:"DISTINCT",161:"UNIQUE",163:"SELECT",164:"COLUMN",165:"MATRIX",166:"TEXTSTRING",167:"INDEX",168:"RECORDSET",169:"TOP",172:"INTO",180:"CROSS",181:"APPLY",182:"OUTER",194:"NATURAL",195:"JOIN",196:"INNER",197:"LEFT",198:"RIGHT",199:"FULL",200:"SEMI",201:"ANTI",202:"ON",203:"USING",205:"GROUP",209:"GROUPING",210:"ROLLUP",211:"CUBE",212:"HAVING",213:"CORRESPONDING",215:"DIRECTION",216:"COLLATE",217:"NOCASE",218:"LIMIT",220:"OFFSET",232:"CURRENT_TIMESTAMP",233:"JAVASCRIPT",234:"NEW",235:"CAST",237:"CONVERT",240:"OVER",243:"PARTITION",244:"SUM",245:"COUNT",246:"MIN",247:"MAX",248:"AVG",249:"FIRST",250:"LAST",251:"AGGR",252:"ARRAY",254:"TRUE",255:"FALSE",256:"NSTRING",257:"NULL",258:"EXISTS",260:"BRAQUESTION",261:"CASE",264:"END",266:"WHEN",267:"THEN",268:"ELSE",269:"NOT_LIKE",270:"MINUS",271:"GE",272:"LE",273:"EQEQ",274:"EQEQEQ",275:"NE",276:"NEEQEQ",277:"NEEQEQEQ",280:"IN",282:"BETWEEN",283:"NOT_BETWEEN",284:"IS",285:"DOUBLECOLON",286:"SOME",287:"UPDATE",290:"DELETE",291:"INSERT",294:"DEFAULT",297:"DateValue",298:"CREATE",304:"TABLE",307:"IDENTITY",308:"TEMP",318:"CONSTRAINT",319:"CHECK",320:"PRIMARY",321:"KEY",324:"FOREIGN",325:"REFERENCES",331:"NO",332:"ACTION",336:"ColumnConstraints",338:"ENUM",344:"DROP",347:"ALTER",348:"RENAME",349:"ADD",350:"MODIFY",351:"ATTACH",352:"DATABASE",353:"DETACH",355:"USE",356:"SHOW",357:"VIEW",361:"READ",362:"ONLY",363:"OPTION",364:"HELP",365:"SOURCE",366:"ASSERT",368:"ATLBRA",372:"LCUR",374:"RCUR",376:"RBRA",382:"OFF",383:"COMMIT",384:"TRANSACTION",385:"ROLLBACK",386:"BEGIN",388:"WHILE",389:"CONTINUE",390:"BREAK",391:"PRINT",392:"REQUIRE",396:"ECHO",397:"DECLARE",400:"TRUNCATE",401:"MERGE",409:"MATCHED",412:"TARGET",413:"OUTPUT",419:"CONTENT",421:"GRAPH",447:"COLONDASH",449:"QUESTIONDASH",450:"SEMICOLON",451:"GO",452:"PERCENT"},productions_:[0,[3,1],[3,1],[6,2],[7,3],[7,1],[7,1],[11,2],[11,4],[10,1],[15,0],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[15,1],[43,3],[66,3],[66,1],[68,5],[36,9],[36,4],[84,3],[87,3],[87,1],[88,1],[88,2],[92,1],[92,5],[92,5],[92,2],[92,1],[92,2],[92,2],[92,3],[92,4],[92,4],[92,4],[92,4],[92,1],[92,1],[92,1],[92,1],[92,1],[92,1],[92,2],[92,2],[92,2],[92,1],[92,1],[92,1],[92,2],[92,1],[92,2],[92,3],[92,4],[92,3],[92,1],[92,4],[92,2],[92,2],[92,4],[92,4],[92,4],[92,4],[92,4],[92,5],[92,4],[92,4],[92,4],[92,4],[92,4],[92,4],[92,4],[92,4],[92,6],[137,3],[137,1],[127,1],[127,1],[127,1],[156,2],[72,4],[72,4],[72,4],[72,3],[158,1],[158,2],[158,2],[158,2],[158,2],[158,2],[158,2],[158,2],[160,3],[160,4],[160,0],[74,0],[74,2],[74,2],[74,2],[74,2],[74,2],[75,2],[75,3],[75,5],[75,0],[179,6],[179,7],[179,6],[179,7],[177,1],[177,3],[183,4],[183,5],[183,3],[183,3],[183,2],[183,3],[183,1],[183,2],[183,3],[183,1],[183,1],[183,2],[183,3],[183,1],[183,2],[183,3],[183,1],[183,2],[183,3],[186,1],[173,3],[173,1],[178,2],[178,2],[178,1],[178,1],[187,3],[189,1],[189,2],[189,3],[189,3],[189,2],[189,3],[189,4],[189,5],[189,1],[189,2],[189,3],[189,1],[189,2],[189,3],[188,1],[188,2],[193,1],[193,2],[193,2],[193,3],[193,2],[193,3],[193,2],[193,3],[193,2],[193,2],[193,2],[190,2],[190,2],[190,0],[76,0],[76,2],[77,0],[77,4],[206,1],[206,3],[208,5],[208,4],[208,4],[208,1],[207,0],[207,2],[80,0],[80,2],[80,3],[80,2],[80,2],[80,3],[80,4],[80,3],[80,3],[78,0],[78,3],[95,1],[95,3],[214,1],[214,2],[214,3],[214,4],[79,0],[79,3],[219,0],[219,2],[148,3],[148,1],[221,3],[221,2],[221,3],[221,2],[221,3],[221,2],[221,1],[222,5],[222,3],[222,1],[89,5],[89,3],[89,3],[89,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,1],[100,3],[100,3],[100,3],[100,1],[100,1],[51,1],[230,2],[230,2],[229,6],[229,8],[229,6],[229,8],[238,1],[238,1],[238,1],[238,1],[238,1],[238,1],[238,1],[223,5],[223,6],[223,6],[239,0],[239,4],[239,4],[239,5],[241,3],[242,3],[132,1],[132,1],[132,1],[132,1],[132,1],[132,1],[132,1],[132,1],[132,1],[174,5],[174,3],[125,1],[125,3],[170,1],[225,1],[225,1],[91,1],[91,1],[226,1],[176,2],[227,4],[175,2],[175,2],[175,1],[175,1],[228,5],[228,4],[262,2],[262,1],[265,4],[263,2],[263,0],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,5],[224,3],[224,3],[224,3],[224,5],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,3],[224,6],[224,6],[224,3],[224,3],[224,2],[224,2],[224,2],[224,2],[224,3],[224,5],[224,6],[224,5],[224,6],[224,4],[224,5],[224,3],[224,4],[224,3],[224,4],[224,3],[224,3],[224,3],[224,3],[281,1],[281,1],[281,4],[278,1],[278,1],[278,1],[278,1],[278,1],[278,1],[279,1],[279,1],[279,1],[49,6],[49,4],[121,1],[121,3],[288,3],[288,4],[26,5],[26,3],[33,5],[33,5],[33,8],[33,4],[33,7],[292,0],[292,1],[293,3],[293,1],[293,1],[293,5],[293,3],[293,3],[295,1],[295,3],[296,1],[296,1],[296,1],[296,1],[296,1],[296,1],[204,1],[204,3],[21,9],[21,5],[300,1],[300,1],[303,0],[303,1],[305,2],[305,1],[306,1],[306,3],[306,3],[299,0],[299,1],[301,0],[301,3],[302,3],[302,1],[302,2],[310,1],[310,3],[311,2],[311,2],[311,2],[311,2],[311,2],[312,0],[312,2],[317,4],[313,6],[314,9],[328,3],[327,0],[327,2],[329,4],[330,4],[315,5],[316,5],[316,5],[323,1],[323,1],[323,3],[323,3],[309,1],[309,3],[334,3],[334,2],[334,1],[236,6],[236,7],[236,4],[236,5],[236,1],[236,2],[236,4],[337,1],[337,1],[335,0],[335,1],[339,2],[339,1],[341,3],[340,2],[340,5],[340,3],[340,6],[340,1],[340,2],[340,4],[340,1],[340,2],[340,1],[340,1],[30,4],[346,0],[346,2],[16,6],[16,6],[16,6],[16,8],[16,6],[35,5],[17,4],[17,7],[17,6],[17,9],[27,3],[18,4],[18,6],[18,9],[18,6],[354,0],[354,2],[48,3],[48,2],[28,4],[28,5],[28,5],[19,8],[19,9],[29,3],[39,2],[39,4],[39,3],[39,5],[41,2],[41,4],[41,4],[41,6],[38,4],[38,6],[40,4],[40,6],[37,4],[37,6],[22,11],[22,8],[360,3],[360,3],[360,5],[31,4],[50,2],[50,1],[61,2],[52,2],[53,2],[53,2],[53,4],[118,4],[118,2],[118,2],[118,2],[118,2],[118,1],[118,2],[118,2],[370,1],[370,1],[371,1],[371,1],[371,1],[371,1],[371,1],[371,1],[371,1],[371,3],[367,3],[367,4],[367,2],[369,2],[369,3],[369,1],[373,3],[373,1],[377,3],[377,3],[377,3],[375,3],[375,1],[60,3],[60,5],[60,6],[379,1],[379,1],[380,3],[380,2],[381,1],[381,1],[381,3],[378,1],[378,1],[45,2],[46,2],[44,2],[32,4],[32,3],[387,2],[54,3],[55,1],[56,1],[57,3],[58,2],[58,2],[59,2],[59,2],[395,1],[395,1],[64,2],[393,3],[393,1],[394,3],[394,1],[25,2],[398,1],[398,3],[399,3],[399,4],[399,5],[399,6],[42,3],[34,6],[402,1],[402,2],[403,2],[404,2],[405,2],[405,2],[405,1],[405,1],[407,4],[407,6],[410,1],[410,3],[408,5],[408,7],[408,7],[408,9],[408,7],[408,9],[411,3],[411,6],[411,3],[411,6],[406,0],[406,2],[406,5],[406,4],[406,7],[24,6],[418,2],[417,0],[417,2],[417,2],[417,1],[23,8],[20,3],[20,4],[422,3],[422,1],[423,3],[423,7],[423,4],[431,2],[432,3],[433,2],[424,4],[438,2],[439,2],[439,2],[440,4],[442,6],[62,3],[62,2],[448,3],[448,1],[446,1],[446,4],[63,2],[9,1],[9,1],[73,0],[73,1],[82,0],[82,2],[83,0],[83,1],[86,0],[86,1],[96,0],[96,1],[101,1],[101,2],[102,1],[102,2],[103,0],[103,1],[129,0],[129,2],[131,0],[131,2],[133,0],[133,2],[134,1],[134,1],[135,0],[135,2],[139,0],[139,2],[141,0],[141,2],[150,0],[150,2],[151,0],[151,2],[152,0],[152,2],[162,0],[162,1],[171,0],[171,1],[184,0],[184,1],[185,0],[185,1],[191,0],[191,1],[192,0],[192,1],[231,1],[231,1],[453,1],[453,1],[253,0],[253,1],[259,1],[259,1],[289,1],[289,1],[322,0],[322,1],[326,0],[326,1],[333,0],[333,1],[342,0],[342,1],[343,0],[343,1],[345,1],[345,1],[358,0],[358,1],[359,0],[359,1],[414,0],[414,1],[415,0],[415,1],[416,0],[416,1],[420,0],[420,1],[425,0],[425,1],[426,0],[426,1],[427,1],[427,1],[428,0],[428,1],[429,0],[429,1],[430,1],[430,1],[434,0],[434,1],[435,0],[435,1],[436,0],[436,1],[437,0],[437,1],[441,0],[441,2],[443,0],[443,2],[444,0],[444,2],[445,0],[445,2]],performAction:function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$){var $0=$$.length-1;switch(yystate){case 1:if(yy.casesensitive)this.$=$$[$0];else this.$=$$[$0].toLowerCase();break;case 2:this.$=doubleq($$[$0].substr(1,$$[$0].length-2));break;case 3:return new yy.Statements({statements:$$[$0-1]});break;case 4:this.$=$$[$0-2];if($$[$0])$$[$0-2].push($$[$0]);break;case 5:case 6:case 62:case 68:case 121:case 155:case 181:case 182:case 218:case 237:case 248:case 318:case 335:case 402:case 416:case 417:case 421:case 429:case 469:case 470:case 588:case 595:case 619:case 621:case 623:case 637:case 638:case 668:case 684:this.$=[$$[$0]];break;case 7:this.$=$$[$0];$$[$0].explain=true;
-break;case 8:this.$=$$[$0];$$[$0].explain=true;break;case 9:this.$=$$[$0];if(yy.exists)this.$.exists=yy.exists;delete yy.exists;if(yy.queries)this.$.queries=yy.queries;delete yy.queries;break;case 10:case 140:case 150:case 213:case 214:case 216:case 224:case 226:case 235:case 243:case 245:case 338:case 433:case 442:case 444:case 456:case 462:case 463:case 504:this.$=undefined;break;case 60:this.$=new yy.WithSelect({withs:$$[$0-1],select:$$[$0]});break;case 61:$$[$0-2].push($$[$0]);this.$=$$[$0-2];break;case 63:this.$={name:$$[$0-4],select:$$[$0-1]};break;case 64:yy.extend(this.$,$$[$0-8]);yy.extend(this.$,$$[$0-7]);yy.extend(this.$,$$[$0-6]);yy.extend(this.$,$$[$0-5]);yy.extend(this.$,$$[$0-4]);yy.extend(this.$,$$[$0-3]);yy.extend(this.$,$$[$0-2]);yy.extend(this.$,$$[$0-1]);yy.extend(this.$,$$[$0]);this.$=$$[$0-8];break;case 65:this.$=new yy.Search({selectors:$$[$0-2],from:$$[$0]});yy.extend(this.$,$$[$0-1]);break;case 66:this.$={removecolumns:$$[$0]};break;case 67:case 667:case 683:this.$=$$[$0-2];this.$.push($$[$0]);break;case 69:case 125:case 163:case 223:case 255:case 263:case 264:case 265:case 266:case 267:case 268:case 269:case 270:case 271:case 272:case 273:case 274:case 275:case 276:case 278:case 291:case 292:case 293:case 294:case 295:case 296:case 337:case 391:case 392:case 393:case 394:case 395:case 396:case 457:case 488:case 490:case 562:case 563:case 564:case 565:case 566:case 567:case 571:case 573:case 574:case 583:case 596:case 597:case 659:case 673:case 674:case 676:case 677:this.$=$$[$0];break;case 70:this.$={like:$$[$0]};break;case 71:case 83:this.$={srchid:"PROP",args:[$$[$0]]};break;case 72:this.$={srchid:"ORDERBY",args:$$[$0-1]};break;case 73:var dir=$$[$0-1];if(!dir)dir="ASC";this.$={srchid:"ORDERBY",args:[{expression:new yy.Column({columnid:"_"}),direction:dir}]};break;case 74:this.$={srchid:"APROP",args:[$$[$0]]};break;case 75:this.$={selid:"ROOT"};break;case 76:this.$={srchid:"EQ",args:[$$[$0]]};break;case 77:this.$={srchid:"LIKE",args:[$$[$0]]};break;case 78:case 79:this.$={selid:"WITH",args:$$[$0-1]};break;case 80:this.$={srchid:$$[$0-3].toUpperCase(),args:$$[$0-1]};break;case 81:this.$={srchid:"WHERE",args:[$$[$0-1]]};break;case 82:this.$={srchid:"CLASS",args:[$$[$0-1]]};break;case 84:this.$={srchid:"NAME",args:[$$[$0].substr(1,$$[$0].length-2)]};break;case 85:this.$={srchid:"CHILD"};break;case 86:this.$={srchid:"VERTEX"};break;case 87:this.$={srchid:"EDGE"};break;case 88:this.$={srchid:"REF"};break;case 89:this.$={srchid:"SHARP",args:[$$[$0]]};break;case 90:this.$={srchid:"ATTR",args:typeof $$[$0]=="undefined"?undefined:[$$[$0]]};break;case 91:this.$={srchid:"ATTR"};break;case 92:this.$={srchid:"OUT"};break;case 93:this.$={srchid:"IN"};break;case 94:this.$={srchid:"CONTENT"};break;case 95:this.$={srchid:"PARENT"};break;case 96:this.$={srchid:"EX",args:[new yy.Json({value:$$[$0]})]};break;case 97:this.$={srchid:"AT",args:[$$[$0]]};break;case 98:this.$={srchid:"AS",args:[$$[$0]]};break;case 99:this.$={srchid:"SET",args:$$[$0-1]};break;case 100:this.$={selid:"TO",args:[$$[$0]]};break;case 101:this.$={srchid:"VALUE"};break;case 102:this.$={srchid:"ROW",args:$$[$0-1]};break;case 103:this.$={srchid:"CLASS",args:[$$[$0]]};break;case 104:this.$={selid:$$[$0],args:[$$[$0-1]]};break;case 105:this.$={selid:"NOT",args:$$[$0-1]};break;case 106:this.$={selid:"IF",args:$$[$0-1]};break;case 107:this.$={selid:$$[$0-3],args:$$[$0-1]};break;case 108:this.$={selid:"DISTINCT",args:$$[$0-1]};break;case 109:this.$={selid:"UNION",args:$$[$0-1]};break;case 110:this.$={selid:"UNIONALL",args:$$[$0-1]};break;case 111:this.$={selid:"ALL",args:[$$[$0-1]]};break;case 112:this.$={selid:"ANY",args:[$$[$0-1]]};break;case 113:this.$={selid:"INTERSECT",args:$$[$0-1]};break;case 114:this.$={selid:"EXCEPT",args:$$[$0-1]};break;case 115:this.$={selid:"AND",args:$$[$0-1]};break;case 116:this.$={selid:"OR",args:$$[$0-1]};break;case 117:this.$={selid:"PATH",args:[$$[$0-1]]};break;case 118:this.$={srchid:"RETURN",args:$$[$0-1]};break;case 119:this.$={selid:"REPEAT",sels:$$[$0-3],args:$$[$0-1]};break;case 120:this.$=$$[$0-2];this.$.push($$[$0]);break;case 122:this.$="PLUS";break;case 123:this.$="STAR";break;case 124:this.$="QUESTION";break;case 126:this.$=new yy.Select({columns:$$[$0],distinct:true});yy.extend(this.$,$$[$0-3]);yy.extend(this.$,$$[$0-1]);break;case 127:this.$=new yy.Select({columns:$$[$0],distinct:true});yy.extend(this.$,$$[$0-3]);yy.extend(this.$,$$[$0-1]);break;case 128:this.$=new yy.Select({columns:$$[$0],all:true});yy.extend(this.$,$$[$0-3]);yy.extend(this.$,$$[$0-1]);break;case 129:if(!$$[$0]){this.$=new yy.Select({columns:[new yy.Column({columnid:"_"})],modifier:"COLUMN"})}else{this.$=new yy.Select({columns:$$[$0]});yy.extend(this.$,$$[$0-2]);yy.extend(this.$,$$[$0-1])}break;case 130:if($$[$0]=="SELECT")this.$=undefined;else this.$={modifier:$$[$0]};break;case 131:this.$={modifier:"VALUE"};break;case 132:this.$={modifier:"ROW"};break;case 133:this.$={modifier:"COLUMN"};break;case 134:this.$={modifier:"MATRIX"};break;case 135:this.$={modifier:"TEXTSTRING"};break;case 136:this.$={modifier:"INDEX"};break;case 137:this.$={modifier:"RECORDSET"};break;case 138:this.$={top:$$[$0-1],percent:typeof $$[$0]!="undefined"?true:undefined};break;case 139:this.$={top:$$[$0-1]};break;case 141:case 301:case 464:case 465:case 660:this.$=undefined;break;case 142:case 143:case 144:case 145:this.$={into:$$[$0]};break;case 146:var s=$$[$0];s=s.substr(1,s.length-2);var x3=s.substr(-3).toUpperCase();var x4=s.substr(-4).toUpperCase();if(s[0]=="#"){this.$={into:new yy.FuncValue({funcid:"HTML",args:[new yy.StringValue({value:s}),new yy.Json({value:{headers:true}})]})}}else if(x3=="XLS"||x3=="CSV"||x3=="TAB"){this.$={into:new yy.FuncValue({funcid:x3,args:[new yy.StringValue({value:s}),new yy.Json({value:{headers:true}})]})}}else if(x4=="XLSX"||x4=="JSON"){this.$={into:new yy.FuncValue({funcid:x4,args:[new yy.StringValue({value:s}),new yy.Json({value:{headers:true}})]})}}break;case 147:this.$={from:$$[$0]};break;case 148:this.$={from:$$[$0-1],joins:$$[$0]};break;case 149:this.$={from:$$[$0-2],joins:$$[$0-1]};break;case 151:this.$=new yy.Apply({select:$$[$0-2],applymode:"CROSS",as:$$[$0]});break;case 152:this.$=new yy.Apply({select:$$[$0-3],applymode:"CROSS",as:$$[$0]});break;case 153:this.$=new yy.Apply({select:$$[$0-2],applymode:"OUTER",as:$$[$0]});break;case 154:this.$=new yy.Apply({select:$$[$0-3],applymode:"OUTER",as:$$[$0]});break;case 156:case 219:case 403:case 471:case 472:this.$=$$[$0-2];$$[$0-2].push($$[$0]);break;case 157:this.$=$$[$0-2];this.$.as=$$[$0];break;case 158:this.$=$$[$0-3];this.$.as=$$[$0];break;case 159:this.$=$$[$0-1];this.$.as="default";break;case 160:this.$=new yy.Json({value:$$[$0-2]});$$[$0-2].as=$$[$0];break;case 161:this.$=$$[$0-1];$$[$0-1].as=$$[$0];break;case 162:this.$=$$[$0-2];$$[$0-2].as=$$[$0];break;case 164:case 168:case 171:case 174:this.$=$$[$0-1];$$[$0-1].as=$$[$0];break;case 165:case 169:case 172:case 175:this.$=$$[$0-2];$$[$0-2].as=$$[$0];break;case 166:case 167:case 170:case 173:this.$=$$[$0];$$[$0].as="default";break;case 176:var s=$$[$0];s=s.substr(1,s.length-2);var x3=s.substr(-3).toUpperCase();var x4=s.substr(-4).toUpperCase();var r;if(s[0]=="#"){r=new yy.FuncValue({funcid:"HTML",args:[new yy.StringValue({value:s}),new yy.Json({value:{headers:true}})]})}else if(x3=="XLS"||x3=="CSV"||x3=="TAB"){r=new yy.FuncValue({funcid:x3,args:[new yy.StringValue({value:s}),new yy.Json({value:{headers:true}})]})}else if(x4=="XLSX"||x4=="JSON"){r=new yy.FuncValue({funcid:x4,args:[new yy.StringValue({value:s}),new yy.Json({value:{headers:true}})]})}else{throw new Error("Unknown string in FROM clause")};this.$=r;break;case 177:if($$[$0-2]=="INFORMATION_SCHEMA"){this.$=new yy.FuncValue({funcid:$$[$0-2],args:[new yy.StringValue({value:$$[$0]})]})}else{this.$=new yy.Table({databaseid:$$[$0-2],tableid:$$[$0]})}break;case 178:this.$=new yy.Table({tableid:$$[$0]});break;case 179:case 180:this.$=$$[$0-1];$$[$0-1].push($$[$0]);break;case 183:this.$=new yy.Join($$[$0-2]);yy.extend(this.$,$$[$0-1]);yy.extend(this.$,$$[$0]);break;case 184:this.$={table:$$[$0]};break;case 185:this.$={table:$$[$0-1],as:$$[$0]};break;case 186:this.$={table:$$[$0-2],as:$$[$0]};break;case 187:this.$={json:new yy.Json({value:$$[$0-2],as:$$[$0]})};break;case 188:this.$={param:$$[$0-1],as:$$[$0]};break;case 189:this.$={param:$$[$0-2],as:$$[$0]};break;case 190:this.$={select:$$[$0-3],as:$$[$0]};break;case 191:this.$={select:$$[$0-4],as:$$[$0]};break;case 192:this.$={funcid:$$[$0],as:"default"};break;case 193:this.$={funcid:$$[$0-1],as:$$[$0]};break;case 194:this.$={funcid:$$[$0-2],as:$$[$0]};break;case 195:this.$={variable:$$[$0],as:"default"};break;case 196:this.$={variable:$$[$0-1],as:$$[$0]};break;case 197:this.$={variable:$$[$0-2],as:$$[$0]};break;case 198:this.$={joinmode:$$[$0]};break;case 199:this.$={joinmode:$$[$0-1],natural:true};break;case 200:case 201:this.$="INNER";break;case 202:case 203:this.$="LEFT";break;case 204:case 205:this.$="RIGHT";break;case 206:case 207:this.$="OUTER";break;case 208:this.$="SEMI";break;case 209:this.$="ANTI";break;case 210:this.$="CROSS";break;case 211:this.$={on:$$[$0]};break;case 212:case 633:this.$={using:$$[$0]};break;case 215:this.$={where:new yy.Expression({expression:$$[$0]})};break;case 217:this.$={group:$$[$0-1]};yy.extend(this.$,$$[$0]);break;case 220:this.$=new yy.GroupExpression({type:"GROUPING SETS",group:$$[$0-1]});break;case 221:this.$=new yy.GroupExpression({type:"ROLLUP",group:$$[$0-1]});break;case 222:this.$=new yy.GroupExpression({type:"CUBE",group:$$[$0-1]});break;case 225:this.$={having:$$[$0]};break;case 227:this.$={union:$$[$0]};break;case 228:this.$={unionall:$$[$0]};break;case 229:this.$={except:$$[$0]};break;case 230:this.$={intersect:$$[$0]};break;case 231:this.$={union:$$[$0],corresponding:true};break;case 232:this.$={unionall:$$[$0],corresponding:true};break;case 233:this.$={except:$$[$0],corresponding:true};break;case 234:this.$={intersect:$$[$0],corresponding:true};break;case 236:this.$={order:$$[$0]};break;case 238:this.$=$$[$0-2];$$[$0-2].push($$[$0]);break;case 239:this.$=new yy.Expression({expression:$$[$0],direction:"ASC"});break;case 240:this.$=new yy.Expression({expression:$$[$0-1],direction:$$[$0].toUpperCase()});break;case 241:this.$=new yy.Expression({expression:$$[$0-2],direction:"ASC",nocase:true});break;case 242:this.$=new yy.Expression({expression:$$[$0-3],direction:$$[$0].toUpperCase(),nocase:true});break;case 244:this.$={limit:$$[$0-1]};yy.extend(this.$,$$[$0]);break;case 246:this.$={offset:$$[$0]};break;case 247:case 450:case 474:case 587:case 594:case 618:case 620:case 624:$$[$0-2].push($$[$0]);this.$=$$[$0-2];break;case 249:case 251:case 253:$$[$0-2].as=$$[$0];this.$=$$[$0-2];break;case 250:case 252:case 254:$$[$0-1].as=$$[$0];this.$=$$[$0-1];break;case 256:this.$=new yy.Column({columid:$$[$0],tableid:$$[$0-2],databaseid:$$[$0-4]});break;case 257:this.$=new yy.Column({columnid:$$[$0],tableid:$$[$0-2]});break;case 258:this.$=new yy.Column({columnid:$$[$0]});break;case 259:this.$=new yy.Column({columnid:$$[$0],tableid:$$[$0-2],databaseid:$$[$0-4]});break;case 260:case 261:this.$=new yy.Column({columnid:$$[$0],tableid:$$[$0-2]});break;case 262:this.$=new yy.Column({columnid:$$[$0]});break;case 277:this.$=new yy.Json({value:$$[$0]});break;case 279:case 280:case 281:if(!yy.queries)yy.queries=[];yy.queries.push($$[$0-1]);$$[$0-1].queriesidx=yy.queries.length;this.$=$$[$0-1];break;case 282:this.$=$$[$0];break;case 283:this.$=new yy.FuncValue({funcid:"CURRENT_TIMESTAMP"});break;case 284:this.$=new yy.JavaScript({value:$$[$0].substr(2,$$[$0].length-4)});break;case 285:this.$=new yy.FuncValue({funcid:$$[$0],newid:true});break;case 286:this.$=$$[$0];yy.extend(this.$,{newid:true});break;case 287:this.$=new yy.Convert({expression:$$[$0-3]});yy.extend(this.$,$$[$0-1]);break;case 288:this.$=new yy.Convert({expression:$$[$0-5],style:$$[$0-1]});yy.extend(this.$,$$[$0-3]);break;case 289:this.$=new yy.Convert({expression:$$[$0-1]});yy.extend(this.$,$$[$0-3]);break;case 290:this.$=new yy.Convert({expression:$$[$0-3],style:$$[$0-1]});yy.extend(this.$,$$[$0-5]);break;case 297:this.$=new yy.FuncValue({funcid:"CURRENT_TIMESTAMP"});break;case 298:if($$[$0-2].length>1&&($$[$0-4].toUpperCase()=="MAX"||$$[$0-4].toUpperCase()=="MIN")){this.$=new yy.FuncValue({funcid:$$[$0-4],args:$$[$0-2]})}else{this.$=new yy.AggrValue({aggregatorid:$$[$0-4].toUpperCase(),expression:$$[$0-2].pop(),over:$$[$0]})}break;case 299:this.$=new yy.AggrValue({aggregatorid:$$[$0-5].toUpperCase(),expression:$$[$0-2],distinct:true,over:$$[$0]});break;case 300:this.$=new yy.AggrValue({aggregatorid:$$[$0-5].toUpperCase(),expression:$$[$0-2],over:$$[$0]});break;case 302:case 303:this.$=new yy.Over;yy.extend(this.$,$$[$0-1]);break;case 304:this.$=new yy.Over;yy.extend(this.$,$$[$0-2]);yy.extend(this.$,$$[$0-1]);break;case 305:this.$={partition:$$[$0]};break;case 306:this.$={order:$$[$0]};break;case 307:this.$="SUM";break;case 308:this.$="COUNT";break;case 309:this.$="MIN";break;case 310:case 486:this.$="MAX";break;case 311:this.$="AVG";break;case 312:this.$="FIRST";break;case 313:this.$="LAST";break;case 314:this.$="AGGR";break;case 315:this.$="ARRAY";break;case 316:var funcid=$$[$0-4];var exprlist=$$[$0-1];if(exprlist.length>1&&(funcid.toUpperCase()=="MIN"||funcid.toUpperCase()=="MAX")){this.$=new yy.FuncValue({funcid:funcid,args:exprlist})}else if(alasql.aggr[$$[$0-4]]){this.$=new yy.AggrValue({aggregatorid:"REDUCE",funcid:funcid,expression:exprlist.pop(),distinct:$$[$0-2]=="DISTINCT"})}else{this.$=new yy.FuncValue({funcid:funcid,args:exprlist})};break;case 317:this.$=new yy.FuncValue({funcid:$$[$0-2]});break;case 319:$$[$0-2].push($$[$0]);this.$=$$[$0-2];break;case 320:this.$=new yy.NumValue({value:+$$[$0]});break;case 321:this.$=new yy.LogicValue({value:true});break;case 322:this.$=new yy.LogicValue({value:false});break;case 323:this.$=new yy.StringValue({value:$$[$0].substr(1,$$[$0].length-2).replace(/(\\\')/g,"'").replace(/(\'\')/g,"'")});break;case 324:this.$=new yy.StringValue({value:$$[$0].substr(2,$$[$0].length-3).replace(/(\\\')/g,"'").replace(/(\'\')/g,"'")});break;case 325:this.$=new yy.NullValue({value:undefined});break;case 326:this.$=new yy.VarValue({variable:$$[$0]});break;case 327:if(!yy.exists)yy.exists=[];this.$=new yy.ExistsValue({value:$$[$0-1],existsidx:yy.exists.length});yy.exists.push($$[$0-1]);break;case 328:case 329:this.$=new yy.ParamValue({param:$$[$0]});break;case 330:if(typeof yy.question=="undefined")yy.question=0;this.$=new yy.ParamValue({param:yy.question++});break;case 331:if(typeof yy.question=="undefined")yy.question=0;this.$=new yy.ParamValue({param:yy.question++,array:true});break;case 332:this.$=new yy.CaseValue({expression:$$[$0-3],whens:$$[$0-2],elses:$$[$0-1]});break;case 333:this.$=new yy.CaseValue({whens:$$[$0-2],elses:$$[$0-1]});break;case 334:case 635:case 636:this.$=$$[$0-1];this.$.push($$[$0]);break;case 336:this.$={when:$$[$0-2],then:$$[$0]};break;case 339:this.$=new yy.Op({left:$$[$0-2],op:"LIKE",right:$$[$0]});break;case 340:this.$=new yy.Op({left:$$[$0-2],op:"NOT LIKE",right:$$[$0]});break;case 341:this.$=new yy.Op({left:$$[$0-2],op:"+",right:$$[$0]});break;case 342:this.$=new yy.Op({left:$$[$0-2],op:"-",right:$$[$0]});break;case 343:this.$=new yy.Op({left:$$[$0-2],op:"*",right:$$[$0]});break;case 344:this.$=new yy.Op({left:$$[$0-2],op:"/",right:$$[$0]});break;case 345:this.$=new yy.Op({left:$$[$0-2],op:"%",right:$$[$0]});break;case 346:this.$=new yy.Op({left:$$[$0-2],op:"^",right:$$[$0]});break;case 347:case 348:case 350:this.$=new yy.Op({left:$$[$0-2],op:"->",right:$$[$0]});break;case 349:this.$=new yy.Op({left:$$[$0-4],op:"->",right:$$[$0-1]});break;case 351:case 352:case 354:this.$=new yy.Op({left:$$[$0-2],op:"!",right:$$[$0]});break;case 353:this.$=new yy.Op({left:$$[$0-4],op:"!",right:$$[$0-1]});break;case 355:this.$=new yy.Op({left:$$[$0-2],op:">",right:$$[$0]});break;case 356:this.$=new yy.Op({left:$$[$0-2],op:">=",right:$$[$0]});break;case 357:this.$=new yy.Op({left:$$[$0-2],op:"<",right:$$[$0]});break;case 358:this.$=new yy.Op({left:$$[$0-2],op:"<=",right:$$[$0]});break;case 359:this.$=new yy.Op({left:$$[$0-2],op:"=",right:$$[$0]});break;case 360:this.$=new yy.Op({left:$$[$0-2],op:"==",right:$$[$0]});break;case 361:this.$=new yy.Op({left:$$[$0-2],op:"===",right:$$[$0]});break;case 362:this.$=new yy.Op({left:$$[$0-2],op:"!=",right:$$[$0]});break;case 363:this.$=new yy.Op({left:$$[$0-2],op:"!==",right:$$[$0]});break;case 364:this.$=new yy.Op({left:$$[$0-2],op:"!===",right:$$[$0]});break;case 365:if(!yy.queries)yy.queries=[];this.$=new yy.Op({left:$$[$0-5],op:$$[$0-4],allsome:$$[$0-3],right:$$[$0-1],queriesidx:yy.queries.length});yy.queries.push($$[$0-1]);break;case 366:this.$=new yy.Op({left:$$[$0-5],op:$$[$0-4],allsome:$$[$0-3],right:$$[$0-1]});break;case 367:if($$[$0-2].op=="BETWEEN1"){if($$[$0-2].left.op=="AND"){this.$=new yy.Op({left:$$[$0-2].left.left,op:"AND",right:new yy.Op({left:$$[$0-2].left.right,op:"BETWEEN",right1:$$[$0-2].right,right2:$$[$0]})})}else{this.$=new yy.Op({left:$$[$0-2].left,op:"BETWEEN",right1:$$[$0-2].right,right2:$$[$0]})}}else if($$[$0-2].op=="NOT BETWEEN1"){if($$[$0-2].left.op=="AND"){this.$=new yy.Op({left:$$[$0-2].left.left,op:"AND",right:new yy.Op({left:$$[$0-2].left.right,op:"NOT BETWEEN",right1:$$[$0-2].right,right2:$$[$0]})})}else{this.$=new yy.Op({left:$$[$0-2].left,op:"NOT BETWEEN",right1:$$[$0-2].right,right2:$$[$0]})}}else{this.$=new yy.Op({left:$$[$0-2],op:"AND",right:$$[$0]})}break;case 368:this.$=new yy.Op({left:$$[$0-2],op:"OR",right:$$[$0]});break;case 369:this.$=new yy.UniOp({op:"NOT",right:$$[$0]});break;case 370:this.$=new yy.UniOp({op:"-",right:$$[$0]});break;case 371:this.$=new yy.UniOp({op:"+",right:$$[$0]});break;case 372:this.$=new yy.UniOp({op:"#",right:$$[$0]});break;case 373:this.$=new yy.UniOp({right:$$[$0-1]});break;case 374:if(!yy.queries)yy.queries=[];this.$=new yy.Op({left:$$[$0-4],op:"IN",right:$$[$0-1],queriesidx:yy.queries.length});yy.queries.push($$[$0-1]);break;case 375:if(!yy.queries)yy.queries=[];this.$=new yy.Op({left:$$[$0-5],op:"NOT IN",right:$$[$0-1],queriesidx:yy.queries.length});yy.queries.push($$[$0-1]);break;case 376:this.$=new yy.Op({left:$$[$0-4],op:"IN",right:$$[$0-1]});break;case 377:this.$=new yy.Op({left:$$[$0-5],op:"NOT IN",right:$$[$0-1]});break;case 378:this.$=new yy.Op({left:$$[$0-3],op:"IN",right:[]});break;case 379:this.$=new yy.Op({left:$$[$0-4],op:"NOT IN",right:[]});break;case 380:case 382:this.$=new yy.Op({left:$$[$0-2],op:"IN",right:$$[$0]});break;case 381:case 383:this.$=new yy.Op({left:$$[$0-3],op:"NOT IN",right:$$[$0]});break;case 384:this.$=new yy.Op({left:$$[$0-2],op:"BETWEEN1",right:$$[$0]});break;case 385:this.$=new yy.Op({left:$$[$0-2],op:"NOT BETWEEN1",right:$$[$0]});break;case 386:this.$=new yy.Op({op:"IS",left:$$[$0-2],right:$$[$0]});break;case 387:this.$=new yy.Convert({expression:$$[$0-2]});yy.extend(this.$,$$[$0]);break;case 388:case 389:this.$=$$[$0];break;case 390:this.$=$$[$0-1];break;case 397:this.$="ALL";break;case 398:this.$="SOME";break;case 399:this.$="ANY";break;case 400:this.$=new yy.Update({table:$$[$0-4],columns:$$[$0-2],where:$$[$0]});break;case 401:this.$=new yy.Update({table:$$[$0-2],columns:$$[$0]});break;case 404:this.$=new yy.SetColumn({column:$$[$0-2],expression:$$[$0]});break;case 405:this.$=new yy.SetColumn({variable:$$[$0-2],expression:$$[$0],method:$$[$0-3]});break;case 406:this.$=new yy.Delete({table:$$[$0-2],where:$$[$0]});break;case 407:this.$=new yy.Delete({table:$$[$0]});break;case 408:this.$=new yy.Insert({into:$$[$0-2],values:$$[$0]});break;case 409:this.$=new yy.Insert({into:$$[$0-2],"default":true});break;case 410:this.$=new yy.Insert({into:$$[$0-5],columns:$$[$0-3],values:$$[$0]});break;case 411:this.$=new yy.Insert({into:$$[$0-1],select:$$[$0]});break;case 412:this.$=new yy.Insert({into:$$[$0-4],columns:$$[$0-2],select:$$[$0]});break;case 415:this.$=[$$[$0-1]];break;case 418:this.$=$$[$0-4];$$[$0-4].push($$[$0-1]);break;case 419:case 420:case 422:case 430:this.$=$$[$0-2];$$[$0-2].push($$[$0]);break;case 431:this.$=new yy.CreateTable({table:$$[$0-4]});yy.extend(this.$,$$[$0-7]);yy.extend(this.$,$$[$0-6]);yy.extend(this.$,$$[$0-5]);yy.extend(this.$,$$[$0-2]);yy.extend(this.$,$$[$0]);break;case 432:this.$=new yy.CreateTable({table:$$[$0]});yy.extend(this.$,$$[$0-3]);yy.extend(this.$,$$[$0-2]);yy.extend(this.$,$$[$0-1]);break;case 434:this.$={"class":true};break;case 443:this.$={temporary:true};break;case 445:this.$={ifnotexists:true};break;case 446:this.$={columns:$$[$0-2],constraints:$$[$0]};break;case 447:this.$={columns:$$[$0]};break;case 448:this.$={as:$$[$0]};break;case 449:case 473:this.$=[$$[$0]];break;case 451:case 452:case 453:case 454:case 455:$$[$0].constraintid=$$[$0-1];this.$=$$[$0];break;case 458:this.$={type:"CHECK",expression:$$[$0-1]};break;case 459:this.$={type:"PRIMARY KEY",columns:$$[$0-1],clustered:($$[$0-3]+"").toUpperCase()};break;case 460:this.$={type:"FOREIGN KEY",columns:$$[$0-5],fktable:$$[$0-2],fkcolumns:$$[$0-1]};break;case 461:case 491:case 522:case 558:case 576:case 579:case 598:this.$=$$[$0-1];break;case 466:this.$={type:"UNIQUE",columns:$$[$0-1],clustered:($$[$0-3]+"").toUpperCase()};break;case 475:this.$=new yy.ColumnDef({columnid:$$[$0-2]});yy.extend(this.$,$$[$0-1]);yy.extend(this.$,$$[$0]);break;case 476:this.$=new yy.ColumnDef({columnid:$$[$0-1]});yy.extend(this.$,$$[$0]);break;case 477:this.$=new yy.ColumnDef({columnid:$$[$0],dbtypeid:""});break;case 478:this.$={dbtypeid:$$[$0-5],dbsize:$$[$0-3],dbprecision:+$$[$0-1]};break;case 479:this.$={dbtypeid:$$[$0-6]+($$[$0-5]?" "+$$[$0-5]:""),dbsize:$$[$0-3],dbprecision:+$$[$0-1]};break;case 480:this.$={dbtypeid:$$[$0-3],dbsize:$$[$0-1]};break;case 481:this.$={dbtypeid:$$[$0-4]+($$[$0-3]?" "+$$[$0-3]:""),dbsize:$$[$0-1]};break;case 482:this.$={dbtypeid:$$[$0]};break;case 483:this.$={dbtypeid:$$[$0-1]+($$[$0]?" "+$$[$0]:"")};break;case 484:this.$={dbtypeid:"ENUM",enumvalues:$$[$0-1]};break;case 485:case 678:this.$=+$$[$0];break;case 487:this.$=undefined;break;case 489:yy.extend($$[$0-1],$$[$0]);this.$=$$[$0-1];break;case 492:this.$={primarykey:true};break;case 493:case 494:this.$={foreignkey:{table:$$[$0-1],columnid:$$[$0]}};break;case 495:this.$={identity:{value:$$[$0-3],step:$$[$0-1]}};break;case 496:this.$={identity:{value:1,step:1}};break;case 497:this.$={"default":$$[$0]};break;case 498:this.$={"default":$$[$0-1]};break;case 499:this.$={"null":true};break;case 500:this.$={notnull:true};break;case 501:this.$={check:$$[$0]};break;case 502:this.$={unique:true};break;case 503:this.$=new yy.DropTable({table:$$[$0],type:$$[$0-2]});yy.extend(this.$,$$[$0-1]);break;case 505:this.$={ifexists:true};break;case 506:this.$=new yy.AlterTable({table:$$[$0-3],renameto:$$[$0]});break;case 507:this.$=new yy.AlterTable({table:$$[$0-3],addcolumn:$$[$0]});break;case 508:this.$=new yy.AlterTable({table:$$[$0-3],modifycolumn:$$[$0]});break;case 509:this.$=new yy.AlterTable({table:$$[$0-5],renamecolumn:$$[$0-2],to:$$[$0]});break;case 510:this.$=new yy.AlterTable({table:$$[$0-3],dropcolumn:$$[$0]});break;case 511:this.$=new yy.AlterTable({table:$$[$0-2],renameto:$$[$0]});break;case 512:this.$=new yy.AttachDatabase({databaseid:$$[$0],engineid:$$[$0-2].toUpperCase()});break;case 513:this.$=new yy.AttachDatabase({databaseid:$$[$0-3],engineid:$$[$0-5].toUpperCase(),args:$$[$0-1]});break;case 514:this.$=new yy.AttachDatabase({databaseid:$$[$0-2],engineid:$$[$0-4].toUpperCase(),as:$$[$0]});break;case 515:this.$=new yy.AttachDatabase({databaseid:$$[$0-5],engineid:$$[$0-7].toUpperCase(),as:$$[$0],args:$$[$0-3]});break;case 516:this.$=new yy.DetachDatabase({databaseid:$$[$0]});break;case 517:this.$=new yy.CreateDatabase({databaseid:$$[$0]});yy.extend(this.$,$$[$0]);break;case 518:this.$=new yy.CreateDatabase({engineid:$$[$0-4].toUpperCase(),databaseid:$$[$0-1],as:$$[$0]});yy.extend(this.$,$$[$0-2]);break;case 519:this.$=new yy.CreateDatabase({engineid:$$[$0-7].toUpperCase(),databaseid:$$[$0-4],args:$$[$0-2],as:$$[$0]});yy.extend(this.$,$$[$0-5]);break;case 520:this.$=new yy.CreateDatabase({engineid:$$[$0-4].toUpperCase(),as:$$[$0],args:[$$[$0-1]]});yy.extend(this.$,$$[$0-2]);break;case 521:this.$=undefined;break;case 523:case 524:this.$=new yy.UseDatabase({databaseid:$$[$0]});break;case 525:this.$=new yy.DropDatabase({databaseid:$$[$0]});yy.extend(this.$,$$[$0-1]);break;case 526:case 527:this.$=new yy.DropDatabase({databaseid:$$[$0],engineid:$$[$0-3].toUpperCase()});yy.extend(this.$,$$[$0-1]);break;case 528:this.$=new yy.CreateIndex({indexid:$$[$0-5],table:$$[$0-3],columns:$$[$0-1]});break;case 529:this.$=new yy.CreateIndex({indexid:$$[$0-5],table:$$[$0-3],columns:$$[$0-1],unique:true});break;case 530:this.$=new yy.DropIndex({indexid:$$[$0]});break;case 531:this.$=new yy.ShowDatabases;break;case 532:this.$=new yy.ShowDatabases({like:$$[$0]});break;case 533:this.$=new yy.ShowDatabases({engineid:$$[$0-1].toUpperCase()});break;case 534:this.$=new yy.ShowDatabases({engineid:$$[$0-3].toUpperCase(),like:$$[$0]});break;case 535:this.$=new yy.ShowTables;break;case 536:this.$=new yy.ShowTables({like:$$[$0]});break;case 537:this.$=new yy.ShowTables({databaseid:$$[$0]});break;case 538:this.$=new yy.ShowTables({like:$$[$0],databaseid:$$[$0-2]});break;case 539:this.$=new yy.ShowColumns({table:$$[$0]});break;case 540:this.$=new yy.ShowColumns({table:$$[$0-2],databaseid:$$[$0]});break;case 541:this.$=new yy.ShowIndex({table:$$[$0]});break;case 542:this.$=new yy.ShowIndex({table:$$[$0-2],databaseid:$$[$0]});break;case 543:this.$=new yy.ShowCreateTable({table:$$[$0]});break;case 544:this.$=new yy.ShowCreateTable({table:$$[$0-2],databaseid:$$[$0]});break;case 545:this.$=new yy.CreateTable({table:$$[$0-6],view:true,select:$$[$0-1],viewcolumns:$$[$0-4]});yy.extend(this.$,$$[$0-9]);yy.extend(this.$,$$[$0-7]);break;case 546:this.$=new yy.CreateTable({table:$$[$0-3],view:true,select:$$[$0-1]});yy.extend(this.$,$$[$0-6]);yy.extend(this.$,$$[$0-4]);break;case 550:this.$=new yy.DropTable({table:$$[$0],view:true});yy.extend(this.$,$$[$0-1]);break;case 551:this.$=new yy.Help({subject:$$[$0].value.toUpperCase()});break;case 552:this.$=new yy.Help;break;case 553:this.$=new yy.ExpressionStatement({expression:$$[$0]});break;case 554:this.$=new yy.Source({url:$$[$0].value});break;case 555:this.$=new yy.Assert({value:$$[$0]});break;case 556:this.$=new yy.Assert({value:$$[$0].value});break;case 557:this.$=new yy.Assert({value:$$[$0],message:$$[$0-2]});break;case 559:case 570:case 572:this.$=$$[$0].value;break;case 560:case 568:this.$=+$$[$0].value;break;case 561:this.$=!!$$[$0].value;break;case 569:this.$=""+$$[$0].value;break;case 575:this.$=$$[$0-1];break;case 577:case 580:this.$=$$[$0-2];break;case 578:this.$={};break;case 581:this.$=[];break;case 582:yy.extend($$[$0-2],$$[$0]);this.$=$$[$0-2];break;case 584:this.$={};this.$[$$[$0-2].substr(1,$$[$0-2].length-2)]=$$[$0];break;case 585:case 586:this.$={};this.$[$$[$0-2]]=$$[$0];break;case 589:this.$=new yy.SetVariable({variable:$$[$0-1].toLowerCase(),value:$$[$0]});break;case 590:this.$=new yy.SetVariable({variable:$$[$0-2],expression:$$[$0],method:$$[$0-3]});break;case 591:this.$=new yy.SetVariable({variable:$$[$0-3],props:$$[$0-2],expression:$$[$0],method:$$[$0-4]});break;case 592:this.$="@";break;case 593:this.$="$";break;case 599:this.$=true;break;case 600:this.$=false;break;case 601:this.$=new yy.CommitTransaction;break;case 602:this.$=new yy.RollbackTransaction;break;case 603:this.$=new yy.BeginTransaction;break;case 604:this.$=new yy.If({expression:$$[$0-2],thenstat:$$[$0-1],elsestat:$$[$0]});if($$[$0-1].exists)this.$.exists=$$[$0-1].exists;if($$[$0-1].queries)this.$.queries=$$[$0-1].queries;break;case 605:this.$=new yy.If({expression:$$[$0-1],thenstat:$$[$0]});if($$[$0].exists)this.$.exists=$$[$0].exists;if($$[$0].queries)this.$.queries=$$[$0].queries;break;case 606:this.$=$$[$0];break;case 607:this.$=new yy.While({expression:$$[$0-1],loopstat:$$[$0]});if($$[$0].exists)this.$.exists=$$[$0].exists;if($$[$0].queries)this.$.queries=$$[$0].queries;break;case 608:this.$=new yy.Continue;break;case 609:this.$=new yy.Break;break;case 610:this.$=new yy.BeginEnd({statements:$$[$0-1]});break;case 611:this.$=new yy.Print({exprs:$$[$0]});break;case 612:this.$=new yy.Print({select:$$[$0]});break;case 613:this.$=new yy.Require({paths:$$[$0]});break;case 614:this.$=new yy.Require({plugins:$$[$0]});break;case 615:case 616:this.$=$$[$0].toUpperCase();break;case 617:this.$=new yy.Echo({expr:$$[$0]});break;case 622:this.$=new yy.Declare({declares:$$[$0]});break;case 625:this.$={variable:$$[$0-1]};yy.extend(this.$,$$[$0]);break;case 626:this.$={variable:$$[$0-2]};yy.extend(this.$,$$[$0]);break;case 627:this.$={variable:$$[$0-3],expression:$$[$0]};yy.extend(this.$,$$[$0-2]);break;case 628:this.$={variable:$$[$0-4],expression:$$[$0]};yy.extend(this.$,$$[$0-2]);break;case 629:this.$=new yy.TruncateTable({table:$$[$0]});break;case 630:this.$=new yy.Merge;yy.extend(this.$,$$[$0-4]);yy.extend(this.$,$$[$0-3]);yy.extend(this.$,$$[$0-2]);yy.extend(this.$,{matches:$$[$0-1]});yy.extend(this.$,$$[$0]);break;case 631:case 632:this.$={into:$$[$0]};break;case 634:this.$={on:$$[$0]};break;case 639:this.$={matched:true,action:$$[$0]};break;case 640:this.$={matched:true,expr:$$[$0-2],action:$$[$0]};break;case 641:this.$={"delete":true};break;case 642:this.$={update:$$[$0]};break;case 643:case 644:this.$={matched:false,bytarget:true,action:$$[$0]};break;case 645:case 646:this.$={matched:false,bytarget:true,expr:$$[$0-2],action:$$[$0]};break;case 647:this.$={matched:false,bysource:true,action:$$[$0]};break;case 648:this.$={matched:false,bysource:true,expr:$$[$0-2],action:$$[$0]};break;case 649:this.$={insert:true,values:$$[$0]};break;case 650:this.$={insert:true,values:$$[$0],columns:$$[$0-3]};break;case 651:this.$={insert:true,defaultvalues:true};break;case 652:this.$={insert:true,defaultvalues:true,columns:$$[$0-3]};break;case 654:this.$={output:{columns:$$[$0]}};break;case 655:this.$={output:{columns:$$[$0-3],intovar:$$[$0],method:$$[$0-1]}};break;case 656:this.$={output:{columns:$$[$0-2],intotable:$$[$0]}};break;case 657:this.$={output:{columns:$$[$0-5],intotable:$$[$0-3],intocolumns:$$[$0-1]}};break;case 658:this.$=new yy.CreateVertex({"class":$$[$0-3],sharp:$$[$0-2],name:$$[$0-1]});yy.extend(this.$,$$[$0]);break;case 661:this.$={sets:$$[$0]};break;case 662:this.$={content:$$[$0]};break;case 663:this.$={select:$$[$0]};break;case 664:this.$=new yy.CreateEdge({from:$$[$0-3],to:$$[$0-1],name:$$[$0-5]});yy.extend(this.$,$$[$0]);break;case 665:this.$=new yy.CreateGraph({graph:$$[$0]});break;case 666:this.$=new yy.CreateGraph({from:$$[$0]});break;case 669:this.$=$$[$0-2];if($$[$0-1])this.$.json=new yy.Json({value:$$[$0-1]});if($$[$0])this.$.as=$$[$0];break;case 670:this.$={source:$$[$0-6],target:$$[$0]};if($$[$0-3])this.$.json=new yy.Json({value:$$[$0-3]});if($$[$0-2])this.$.as=$$[$0-2];yy.extend(this.$,$$[$0-4]);break;case 672:this.$={vars:$$[$0],method:$$[$0-1]};break;case 675:var s3=$$[$0-1];this.$={prop:$$[$0-3],sharp:$$[$0-2],name:typeof s3=="undefined"?undefined:s3.substr(1,s3.length-2),"class":$$[$0]};break;case 681:this.$=new yy.AddRule({left:$$[$0-2],right:$$[$0]});break;case 682:this.$=new yy.AddRule({right:$$[$0]});break;case 685:this.$=new yy.Term({termid:$$[$0]});break;case 686:this.$=new yy.Term({termid:$$[$0-3],args:$$[$0-1]});break;case 692:case 706:case 708:case 710:case 714:case 716:case 718:case 720:case 722:case 724:this.$=[];break;case 693:case 701:case 703:case 707:case 709:case 711:case 715:case 717:case 719:case 721:case 723:case 725:$$[$0-1].push($$[$0]);break;case 700:case 702:this.$=[$$[$0]];break}},table:[o([8,450,451],$V0,{6:1,7:2,10:3,11:4,15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,4:$V1,5:$V2,12:$V3,47:$V4,65:$V5,81:$V6,99:$V7,120:$V8,130:$V9,163:$Va,233:$Vb,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),{1:[3]},{8:[1,97],9:98,450:$VE,451:$VF},o($VG,[2,5]),o($VG,[2,6]),o($VH,[2,9]),o($VG,$V0,{15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,10:101,4:$V1,5:$V2,13:[1,102],47:$V4,65:$V5,81:$V6,99:$V7,120:$V8,130:$V9,163:$Va,233:$Vb,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),o($VH,[2,11]),o($VH,[2,12]),o($VH,[2,13]),o($VH,[2,14]),o($VH,[2,15]),o($VH,[2,16]),o($VH,[2,17]),o($VH,[2,18]),o($VH,[2,19]),o($VH,[2,20]),o($VH,[2,21]),o($VH,[2,22]),o($VH,[2,23]),o($VH,[2,24]),o($VH,[2,25]),o($VH,[2,26]),o($VH,[2,27]),o($VH,[2,28]),o($VH,[2,29]),o($VH,[2,30]),o($VH,[2,31]),o($VH,[2,32]),o($VH,[2,33]),o($VH,[2,34]),o($VH,[2,35]),o($VH,[2,36]),o($VH,[2,37]),o($VH,[2,38]),o($VH,[2,39]),o($VH,[2,40]),o($VH,[2,41]),o($VH,[2,42]),o($VH,[2,43]),o($VH,[2,44]),o($VH,[2,45]),o($VH,[2,46]),o($VH,[2,47]),o($VH,[2,48]),o($VH,[2,49]),o($VH,[2,50]),o($VH,[2,51]),o($VH,[2,52]),o($VH,[2,53]),o($VH,[2,54]),o($VH,[2,55]),o($VH,[2,56]),o($VH,[2,57]),o($VH,[2,58]),o($VH,[2,59]),{304:[1,103]},{3:104,4:$V1,5:$V2},o($VI,[2,442],{3:106,299:110,4:$V1,5:$V2,109:$VJ,110:$VK,161:[1,108],167:[1,107],308:[1,113],352:[1,105],421:[1,109]}),{119:$VL,398:114,399:115},{157:[1,117]},{352:[1,118]},{3:120,4:$V1,5:$V2,105:[1,125],167:[1,121],304:[1,124],345:122,352:[1,119],357:[1,123]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:126,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vi1,[2,413],{292:181,172:[1,182]}),{3:193,4:$V1,5:$V2,70:$Vj1,107:$Vk1,116:$VQ,118:187,119:$VR,126:$VS,155:$VW,172:[1,185],173:188,174:190,175:189,176:191,183:184,186:192,260:$Vd1,367:168,368:$Vg1,372:$Vh1,402:183},{304:[1,195]},o($Vl1,[2,690],{73:196,84:197,85:[1,198]}),o($Vm1,[2,692],{82:199}),{3:203,4:$V1,5:$V2,164:[1,201],167:[1,204],298:[1,200],304:[1,205],352:[1,202]},{304:[1,206]},{3:209,4:$V1,5:$V2,66:207,68:208},o([264,450,451],$V0,{10:3,11:4,15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,7:211,4:$V1,5:$V2,12:$V3,47:$V4,65:$V5,81:$V6,99:$V7,120:$V8,130:$V9,163:$Va,233:$Vb,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,384:[1,210],385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),{384:[1,212]},{384:[1,213]},{3:215,4:$V1,5:$V2,352:[1,214]},{3:217,4:$V1,5:$V2,173:216},o($VH,[2,552],{91:218,107:$VO,256:$Va1}),o($Vn1,[2,284]),{91:219,107:$VO,256:$Va1},{3:231,4:$V1,5:$V2,91:225,106:$VN,107:[1,222],116:$VQ,118:220,119:$Vo1,126:$VS,155:$VW,170:224,174:229,175:228,225:226,226:227,232:$Vp1,238:221,254:$V81,255:$V91,256:$Va1,257:$Vb1,260:$Vd1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:232,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VH,[2,608]),o($VH,[2,609]),{3:147,4:$V1,5:$V2,36:234,51:144,70:$VM,72:67,81:$V6,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:233,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,158:92,163:$Va,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:241,4:$V1,5:$V2,91:238,107:$VO,256:$Va1,393:236,394:237,395:239,396:$Vq1},{3:242,4:$V1,5:$V2,116:$Vr1,119:$Vs1,379:243},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:246,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{447:[1,247]},{3:93,4:$V1,5:$V2,446:249,448:248},{3:231,4:$V1,5:$V2,174:250},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:251,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vt1,$Vu1,{160:255,138:[1,254],159:[1,252],161:[1,253],169:$Vv1}),o($Vw1,[2,685],{70:[1,257]}),o($Vx1,[2,130],{123:[1,258],124:[1,259],164:[1,260],165:[1,261],166:[1,262],167:[1,263],168:[1,264]}),o($Vy1,[2,1]),o($Vy1,[2,2]),{1:[2,3]},o($VG,$V0,{15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,10:265,4:$V1,5:$V2,47:$V4,65:$V5,81:$V6,99:$V7,120:$V8,130:$V9,163:$Va,233:$Vb,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),o($Vz1,[2,688]),o($Vz1,[2,689]),o($VG,[2,7]),{14:[1,266]},{3:217,4:$V1,5:$V2,173:267},{352:[1,268]},o($Vi1,$VA1,{301:269,130:$VB1}),{352:[1,271]},{3:272,4:$V1,5:$V2},{167:[1,273]},o([8,67,69,107,112,114,126,264,268,368,372,450,451],$VC1,{422:274,423:276,424:277,427:278,3:279,434:280,431:281,379:282,4:$V1,5:$V2,116:$Vr1,119:$Vs1,157:[1,275]}),{105:[1,286],300:283,304:[1,285],357:[1,284]},{91:288,107:$VO,157:[2,770],256:$Va1,420:287},o($VD1,[2,764],{414:289,3:290,4:$V1,5:$V2}),o($VI,[2,443]),o($VH,[2,622],{67:[1,291]}),o($VE1,[2,623]),{3:292,4:$V1,5:$V2},{3:217,4:$V1,5:$V2,173:293},{3:294,4:$V1,5:$V2},o($Vi1,$VF1,{346:295,130:$VG1}),{352:[1,297]},{3:298,4:$V1,5:$V2},o($Vi1,$VF1,{346:299,130:$VG1}),o($Vi1,$VF1,{346:300,130:$VG1}),o($VH1,[2,758]),o($VH1,[2,759]),o($VH,$V0,{15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,10:301,278:322,4:$V1,5:$V2,47:$V4,65:$V5,81:$V6,90:$VI1,97:$VJ1,98:$VK1,99:$VL1,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,120:$V8,128:$VR1,130:$V9,144:$VS1,145:$VT1,153:$VU1,154:$VV1,163:$Va,233:$Vb,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),o($Vn1,[2,263]),o($Vn1,[2,264]),o($Vn1,[2,265]),o($Vn1,[2,266]),o($Vn1,[2,267]),o($Vn1,[2,268]),o($Vn1,[2,269]),o($Vn1,[2,270]),o($Vn1,[2,271]),o($Vn1,[2,272]),o($Vn1,[2,273]),o($Vn1,[2,274]),o($Vn1,[2,275]),o($Vn1,[2,276]),o($Vn1,[2,277]),o($Vn1,[2,278]),{3:147,4:$V1,5:$V2,23:336,24:335,33:332,36:331,51:144,70:$VM,72:67,81:$V6,89:130,91:134,100:334,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,158:92,163:$Va,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,231:333,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,291:$Ve,298:[1,337],367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,282]),o($Vn1,[2,283]),{70:[1,338]},o([4,5,8,47,65,67,69,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$V82,{70:$V92,117:[1,340]}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:341,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:342,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:343,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:344,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,258]),o([4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,202,203,205,212,215,216,218,220,232,233,234,235,237,244,245,246,247,248,249,250,251,252,254,255,256,257,258,260,261,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,294,298,307,319,320,324,325,344,347,348,351,353,355,356,364,365,366,368,372,374,376,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451,452],[2,320]),o($Va2,[2,321]),o($Va2,[2,322]),o($Va2,$Vb2),o($Va2,[2,324]),o([4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,257,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,294,298,307,319,320,324,325,344,347,348,351,353,355,356,364,365,366,368,372,374,376,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,325]),{3:346,4:$V1,5:$V2,106:[1,347],259:345},{3:348,4:$V1,5:$V2},o($Va2,[2,330]),o($Va2,[2,331]),{3:349,4:$V1,5:$V2,70:$Vc2,91:351,106:$VN,107:$VO,116:$VQ,126:$VS,155:$VW,170:352,175:354,225:353,254:$V81,255:$V91,256:$Va1,260:$Vd1,367:355,372:$Vh1},{70:[1,356]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:357,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,262:358,265:359,266:$Vd2,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{70:[1,361]},{70:[1,362]},o($Ve2,[2,563]),{3:377,4:$V1,5:$V2,70:$Vf2,89:372,91:370,106:$VN,107:$VO,116:$VQ,118:367,119:$Vo1,126:$VS,155:$VW,170:369,174:375,175:374,225:371,226:373,254:$V81,255:$V91,256:$Va1,257:$Vb1,260:$Vd1,367:168,368:$Vg1,369:363,370:366,371:368,372:$Vh1,375:364,376:[1,365]},{3:378,4:$V1,5:$V2,174:379},{70:[2,307]},{70:[2,308]},{70:[2,309]},{70:[2,310]},{70:[2,311]},{70:[2,312]},{70:[2,313]},{70:[2,314]},{70:[2,315]},{3:385,4:$V1,5:$V2,106:$Vg2,107:$Vh2,373:380,374:[1,381],377:382},{3:217,4:$V1,5:$V2,173:386},o($Vi1,[2,414]),{203:[1,388],403:387},{203:[2,631]},{3:193,4:$V1,5:$V2,70:$Vj1,107:$Vk1,116:$VQ,118:187,119:$VR,126:$VS,155:$VW,173:188,174:190,175:189,176:191,183:389,186:192,260:$Vd1,367:168,368:$Vg1,372:$Vh1},{36:390,72:67,81:$V6,158:92,163:$Va},o($Vi2,[2,730],{184:391,69:[1,392]}),o($Vj2,[2,163],{3:393,4:$V1,5:$V2,69:[1,394]}),o($Vj2,[2,166],{3:395,4:$V1,5:$V2,69:[1,396]}),o($Vj2,[2,167],{3:397,4:$V1,5:$V2,69:[1,398]}),o($Vj2,[2,170],{3:399,4:$V1,5:$V2,69:[1,400]}),o($Vj2,[2,173],{3:401,4:$V1,5:$V2,69:[1,402]}),o([4,5,8,65,67,69,71,93,104,136,142,143,157,180,182,194,195,196,197,198,199,200,201,202,203,205,218,264,268,450,451],$Vk2,{70:$V92,117:$Vl2}),o([4,5,8,65,67,69,71,93,104,136,142,143,180,182,194,195,196,197,198,199,200,201,202,203,205,218,264,268,450,451],[2,176]),{3:217,4:$V1,5:$V2,173:404},o($Vm2,$Vn2,{74:405,172:$Vo2}),o($Vl1,[2,691]),o($Vp2,[2,696],{86:407,164:[1,408]}),o([8,71,157,264,268,450,451],$Vn2,{367:168,74:409,92:410,3:411,118:433,132:443,134:444,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,90:$Vt2,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,172:$Vo2,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,368:$Vg1,372:$Vh1}),{304:[1,457]},{157:[1,458]},o($VH,[2,531],{90:[1,459]}),{352:[1,460]},{157:[1,461]},o($VH,[2,535],{90:[1,462],157:[1,463]}),{3:217,4:$V1,5:$V2,173:464},{36:465,67:[1,466],72:67,81:$V6,158:92,163:$Va},o($V43,[2,62]),{69:[1,467]},o($VH,[2,603]),{9:98,264:[1,468],450:$VE,451:$VF},o($VH,[2,601]),o($VH,[2,602]),{3:469,4:$V1,5:$V2},o($VH,[2,524]),{120:[1,470]},o([8,67,69,70,71,81,104,120,122,123,128,157,161,163,202,257,264,268,294,307,319,320,324,325,344,348,349,350,450,451],$Vk2,{117:$Vl2}),o($VH,[2,551]),o($VH,[2,554]),o($VH,[2,555]),o($VH,[2,556]),o($VH,$Vb2,{67:[1,471]}),{70:$Vc2,91:351,106:$VN,107:$VO,116:$VQ,126:$VS,155:$VW,170:352,175:354,225:353,254:$V81,255:$V91,256:$Va1,260:$Vd1,367:355,372:$Vh1},o($V53,[2,291]),o($V53,[2,292]),o($V53,[2,293]),o($V53,[2,294]),o($V53,[2,295]),o($V53,[2,296]),o($V53,[2,297]),{70:$V92},o($VH,$V0,{15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,278:322,10:472,4:$V1,5:$V2,47:$V4,65:$V5,81:$V6,90:$VI1,97:$VJ1,98:$VK1,99:$VL1,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,120:$V8,128:$VR1,130:$V9,144:$VS1,145:$VT1,153:$VU1,154:$VV1,163:$Va,233:$Vb,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),o($VH,[2,611],{67:$V63}),o($VH,[2,612]),o($V73,[2,318],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($VH,[2,613],{67:[1,475]}),o($VH,[2,614],{67:[1,476]}),o($VE1,[2,619]),o($VE1,[2,621]),o($VE1,[2,615]),o($VE1,[2,616]),{202:[1,478],378:477,382:[1,479]},{3:480,4:$V1,5:$V2},o($Vi1,[2,592]),o($Vi1,[2,593]),o($VH,[2,553],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{3:93,4:$V1,5:$V2,446:249,448:481},o($VH,[2,682],{67:$V93}),o($V73,[2,684]),o($VH,[2,687]),o($VH,[2,617],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($Va3,$Vu1,{160:483,169:$Vv1}),o($Va3,$Vu1,{160:484,169:$Vv1}),o($Va3,$Vu1,{160:485,169:$Vv1}),o($Vb3,[2,726],{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,162:486,148:487,221:488,100:489,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),{70:[1,491],106:$VN,170:490},{3:93,4:$V1,5:$V2,446:249,448:492},o($Vx1,[2,131]),o($Vx1,[2,132]),o($Vx1,[2,133]),o($Vx1,[2,134]),o($Vx1,[2,135]),o($Vx1,[2,136]),o($Vx1,[2,137]),o($VG,[2,4]),o($VG,$V0,{15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,10:493,4:$V1,5:$V2,47:$V4,65:$V5,81:$V6,99:$V7,120:$V8,130:$V9,163:$Va,233:$Vb,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),{344:[1,497],348:[1,494],349:[1,495],350:[1,496]},{3:498,4:$V1,5:$V2},{3:499,4:$V1,5:$V2},{128:[1,500]},o($Vc3,$VA1,{301:501,130:$VB1}),{202:[1,502]},{3:503,4:$V1,5:$V2},o($VH,[2,665],{67:$Vd3}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:505,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($V73,[2,668]),o($Ve3,[2,772],{367:168,425:506,118:507,114:[2,776],119:$Vo1,368:$Vg1,372:$Vh1}),{114:[1,508]},o($Vf3,$Vg3,{70:[1,509]}),o($Vh3,[2,786],{435:510,439:511,112:[1,512]}),{114:[2,777]},{3:513,4:$V1,5:$V2},o($Vi1,$VA1,{301:514,130:$VB1}),o($Vi1,$VA1,{301:515,130:$VB1}),o($VH1,[2,433]),o($VH1,[2,434]),{157:[1,516]},{157:[2,771]},o($Vi3,[2,766],{415:517,418:518,112:[1,519]}),o($VD1,[2,765]),{119:$VL,399:520},{4:$Vj3,69:[1,522],236:521,338:$Vk3},o($VH,[2,407],{104:[1,525]}),o($VH,[2,516]),{3:526,4:$V1,5:$V2},{258:[1,527]},o($Vc3,$VF1,{346:528,130:$VG1}),o($VH,[2,530]),{3:217,4:$V1,5:$V2,173:529},{3:217,4:$V1,5:$V2,173:530},o($VG,[2,605],{387:531,268:[1,532]}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:533,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:534,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:535,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:536,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:537,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:538,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:539,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:540,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:541,4:$V1,5:$V2,70:[1,543],106:$VN,170:542,174:544},{3:545,4:$V1,5:$V2,70:[1,547],106:$VN,170:546,174:548},o($Vl3,[2,391],{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,100:549,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),o($Vl3,[2,392],{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,100:550,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),o($Vl3,[2,393],{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,100:551,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),o($Vl3,[2,394],{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,100:552,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),o($Vl3,$Vm3,{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,100:553,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:554,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:555,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vl3,[2,396],{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,100:556,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:557,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:558,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{138:[1,560],140:[1,562],279:559,286:[1,561]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:563,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:564,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:377,4:$V1,5:$V2,70:[1,565],89:568,119:$Vn3,174:569,176:567,281:566},{280:[1,571]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:572,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:573,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:574,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{4:$Vj3,236:575,338:$Vk3},{71:[1,576]},{71:[1,577]},{71:[1,578]},{71:[1,579],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{71:[2,738]},{71:[2,739]},{109:$VJ,110:$VK},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:580,126:$VS,128:$VT,132:146,138:[1,582],153:$VU,154:$VV,155:$VW,159:[1,581],170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Va3,[2,742],{253:583,453:585,71:[1,584],138:[1,587],159:[1,586]}),{3:588,4:$V1,5:$V2,123:$Vo3,154:[1,590]},o([4,5,8,47,65,67,69,70,71,81,85,93,99,104,105,106,107,109,110,112,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,271,272,273,274,275,276,277,280,282,283,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,369],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,284:$V62}),o($Vp3,[2,370],{278:322,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,154:$VV1}),o($Vp3,[2,371],{278:322,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,154:$VV1}),o($Vn1,[2,372],{278:322}),o($Va2,[2,328]),o($Va2,[2,744]),o($Va2,[2,745]),o($Va2,[2,329]),o([4,5,8,47,65,67,69,70,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,202,203,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,326]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:591,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Ve2,[2,559]),o($Ve2,[2,560]),o($Ve2,[2,561]),o($Ve2,[2,562]),o($Ve2,[2,564]),{36:592,72:67,81:$V6,158:92,163:$Va},{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,262:593,265:359,266:$Vd2,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{263:594,264:$Vq3,265:595,266:$Vd2,268:$Vr3},o($Vs3,[2,335]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:597,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:598,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{4:$Vj3,236:599,338:$Vk3},o($Ve2,[2,565]),{67:[1,601],376:[1,600]},o($Ve2,[2,581]),o($Vt3,[2,588]),o($Vu3,[2,566]),o($Vu3,[2,567]),o($Vu3,[2,568]),o($Vu3,[2,569]),o($Vu3,[2,570]),o($Vu3,[2,571]),o($Vu3,[2,572]),o($Vu3,[2,573]),o($Vu3,[2,574]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:602,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o([4,5,8,47,65,67,69,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,374,376,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],$V82,{70:$V92,117:$Vv3}),o($Vw3,[2,285],{70:$V92}),o($Vn1,[2,286]),{67:[1,605],374:[1,604]},o($Ve2,[2,578]),o($Vx3,[2,583]),{126:[1,606]},{126:[1,607]},{126:[1,608]},{36:612,70:[1,611],72:67,81:$V6,123:[1,609],158:92,163:$Va,294:[1,610]},{202:[1,614],404:613},{3:193,4:$V1,5:$V2,70:$Vj1,107:$Vk1,116:$VQ,118:187,119:$VR,126:$VS,155:$VW,173:188,174:190,175:189,176:191,183:615,186:192,260:$Vd1,367:168,368:$Vg1,372:$Vh1},{203:[2,632]},{71:[1,616]},o($Vj2,[2,732],{185:617,3:618,4:$V1,5:$V2}),o($Vi2,[2,731]),o($Vj2,[2,161]),{3:619,4:$V1,5:$V2},o($Vj2,[2,164]),{3:620,4:$V1,5:$V2},o($Vj2,[2,168]),{3:621,4:$V1,5:$V2},o($Vj2,[2,171]),{3:622,4:$V1,5:$V2},o($Vj2,[2,174]),{3:623,4:$V1,5:$V2},{3:624,4:$V1,5:$V2},{122:[1,625]},o($Vy3,[2,150],{75:626,157:[1,627]}),{3:193,4:$V1,5:$V2,107:[1,632],116:$VQ,119:[1,633],126:$VS,155:$VW,173:628,174:629,175:630,176:631,260:$Vd1},{3:638,4:$V1,5:$V2,87:634,88:635,89:636,90:$Vz3},o($Vp2,[2,697]),o($VA3,[2,694],{83:639,156:640,157:[1,641]}),o($Vm1,[2,693],{127:642,153:$VB3,154:$VC3,155:$VD3}),o([4,5,8,65,67,69,71,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,172,244,245,246,247,248,249,250,251,252,264,268,368,372,450,451],[2,71],{70:[1,646]}),{94:[1,647]},{3:648,4:$V1,5:$V2},o($VE3,[2,75]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:649,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:650,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,90:$Vt2,92:652,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,101:651,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{70:[1,653]},{70:[1,654]},{70:[1,655]},o($VE3,[2,83]),o($VE3,[2,84]),o($VE3,[2,85]),o($VE3,[2,86]),o($VE3,[2,87]),o($VE3,[2,88]),{3:656,4:$V1,5:$V2},{3:657,4:$V1,5:$V2,108:[1,658]},o($VE3,[2,92]),o($VE3,[2,93]),o($VE3,[2,94]),{117:[1,659]},o($VE3,[2,96]),{3:660,4:$V1,5:$V2,70:$Vc2,91:351,106:$VN,107:$VO,116:$VQ,126:$VS,155:$VW,170:352,175:354,225:353,254:$V81,255:$V91,256:$Va1,260:$Vd1,367:355,372:$Vh1},{119:[1,661]},{70:[1,662]},{119:[1,663]},o($VE3,[2,101]),{70:[1,664]},{3:665,4:$V1,5:$V2},{70:[1,666]},{70:[1,667]},{70:[1,668]},{70:[1,669]},{70:[1,670],138:[1,671]},{70:[1,672]},{70:[1,673]},{70:[1,674]},{70:[1,675]},{70:[1,676]},{70:[1,677]},{70:[1,678]},{70:[1,679]},{70:[1,680]},{70:[2,712]},{70:[2,713]},{3:217,4:$V1,5:$V2,173:681},{3:217,4:$V1,5:$V2,173:682},{91:683,107:$VO,256:$Va1},o($VH,[2,533],{90:[1,684]}),{3:217,4:$V1,5:$V2,173:685},{91:686,107:$VO,256:$Va1},{3:687,4:$V1,5:$V2},o($VH,[2,629]),o($VH,[2,60]),{3:209,4:$V1,5:$V2,68:688},{70:[1,689]},o($VH,[2,610]),o($VH,[2,523]),{3:638,4:$V1,5:$V2,89:692,116:$VF3,119:$VG3,121:690,288:691,289:693},{118:696,119:$Vo1,367:168,368:$Vg1,372:$Vh1},o($VH,[2,607]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:697,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vl3,$Vm3,{223:127,174:128,224:129,89:130,222:131,170:132,225:133,91:134,226:135,175:136,176:137,227:138,228:139,229:140,118:141,230:142,51:144,132:146,3:147,367:168,100:698,4:$V1,5:$V2,70:$VM,106:$VN,107:$VO,112:$VP,116:$VQ,119:$VR,126:$VS,128:$VT,153:$VU,154:$VV,155:$VW,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,368:$Vg1,372:$Vh1}),{91:699,107:$VO,256:$Va1},{3:241,4:$V1,5:$V2,395:700,396:$Vq1},o($VH,[2,589]),o($VH,[2,599]),o($VH,[2,600]),{97:[1,703],99:[1,701],380:702},o($VH,[2,681],{67:$V93}),{3:93,4:$V1,5:$V2,446:704},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:489,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,148:705,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,221:488,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:489,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,148:706,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,221:488,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:489,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,148:707,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,221:488,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vb3,[2,129]),o($Vb3,[2,727],{67:$VH3}),o($VI3,[2,248]),o($VI3,[2,255],{278:322,3:710,91:712,4:$V1,5:$V2,69:[1,709],90:$VI1,97:$VJ1,98:$VK1,99:$V83,106:[1,711],107:$VO,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,256:$Va1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($Vt1,[2,728],{171:713,452:[1,714]}),{106:$VN,170:715},{67:$V93,71:[1,716]},o($VG,[2,8]),{122:[1,717],164:[1,718]},{164:[1,719]},{164:[1,720]},{164:[1,721]},o($VH,[2,512],{69:[1,723],70:[1,722]}),o($VH,[2,517]),{258:[1,724]},{3:725,4:$V1,5:$V2,91:726,107:$VO,256:$Va1},{3:217,4:$V1,5:$V2,173:727},{202:[1,728]},o([8,67,69,71,107,112,114,126,264,268,368,372,450,451],$VC1,{424:277,427:278,3:279,434:280,431:281,379:282,423:729,4:$V1,5:$V2,116:$Vr1,119:$Vs1}),o($VH,[2,666],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($V73,[2,774],{426:730,432:731,69:$VJ3}),o($Ve3,[2,773]),o([69,107,112,114,119,126,368,372],$VC1,{434:280,424:733,3:734,4:$V1,5:$V2}),o([67,69,71,107,112,114,126,368,372],$VC1,{423:276,424:277,427:278,3:279,434:280,431:281,379:282,422:735,4:$V1,5:$V2,116:$Vr1,119:$Vs1}),o($VK3,[2,788],{436:736,107:[1,737]}),o($Vh3,[2,787]),{3:738,4:$V1,5:$V2,106:[1,739]},o($VL3,[2,672]),{3:217,4:$V1,5:$V2,173:740},{3:217,4:$V1,5:$V2,173:741},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:742,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VM3,[2,768],{416:743,91:744,107:$VO,256:$Va1}),o($Vi3,[2,767]),{3:745,4:$V1,5:$V2},o($VE1,[2,624]),o($VE1,[2,625],{99:[1,746]}),{4:$Vj3,236:747,338:$Vk3},o([5,8,47,65,67,69,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,257,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,294,298,307,319,320,324,325,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,482],{4:[1,749],70:[1,748]}),{70:[1,750]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:751,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VH,[2,525]),o($Vc3,[2,505]),{3:752,4:$V1,5:$V2,91:753,107:$VO,256:$Va1},o($VH,[2,503]),o($VH,[2,550]),o($VH,[2,604]),o($VH,$V0,{15:5,16:7,17:8,18:9,19:10,20:11,21:12,22:13,23:14,24:15,25:16,26:17,27:18,28:19,29:20,30:21,31:22,32:23,33:24,34:25,35:26,36:27,37:28,38:29,39:30,40:31,41:32,42:33,43:34,44:35,45:36,46:37,48:39,49:40,50:41,51:42,52:43,53:44,54:45,55:46,56:47,57:48,58:49,59:50,60:51,61:52,62:53,63:54,64:55,72:67,446:88,158:92,3:93,10:754,4:$V1,5:$V2,47:$V4,65:$V5,81:$V6,99:$V7,120:$V8,130:$V9,163:$Va,233:$Vb,287:$Vc,290:$Vd,291:$Ve,298:$Vf,344:$Vg,347:$Vh,348:$Vi,351:$Vj,353:$Vk,355:$Vl,356:$Vm,364:$Vn,365:$Vo,366:$Vp,383:$Vq,385:$Vr,386:$Vs,388:$Vt,389:$Vu,390:$Vv,391:$Vw,392:$Vx,396:$Vy,397:$Vz,400:$VA,401:$VB,447:$VC,449:$VD}),o($VN3,[2,339],{278:322,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,153:$VU1,154:$VV1,270:$VX1}),o($VN3,[2,340],{278:322,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,153:$VU1,154:$VV1,270:$VX1}),o($Vp3,[2,341],{278:322,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,154:$VV1}),o($Vp3,[2,342],{278:322,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,154:$VV1}),o($VO3,[2,343],{278:322,97:$VJ1,98:$VK1,111:$VN1}),o($VO3,[2,344],{278:322,97:$VJ1,98:$VK1,111:$VN1}),o($VO3,[2,345],{278:322,97:$VJ1,98:$VK1,111:$VN1}),o([4,5,8,47,65,67,69,70,71,81,85,90,93,98,99,104,105,106,107,108,109,110,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,346],{278:322,97:$VJ1,111:$VN1}),o($Vw3,[2,347],{70:$V92}),o($Vn1,[2,348]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:755,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,350]),o($Vw3,[2,351],{70:$V92}),o($Vn1,[2,352]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:756,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,354]),o($VP3,[2,355],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,356],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,357],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,358],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o([4,5,8,47,65,81,99,114,115,120,130,144,145,163,233,264,268,271,272,273,274,275,276,277,282,283,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,383,385,386,388,389,390,391,392,396,397,400,401,447,449,450,451],$VQ3,{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,360],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,361],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,362],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,363],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VP3,[2,364],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),{70:[1,757]},{70:[2,397]},{70:[2,398]},{70:[2,399]},o($VR3,[2,367],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,284:$V62}),o([4,5,8,47,65,67,69,70,71,81,85,93,104,105,106,107,109,110,112,116,117,119,120,122,123,124,126,130,136,138,140,142,143,145,146,147,149,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,368],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62}),{3:147,4:$V1,5:$V2,36:758,51:144,70:$VM,71:[1,760],72:67,81:$V6,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:759,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,158:92,163:$Va,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,380]),o($Vn1,[2,382]),o($Vn1,[2,388]),o($Vn1,[2,389]),{3:349,4:$V1,5:$V2,70:[1,761]},{3:377,4:$V1,5:$V2,70:[1,762],89:568,119:$Vn3,174:569,176:764,281:763},o($VR3,[2,384],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,284:$V62}),o($VR3,[2,385],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,284:$V62}),o([4,5,8,47,65,67,69,70,71,81,85,93,99,104,105,106,107,109,110,112,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,264,266,267,268,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,298,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,386],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,153:$VU1,154:$VV1,269:$VW1,270:$VX1}),o($Vn1,[2,387]),o($Vn1,[2,279]),o($Vn1,[2,280]),o($Vn1,[2,281]),o($Vn1,[2,373]),{67:$V63,71:[1,765]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:766,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:767,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:768,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Va2,[2,317]),o($Va3,[2,743]),o($Va3,[2,740]),o($Va3,[2,741]),o($Vn1,$VS3),o($VT3,[2,261]),o($Vn1,[2,257]),{71:[1,770],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{71:[1,771]},{263:772,264:$Vq3,265:595,266:$Vd2,268:$Vr3},{264:[1,773]},o($Vs3,[2,334]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:774,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,267:[1,775],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{69:[1,776],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{67:[1,777]},o($Ve2,[2,579]),{3:377,4:$V1,5:$V2,70:$Vf2,89:372,91:370,106:$VN,107:$VO,116:$VQ,118:367,119:$Vo1,126:$VS,155:$VW,170:369,174:375,175:374,225:371,226:373,254:$V81,255:$V91,256:$Va1,257:$Vb1,260:$Vd1,367:168,368:$Vg1,370:779,371:368,372:$Vh1,376:[1,778]},{71:[1,780],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{3:781,4:$V1,5:$V2,123:$Vo3},o($Ve2,[2,576]),{3:385,4:$V1,5:$V2,106:$Vg2,107:$Vh2,374:[1,782],377:783},{3:377,4:$V1,5:$V2,70:$Vf2,89:372,91:370,106:$VN,107:$VO,116:$VQ,118:367,119:$Vo1,126:$VS,155:$VW,170:369,174:375,175:374,225:371,226:373,254:$V81,255:$V91,256:$Va1,257:$Vb1,260:$Vd1,367:168,368:$Vg1,370:784,371:368,372:$Vh1},{3:377,4:$V1,5:$V2,70:$Vf2,89:372,91:370,106:$VN,107:$VO,116:$VQ,118:367,119:$Vo1,126:$VS,155:$VW,170:369,174:375,175:374,225:371,226:373,254:$V81,255:$V91,256:$Va1,257:$Vb1,260:$Vd1,367:168,368:$Vg1,370:785,371:368,372:$Vh1},{3:377,4:$V1,5:$V2,70:$Vf2,89:372,91:370,106:$VN,107:$VO,116:$VQ,118:367,119:$Vo1,126:$VS,155:$VW,170:369,174:375,175:374,225:371,226:373,254:$V81,255:$V91,256:$Va1,257:$Vb1,260:$Vd1,367:168,368:$Vg1,370:786,371:368,372:$Vh1},{70:$VU3,116:$VQ,118:789,119:$Vo1,126:$VS,155:$VW,175:790,260:$Vd1,293:787,367:168,368:$Vg1,372:$Vh1},{123:[1,791]},{3:638,4:$V1,5:$V2,89:793,204:792},o($VV3,[2,411]),{266:$VW3,405:794,407:795,408:796},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:798,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{202:[2,633]},o($Vj2,[2,159],{3:799,4:$V1,5:$V2,69:[1,800]}),o($Vj2,[2,160]),o($Vj2,[2,733]),o($Vj2,[2,162]),o($Vj2,[2,165]),o($Vj2,[2,169]),o($Vj2,[2,172]),o($Vj2,[2,175]),o([4,5,8,65,67,69,70,71,81,93,104,120,122,123,128,136,142,143,157,161,163,180,182,194,195,196,197,198,199,200,201,202,203,205,218,257,264,268,294,307,319,320,324,325,344,348,349,350,450,451],[2,177]),{3:801,4:$V1,5:$V2},o($VX3,[2,214],{76:802,104:[1,803]}),{3:193,4:$V1,5:$V2,70:[1,805],107:$Vk1,116:$VQ,118:187,119:$VR,126:$VS,155:$VW,173:188,174:190,175:189,176:191,177:804,183:806,186:192,260:$Vd1,367:168,368:$Vg1,372:$Vh1},o($Vm2,[2,142]),o($Vm2,[2,143]),o($Vm2,[2,144]),o($Vm2,[2,145]),o($Vm2,[2,146]),{3:349,4:$V1,5:$V2},o($Vl1,[2,66],{67:[1,807]}),o($VY3,[2,68]),o($VY3,[2,69]),{91:808,107:$VO,256:$Va1},o([8,65,67,71,93,99,104,136,142,143,157,172,180,182,194,195,196,197,198,199,200,201,205,218,264,268,450,451],$V82,{117:$Vv3}),o($VA3,[2,65]),o($VA3,[2,695]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:809,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VE3,[2,104]),o($VE3,[2,122]),o($VE3,[2,123]),o($VE3,[2,124]),{3:147,4:$V1,5:$V2,51:144,70:$VM,71:[2,704],89:130,91:134,100:235,103:810,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:811,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{70:[1,812]},o($VE3,[2,74]),o([4,5,8,65,67,69,70,71,93,99,104,105,106,107,109,110,112,114,115,116,117,119,120,122,123,124,126,130,136,138,140,142,143,144,145,146,147,149,155,157,159,161,172,244,245,246,247,248,249,250,251,252,264,268,368,372,450,451],[2,76],{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o([4,5,8,65,67,69,70,71,90,93,99,104,105,106,107,109,110,112,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,155,157,159,161,172,244,245,246,247,248,249,250,251,252,264,268,368,372,450,451],[2,77],{278:322,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,813],90:$Vt2,92:814,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},o($VZ3,[2,700],{127:642,153:$VB3,154:$VC3,155:$VD3}),{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,90:$Vt2,92:816,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,102:815,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:817,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:818,4:$V1,5:$V2},o($VE3,[2,89]),o($VE3,[2,90]),o($VE3,[2,91]),o($VE3,[2,95]),o($VE3,[2,97]),{3:819,4:$V1,5:$V2},{3:638,4:$V1,5:$V2,89:692,116:$VF3,119:$VG3,121:820,288:691,289:693},{3:821,4:$V1,5:$V2},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:822,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VE3,[2,103]),o($VZ3,[2,706],{129:823}),o($VZ3,[2,708],{131:824}),o($VZ3,[2,710],{133:825}),o($VZ3,[2,714],{135:826}),o($V_3,$V$3,{137:827,152:828}),{70:[1,829]},o($VZ3,[2,716],{139:830}),o($VZ3,[2,718],{141:831}),o($V_3,$V$3,{152:828,137:832}),o($V_3,$V$3,{152:828,137:833}),o($V_3,$V$3,{152:828,137:834}),o($V_3,$V$3,{152:828,137:835}),{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,90:$Vt2,92:836,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:489,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,148:837,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,221:488,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($V04,[2,720],{150:838}),o($VH,[2,543],{157:[1,839]}),o($VH,[2,539],{157:[1,840]}),o($VH,[2,532]),{91:841,107:$VO,256:$Va1},o($VH,[2,541],{157:[1,842]}),o($VH,[2,536]),o($VH,[2,537],{90:[1,843]}),o($V43,[2,61]),{36:844,72:67,81:$V6,158:92,163:$Va},o($VH,[2,401],{67:$V14,104:[1,845]}),o($V24,[2,402]),{99:[1,847]},{3:848,4:$V1,5:$V2},o($Vi1,[2,746]),o($Vi1,[2,747]),o($VH,[2,557]),o($V73,[2,319],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($VP3,$VQ3,{278:322,90:$VI1,97:$VJ1,98:$VK1,108:$VM1,111:$VN1,113:$VO1,128:$VR1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,280:$V32,284:$V62}),o($VE1,[2,618]),o($VE1,[2,620]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:849,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{97:[1,851],99:[1,850]},{3:853,4:$V1,5:$V2,70:$V34,106:$V44,381:852},o($V73,[2,683]),o($Vb3,[2,126],{67:$VH3}),o($Vb3,[2,127],{67:$VH3}),o($Vb3,[2,128],{67:$VH3}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:489,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,221:856,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:857,4:$V1,5:$V2,91:859,106:[1,858],107:$VO,256:$Va1},o($VI3,[2,250]),o($VI3,[2,252]),o($VI3,[2,254]),o($Vt1,[2,138]),o($Vt1,[2,729]),{71:[1,860]},o($Vw1,[2,686]),{3:861,4:$V1,5:$V2},{3:862,4:$V1,5:$V2},{3:864,4:$V1,5:$V2,334:863},{3:864,4:$V1,5:$V2,334:865},{3:866,4:$V1,5:$V2},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:867,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:868,4:$V1,5:$V2},o($Vc3,[2,445]),o($VH,$V54,{354:869,69:$V64,70:[1,870]}),o($VH,$V54,{354:872,69:$V64}),{70:[1,873]},{3:217,4:$V1,5:$V2,173:874},o($V73,[2,667]),o($V73,[2,669]),o($V73,[2,775]),{116:$Vr1,119:$Vs1,379:875},o($V74,[2,778],{367:168,428:876,118:877,119:$Vo1,368:$Vg1,372:$Vh1}),o($Vf3,$Vg3),{67:$Vd3,71:[1,878]},o($V84,[2,790],{437:879,438:880,126:[1,881]}),o($VK3,[2,789]),o($Vh3,[2,677]),o($Vh3,[2,678]),o($VH,[2,432],{70:[1,882]}),{69:[1,884],70:[1,883]},{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,122:[1,885],128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},o($VV3,$V94,{72:67,158:92,417:886,36:889,81:$V6,120:$Va4,163:$Va,419:$Vb4}),o($VM3,[2,769]),o($Vi3,[2,659]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:890,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VE1,[2,626],{99:[1,891]}),{106:$Vc4,247:$Vd4,337:892},o([4,5,8,47,65,67,69,71,81,85,90,93,97,98,99,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,122,123,124,126,128,130,136,138,140,142,143,144,145,146,147,149,153,154,155,157,159,161,163,172,180,182,194,195,196,197,198,199,200,201,205,212,215,216,218,233,244,245,246,247,248,249,250,251,252,256,257,264,266,267,268,269,270,271,272,273,274,275,276,277,280,282,283,284,285,287,290,291,294,298,307,319,320,324,325,344,347,348,351,353,355,356,364,365,366,368,372,383,385,386,388,389,390,391,392,396,397,400,401,413,419,447,449,450,451],[2,483],{70:[1,895]}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:897,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,295:896,367:168,368:$Vg1,372:$Vh1},o($VH,[2,406],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($VH,[2,526]),o($VH,[2,527]),o($VH,[2,606]),{71:[1,898],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{71:[1,899],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{3:147,4:$V1,5:$V2,36:900,51:144,70:$VM,72:67,81:$V6,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:901,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,158:92,163:$Va,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{71:[1,902]},{67:$V63,71:[1,903]},o($Vn1,[2,378]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:904,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,36:905,51:144,70:$VM,71:[1,907],72:67,81:$V6,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:906,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,158:92,163:$Va,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,381]),o($Vn1,[2,383]),o($Vn1,$Ve4,{239:908,240:$Vf4}),{71:[1,910],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{71:[1,911],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{67:$V63,71:[1,912]},{3:913,4:$V1,5:$V2,154:[1,914]},o($Ve2,[2,558]),o($Vn1,[2,327]),{264:[1,915]},o($Vn1,[2,333]),{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,264:[2,337],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:916,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{4:$Vj3,236:917,338:$Vk3},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:918,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Ve2,[2,580]),o($Vt3,[2,587]),o($Vu3,[2,575]),o($VT3,$VS3),o($Ve2,[2,577]),o($Vx3,[2,582]),o($Vx3,[2,584]),o($Vx3,[2,585]),o($Vx3,[2,586]),o($VV3,[2,408],{67:$Vg4}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:897,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,295:921,367:168,368:$Vg1,372:$Vh1},o($Vh4,[2,416]),o($Vh4,[2,417]),o($VV3,[2,409]),{67:$Vi4,71:[1,922]},o($Vj4,[2,429]),o($VH,[2,653],{406:924,407:925,408:926,266:$VW3,413:[1,927]}),o($Vk4,[2,637]),o($Vk4,[2,638]),{128:[1,929],409:[1,928]},{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,266:[2,634],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},o($Vj2,[2,157]),{3:930,4:$V1,5:$V2},o($VH,[2,511]),o($Vl4,[2,216],{77:931,205:[1,932]}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:933,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vy3,[2,147],{178:934,187:936,179:937,188:938,193:941,67:$Vm4,180:$Vn4,182:$Vo4,194:$Vp4,195:$Vq4,196:$Vr4,197:$Vs4,198:$Vt4,199:$Vu4,200:$Vv4,201:$Vw4}),{3:193,4:$V1,5:$V2,36:390,70:$Vj1,72:67,81:$V6,107:$Vk1,116:$VQ,118:187,119:$VR,126:$VS,155:$VW,158:92,163:$Va,173:188,174:190,175:189,176:191,177:950,183:806,186:192,260:$Vd1,367:168,368:$Vg1,372:$Vh1},o($Vj4,[2,155]),{3:638,4:$V1,5:$V2,88:951,89:636,90:$Vz3},o($VY3,[2,70]),o($VA3,[2,125],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{71:[1,952]},{67:$V63,71:[2,705]},{3:147,4:$V1,5:$V2,51:144,70:$VM,71:[2,698],89:130,91:134,95:953,96:954,100:957,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,214:955,215:[1,956],222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VE3,[2,78]),o($VZ3,[2,701],{127:642,153:$VB3,154:$VC3,155:$VD3}),{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,958],90:$Vt2,92:959,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},o($VZ3,[2,702],{127:642,153:$VB3,154:$VC3,155:$VD3}),{71:[1,960],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{71:[1,961]},o($VE3,[2,98]),{67:$V14,71:[1,962]},o($VE3,[2,100]),{67:$V63,71:[1,963]},{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,964],90:$Vt2,92:965,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,966],90:$Vt2,92:967,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,968],90:$Vt2,92:969,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,970],90:$Vt2,92:971,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{67:$Vx4,71:[1,972]},o($Vy4,[2,121],{367:168,3:411,118:433,132:443,134:444,92:974,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,90:$Vt2,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,368:$Vg1,372:$Vh1}),o($V_3,$V$3,{152:828,137:975}),{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,976],90:$Vt2,92:977,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{3:411,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,71:[1,978],90:$Vt2,92:979,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{67:$Vx4,71:[1,980]},{67:$Vx4,71:[1,981]},{67:$Vx4,71:[1,982]},{67:$Vx4,71:[1,983]},{71:[1,984],127:642,153:$VB3,154:$VC3,155:$VD3},{67:$VH3,71:[1,985]},{3:411,4:$V1,5:$V2,65:$Vq2,67:[1,986],69:$Vr2,70:$Vs2,90:$Vt2,92:987,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,118:433,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,132:443,134:444,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,367:168,368:$Vg1,372:$Vh1},{3:988,4:$V1,5:$V2},{3:989,4:$V1,5:$V2},o($VH,[2,534]),{3:990,4:$V1,5:$V2},{91:991,107:$VO,256:$Va1},{71:[1,992]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:993,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:638,4:$V1,5:$V2,89:692,116:$VF3,119:$VG3,288:994,289:693},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:995,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{99:[1,996]},o($VH,[2,590],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:997,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:853,4:$V1,5:$V2,70:$V34,106:$V44,381:998},o($Vz4,[2,595]),o($Vz4,[2,596]),o($Vz4,[2,597]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:999,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VI3,[2,247]),o($VI3,[2,249]),o($VI3,[2,251]),o($VI3,[2,253]),o($Vt1,[2,139]),o($VH,[2,506]),{122:[1,1e3]},o($VH,[2,507]),o($V73,[2,477],{236:1001,4:$Vj3,336:[1,1002],338:$Vk3}),o($VH,[2,508]),o($VH,[2,510]),{67:$V63,71:[1,1003]},o($VH,[2,514]),o($VH,[2,518]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:1004,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:1005,4:$V1,5:$V2},o($VH,[2,520]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,95:1006,100:957,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,214:955,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{70:[1,1007]},{3:1008,4:$V1,5:$V2},{69:$VJ3,114:[2,780],429:1009,432:1010},o($V74,[2,779]),o($V73,[2,671]),o($V84,[2,675]),o($V84,[2,791]),{3:1011,4:$V1,5:$V2},{3:864,4:$V1,5:$V2,69:[1,1014],302:1012,309:1013,334:1015},{3:638,4:$V1,5:$V2,89:793,204:1016},{36:1017,72:67,81:$V6,158:92,163:$Va},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1018,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VV3,[2,658]),{3:638,4:$V1,5:$V2,89:692,116:$VF3,119:$VG3,121:1019,288:691,289:693},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:1020,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VV3,[2,663]),o($VE1,[2,627],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1021,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{67:[1,1022],71:[1,1023]},o($Vy4,[2,485]),o($Vy4,[2,486]),{106:$Vc4,247:$Vd4,337:1024},{67:$VA4,71:[1,1025]},o($Vy4,[2,421],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($Vn1,[2,349]),o($Vn1,[2,353]),{71:[1,1027]},{67:$V63,71:[1,1028]},o($Vn1,[2,374]),o($Vn1,[2,376]),{71:[1,1029],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{71:[1,1030]},{67:$V63,71:[1,1031]},o($Vn1,[2,379]),o($Vn1,[2,298]),{70:[1,1032]},o($Vn1,$Ve4,{239:1033,240:$Vf4}),o($Vn1,$Ve4,{239:1034,240:$Vf4}),o($Va2,[2,316]),o($VT3,[2,259]),o($Vn1,[2,256]),o($Vn1,[2,332]),o($Vs3,[2,336],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{67:[1,1036],71:[1,1035]},{67:[1,1038],71:[1,1037],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{3:913,4:$V1,5:$V2},{70:[1,1039],116:$VQ,118:1040,119:$Vo1,126:$VS,155:$VW,175:1041,260:$Vd1,367:168,368:$Vg1,372:$Vh1},{67:$VA4,71:[1,1042]},{36:1044,72:67,81:$V6,123:[1,1043],158:92,163:$Va},{3:638,4:$V1,5:$V2,89:1045},o($VH,[2,630]),o($Vk4,[2,635]),o($Vk4,[2,636]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:489,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,148:1046,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,221:488,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{144:[1,1048],267:[1,1047]},{409:[1,1049]},o($Vj2,[2,158]),o($VB4,[2,235],{78:1050,93:[1,1051]}),{94:[1,1052]},o($VX3,[2,215],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($Vy3,[2,148],{188:938,193:941,187:1053,179:1054,180:$Vn4,182:$Vo4,194:$Vp4,195:$Vq4,196:$Vr4,197:$Vs4,198:$Vt4,199:$Vu4,200:$Vv4,201:$Vw4}),{3:193,4:$V1,5:$V2,70:$Vj1,107:$Vk1,116:$VQ,118:187,119:$VR,126:$VS,155:$VW,173:188,174:190,175:189,176:191,183:1055,186:192,260:$Vd1,367:168,368:$Vg1,372:$Vh1},o($VC4,[2,181]),o($VC4,[2,182]),{3:193,4:$V1,5:$V2,70:[1,1060],116:$VQ,118:1058,119:$VR,126:$VS,155:$VW,173:1057,174:1061,175:1059,176:1062,189:1056,260:$Vd1,367:168,368:$Vg1,372:$Vh1},{181:[1,1063],195:$VD4},{181:[1,1065],195:$VE4},o($VF4,[2,198]),{180:[1,1069],182:[1,1068],193:1067,195:$Vq4,196:$Vr4,197:$Vs4,198:$Vt4,199:$Vu4,200:$Vv4,201:$Vw4},o($VF4,[2,200]),{195:[1,1070]},{182:[1,1072],195:[1,1071]},{182:[1,1074],195:[1,1073]},{182:[1,1075]},{195:[1,1076]},{195:[1,1077]},{67:$Vm4,178:1078,179:937,180:$Vn4,182:$Vo4,187:936,188:938,193:941,194:$Vp4,195:$Vq4,196:$Vr4,197:$Vs4,198:$Vt4,199:$Vu4,200:$Vv4,201:$Vw4},o($VY3,[2,67]),o($VE3,[2,80]),{67:$VG4,71:[1,1079]},{71:[1,1081]},o($VH4,[2,237]),{71:[2,699]},o($VH4,[2,239],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,215:[1,1082],216:[1,1083],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($VE3,[2,79]),o($VZ3,[2,703],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,81]),o($VE3,[2,82]),o($VE3,[2,99]),o($VE3,[2,102]),o($VE3,[2,105]),o($VZ3,[2,707],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,106]),o($VZ3,[2,709],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,107]),o($VZ3,[2,711],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,108]),o($VZ3,[2,715],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,109]),o($V_3,[2,722],{151:1084}),o($V_3,[2,725],{127:642,153:$VB3,154:$VC3,155:$VD3}),{67:$Vx4,71:[1,1085]},o($VE3,[2,111]),o($VZ3,[2,717],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,112]),o($VZ3,[2,719],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,113]),o($VE3,[2,114]),o($VE3,[2,115]),o($VE3,[2,116]),o($VE3,[2,117]),o($VE3,[2,118]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:235,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,125:1086,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($V04,[2,721],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VH,[2,544]),o($VH,[2,540]),o($VH,[2,542]),o($VH,[2,538]),o($V43,[2,63]),o($VH,[2,400],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($V24,[2,403]),o($V24,[2,404],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1087,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VH,[2,591],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($Vz4,[2,594]),{71:[1,1088],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{3:1089,4:$V1,5:$V2},o($V73,[2,487],{335:1090,339:1091,340:1092,317:1100,128:$VI4,161:$VJ4,257:$VK4,294:$VL4,307:$VM4,319:$VN4,320:$VO4,324:$VP4,325:$VQ4}),o($V73,[2,476]),o($VH,[2,513],{69:[1,1103]}),{67:$V63,71:[1,1104]},o($VH,[2,522]),{67:$VG4,71:[1,1105]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,95:1106,100:957,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,214:955,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VL3,[2,673]),{114:[1,1107]},{114:[2,781]},o($V84,[2,676]),{71:[1,1108]},{67:[1,1109],71:[2,447]},{36:1110,72:67,81:$V6,158:92,163:$Va},o($Vy4,[2,473]),{67:$Vi4,71:[1,1111]},o($VH,[2,762],{359:1112,360:1113,65:$VR4}),o($VV3,$V94,{72:67,158:92,278:322,36:889,417:1115,81:$V6,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,120:$Va4,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,163:$Va,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72,419:$Vb4}),o($VV3,[2,661],{67:$V14}),o($VV3,[2,662],{67:$V63}),o($VE1,[2,628],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{106:[1,1116]},o($VS4,[2,480]),{67:[1,1117],71:[1,1118]},o($VS4,[2,484]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1119,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,365]),o($Vn1,[2,366]),o($Vn1,[2,390]),o($Vn1,[2,375]),o($Vn1,[2,377]),{93:$VT4,241:1120,242:1121,243:[1,1122]},o($Vn1,[2,299]),o($Vn1,[2,300]),o($Vn1,[2,287]),{106:[1,1124]},o($Vn1,[2,289]),{106:[1,1125]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:897,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,295:1126,367:168,368:$Vg1,372:$Vh1},o($Vh4,[2,419]),o($Vh4,[2,420]),o($Vh4,[2,415]),{70:$VU3,116:$VQ,118:789,119:$Vo1,126:$VS,155:$VW,175:790,260:$Vd1,293:1127,367:168,368:$Vg1,372:$Vh1},o($VV3,[2,412]),o($Vj4,[2,430]),o($VH,[2,654],{67:$VH3,172:[1,1128]}),{287:$VU4,290:$VV4,410:1129},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1132,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{94:[1,1134],144:[1,1135],267:[1,1133]},o($VW4,[2,243],{79:1136,218:[1,1137]}),{94:[1,1138]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1144,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,206:1139,208:1140,209:$VX4,210:$VY4,211:$VZ4,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VC4,[2,179]),o($VC4,[2,180]),o($Vj4,[2,156]),o($VC4,[2,213],{190:1145,202:[1,1146],203:[1,1147]}),o($V_4,[2,184],{3:1148,4:$V1,5:$V2,69:[1,1149]}),o($V$4,[2,734],{191:1150,69:[1,1151]}),{3:1152,4:$V1,5:$V2,69:[1,1153]},{36:1154,72:67,81:$V6,158:92,163:$Va},o($V_4,[2,192],{3:1155,4:$V1,5:$V2,69:[1,1156]}),o($V_4,[2,195],{3:1157,4:$V1,5:$V2,69:[1,1158]}),{70:[1,1159]},o($VF4,[2,210]),{70:[1,1160]},o($VF4,[2,206]),o($VF4,[2,199]),{195:$VE4},{195:$VD4},o($VF4,[2,201]),o($VF4,[2,202]),{195:[1,1161]},o($VF4,[2,204]),{195:[1,1162]},{195:[1,1163]},o($VF4,[2,208]),o($VF4,[2,209]),{71:[1,1164],179:1054,180:$Vn4,182:$Vo4,187:1053,188:938,193:941,194:$Vp4,195:$Vq4,196:$Vr4,197:$Vs4,198:$Vt4,199:$Vu4,200:$Vv4,201:$Vw4},o($VE3,[2,72]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:957,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,214:1165,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VE3,[2,73]),o($VH4,[2,240]),{217:[1,1166]},o($Vy4,[2,120],{367:168,3:411,118:433,132:443,134:444,92:1167,4:$V1,5:$V2,65:$Vq2,69:$Vr2,70:$Vs2,90:$Vt2,93:$Vu2,97:$Vv2,98:$Vw2,99:$Vx2,104:$Vy2,105:$Vz2,106:$VA2,107:$VB2,108:$VC2,109:$VD2,110:$VE2,111:$VF2,112:$VG2,113:$VH2,114:$VI2,115:$VJ2,116:$VK2,117:$VL2,119:$VM2,120:$VN2,122:$VO2,123:$VP2,124:$VQ2,126:$VR2,128:$VS2,130:$VT2,136:$VU2,138:$VV2,140:$VW2,142:$VX2,143:$VY2,144:$VZ2,145:$V_2,146:$V$2,147:$V03,149:$V13,159:$V23,161:$V33,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,368:$Vg1,372:$Vh1}),o($VE3,[2,110]),{67:$V63,71:[1,1168]},o($V24,[2,405],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($Vz4,[2,598]),o($VH,[2,509]),o($V73,[2,475]),o($V73,[2,488],{317:1100,340:1169,128:$VI4,161:$VJ4,257:$VK4,294:$VL4,307:$VM4,319:$VN4,320:$VO4,324:$VP4,325:$VQ4}),o($V53,[2,490]),{321:[1,1170]},{321:[1,1171]},{3:217,4:$V1,5:$V2,173:1172},o($V53,[2,496],{70:[1,1173]}),{3:231,4:$V1,5:$V2,70:[1,1175],91:225,106:$VN,107:$VO,116:$VQ,126:$VS,155:$VW,170:224,174:229,175:228,225:226,226:227,232:$Vp1,238:1174,254:$V81,255:$V91,256:$Va1,257:$Vb1,260:$Vd1},o($V53,[2,499]),{257:[1,1176]},o($V53,[2,501]),o($V53,[2,502]),{70:[1,1177]},{3:1178,4:$V1,5:$V2},o($VH,$V54,{354:1179,69:$V64}),o($VH,[2,528]),{67:$VG4,71:[1,1180]},o([8,67,71,107,112,126,264,268,450,451],$VC1,{434:280,379:282,3:734,430:1181,424:1182,431:1183,4:$V1,5:$V2,116:$Vr1,119:$Vs1}),o($VH,[2,435],{303:1184,305:1185,306:1186,4:$V05,294:$V15,307:$V25}),o($V35,$V45,{3:864,310:1190,334:1191,311:1192,312:1193,4:$V1,5:$V2,318:$V55}),{71:[2,448]},{69:[1,1195]},o($VH,[2,546]),o($VH,[2,763]),{319:[1,1197],361:[1,1196]},o($VV3,[2,664]),{71:[1,1198]},{106:[1,1199]},o($VS4,[2,481]),o($Vy4,[2,422],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{71:[1,1200],93:$VT4,242:1201},{71:[1,1202]},{94:[1,1203]},{94:[1,1204]},{71:[1,1205]},{71:[1,1206]},{67:$VA4,71:[1,1207]},o($VV3,[2,410],{67:$Vg4}),{3:217,4:$V1,5:$V2,116:$Vr1,119:$Vs1,173:1209,379:1208},o($Vk4,[2,639]),o($Vk4,[2,641]),{120:[1,1210]},{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,267:[1,1211],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{291:$V65,411:1212},{365:[1,1215],412:[1,1214]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1216,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VA3,[2,226],{80:1217,136:[1,1218],142:[1,1220],143:[1,1219]}),{106:$VN,170:1221},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,95:1222,100:957,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,214:955,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vl4,[2,224],{207:1223,67:$V75,212:[1,1225]}),o($V85,[2,218]),{120:[1,1226]},{70:[1,1227]},{70:[1,1228]},o($V85,[2,223],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($VC4,[2,183]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1229,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:638,4:$V1,5:$V2,89:793,204:1230},o($V_4,[2,185]),{3:1231,4:$V1,5:$V2},o($V_4,[2,736],{192:1232,3:1233,4:$V1,5:$V2}),o($V$4,[2,735]),o($V_4,[2,188]),{3:1234,4:$V1,5:$V2},{71:[1,1235]},o($V_4,[2,193]),{3:1236,4:$V1,5:$V2},o($V_4,[2,196]),{3:1237,4:$V1,5:$V2},{36:1238,72:67,81:$V6,158:92,163:$Va},{36:1239,72:67,81:$V6,158:92,163:$Va},o($VF4,[2,203]),o($VF4,[2,205]),o($VF4,[2,207]),o($Vy3,[2,149]),o($VH4,[2,238]),o($VH4,[2,241],{215:[1,1240]}),o($V_3,[2,723],{127:642,153:$VB3,154:$VC3,155:$VD3}),o($VE3,[2,119]),o($V53,[2,489]),o($V53,[2,492]),{325:[1,1241]},o($V53,[2,756],{343:1242,341:1243,70:$V95}),{106:$VN,170:1245},o($V53,[2,497]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1246,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($V53,[2,500]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1247,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VH,[2,515]),o($VH,[2,519]),o($VH,[2,529]),o($V73,[2,670]),o($V73,[2,782]),o($V73,[2,783]),o($VH,[2,431]),o($VH,[2,436],{306:1248,4:$V05,294:$V15,307:$V25}),o($Va5,[2,438]),o($Va5,[2,439]),{99:[1,1249]},{99:[1,1250]},{67:[1,1251],71:[2,446]},o($Vy4,[2,474]),o($Vy4,[2,449]),{161:[1,1259],167:[1,1260],313:1252,314:1253,315:1254,316:1255,317:1256,319:$VN4,320:[1,1257],321:[1,1261],324:[1,1258]},{3:1262,4:$V1,5:$V2},{36:1263,72:67,81:$V6,158:92,163:$Va},{362:[1,1264]},{363:[1,1265]},o($VS4,[2,478]),{71:[1,1266]},o($Vn1,[2,302]),{71:[1,1267]},o($Vn1,[2,303]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1144,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,206:1268,208:1140,209:$VX4,210:$VY4,211:$VZ4,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,95:1269,100:957,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,214:955,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($Vn1,[2,288]),o($Vn1,[2,290]),o($Vh4,[2,418]),{3:1270,4:$V1,5:$V2},o($VH,[2,656],{70:[1,1271]}),{3:638,4:$V1,5:$V2,89:692,116:$VF3,119:$VG3,121:1272,288:691,289:693},{287:$VU4,290:$VV4,410:1273},o($Vk4,[2,643]),{70:[1,1275],123:[1,1274],294:[1,1276]},{144:[1,1278],267:[1,1277]},{144:[1,1280],267:[1,1279]},{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,267:[1,1281],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},o($VA3,[2,64]),{36:1282,72:67,81:$V6,138:[1,1283],158:92,163:$Va,213:[1,1284]},{36:1285,72:67,81:$V6,158:92,163:$Va,213:[1,1286]},{36:1287,72:67,81:$V6,158:92,163:$Va,213:[1,1288]},o($VW4,[2,245],{219:1289,220:[1,1290]}),o($VB4,[2,236],{67:$VG4}),o($Vl4,[2,217]),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1144,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,208:1291,209:$VX4,210:$VY4,211:$VZ4,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1292,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{70:[1,1293]},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1144,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,206:1294,208:1140,209:$VX4,210:$VY4,211:$VZ4,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1144,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,206:1295,208:1140,209:$VX4,210:$VY4,211:$VZ4,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},o($VC4,[2,211],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),o($VC4,[2,212],{67:$Vi4}),o($V_4,[2,186]),o($V_4,[2,187]),o($V_4,[2,737]),o($V_4,[2,189]),{3:1296,4:$V1,5:$V2,69:[1,1297]},o($V_4,[2,194]),o($V_4,[2,197]),{71:[1,1298]},{71:[1,1299]},o($VH4,[2,242]),{3:217,4:$V1,5:$V2,173:1300},o($V53,[2,494]),o($V53,[2,757]),{3:1301,4:$V1,5:$V2},{67:[1,1302]},{71:[1,1303],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},{71:[1,1304],90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},o($Va5,[2,437]),{3:1305,4:$V1,5:$V2},{106:$VN,170:1306},o($V35,$V45,{312:1193,311:1307,318:$V55}),o($V73,[2,451]),o($V73,[2,452]),o($V73,[2,453]),o($V73,[2,454]),o($V73,[2,455]),{321:[1,1308]},{321:[1,1309]},{3:1311,4:$V1,5:$V2,70:[2,752],333:1310},{3:1312,4:$V1,5:$V2},{3:1313,4:$V1,5:$V2},o($V35,[2,457]),o($VH,[2,760],{358:1314,360:1315,65:$VR4}),o($VH,[2,547]),o($VH,[2,548],{318:[1,1316]}),o($VS4,[2,479]),o($Vn1,[2,304]),o([71,93],[2,305],{67:$V75}),{67:$VG4,71:[2,306]},o($VH,[2,655]),{3:638,4:$V1,5:$V2,89:793,204:1317},o($Vk4,[2,642],{67:$V14}),o($Vk4,[2,640]),{70:$VU3,116:$VQ,118:789,119:$Vo1,126:$VS,155:$VW,175:790,260:$Vd1,293:1318,367:168,368:$Vg1,372:$Vh1},{3:638,4:$V1,5:$V2,89:793,204:1319},{123:[1,1320]},{291:$V65,411:1321},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1322,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{291:$V65,411:1323},{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1324,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{291:$V65,411:1325},o($VA3,[2,227]),{36:1326,72:67,81:$V6,158:92,163:$Va,213:[1,1327]},{36:1328,72:67,81:$V6,158:92,163:$Va},o($VA3,[2,229]),{36:1329,72:67,81:$V6,158:92,163:$Va},o($VA3,[2,230]),{36:1330,72:67,81:$V6,158:92,163:$Va},o($VW4,[2,244]),{106:$VN,170:1331},o($V85,[2,219]),o($Vl4,[2,225],{278:322,90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72}),{3:147,4:$V1,5:$V2,51:144,70:$VM,89:130,91:134,100:1144,106:$VN,107:$VO,112:$VP,116:$VQ,118:141,119:$VR,126:$VS,128:$VT,132:146,153:$VU,154:$VV,155:$VW,170:132,174:128,175:136,176:137,206:1332,208:1140,209:$VX4,210:$VY4,211:$VZ4,222:131,223:127,224:129,225:133,226:135,227:138,228:139,229:140,230:142,232:$VX,233:$Vb,234:$VY,235:$VZ,237:$V_,244:$V$,245:$V01,246:$V11,247:$V21,248:$V31,249:$V41,250:$V51,251:$V61,252:$V71,254:$V81,255:$V91,256:$Va1,257:$Vb1,258:$Vc1,260:$Vd1,261:$Ve1,270:$Vf1,367:168,368:$Vg1,372:$Vh1},{67:$V75,71:[1,1333]},{67:$V75,71:[1,1334]},o($V_4,[2,190]),{3:1335,4:$V1,5:$V2},{3:1336,4:$V1,5:$V2,69:[1,1337]},{3:1338,4:$V1,5:$V2,69:[1,1339]},o($V53,[2,754],{342:1340,341:1341,70:$V95}),{71:[1,1342]},{106:$VN,170:1343},o($V53,[2,498]),o($V53,[2,458]),o($Va5,[2,440]),o($Va5,[2,441]),o($Vy4,[2,450]),{3:1345,4:$V1,5:$V2,70:[2,748],322:1344},{70:[1,1346]},{70:[1,1347]},{70:[2,753]},{70:[1,1348]},{70:[1,1349]},o($VH,[2,545]),o($VH,[2,761]),o($V35,$V45,{312:1193,311:1350,318:$V55}),{67:$Vi4,71:[1,1351]},o($Vk4,[2,649],{67:$Vg4}),{67:$Vi4,71:[1,1352]},o($Vk4,[2,651]),o($Vk4,[2,644]),{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,267:[1,1353],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},o($Vk4,[2,647]),{90:$VI1,97:$VJ1,98:$VK1,99:$V83,108:$VM1,111:$VN1,113:$VO1,114:$VP1,115:$VQ1,128:$VR1,144:$VS1,145:$VT1,153:$VU1,154:$VV1,267:[1,1354],269:$VW1,270:$VX1,271:$VY1,272:$VZ1,273:$V_1,274:$V$1,275:$V02,276:$V12,277:$V22,278:322,280:$V32,282:$V42,283:$V52,284:$V62,285:$V72},o($Vk4,[2,645]),o($VA3,[2,228]),{36:1355,72:67,81:$V6,158:92,163:$Va},o($VA3,[2,231]),o($VA3,[2,233]),o($VA3,[2,234]),o($VW4,[2,246]),{67:$V75,71:[1,1356]},o($V85,[2,221]),o($V85,[2,222]),o($V_4,[2,191]),o($VC4,[2,151]),{3:1357,4:$V1,5:$V2},o($VC4,[2,153]),{3:1358,4:$V1,5:$V2},o($V53,[2,493]),o($V53,[2,755]),o($V53,[2,491]),{71:[1,1359]},{70:[1,1360]},{70:[2,749]},{3:1362,4:$V1,5:$V2,107:$Vb5,323:1361},{3:638,4:$V1,5:$V2,89:793,204:1364},{3:638,4:$V1,5:$V2,89:793,204:1365},{3:638,4:$V1,5:$V2,89:793,204:1366},o($VH,[2,549]),o($VH,[2,657]),{123:[1,1367],294:[1,1368]},{291:$V65,411:1369},{287:$VU4,290:$VV4,410:1370},o($VA3,[2,232]),o($V85,[2,220]),o($VC4,[2,152]),o($VC4,[2,154]),o($V53,[2,495]),{3:1362,4:$V1,5:$V2,107:$Vb5,323:1371},{67:$Vc5,71:[1,1372]},o($Vy4,[2,469]),o($Vy4,[2,470]),{67:$Vi4,71:[1,1374]},{67:$Vi4,71:[1,1375]},{67:$Vi4,71:[1,1376]},{70:$VU3,116:$VQ,118:789,119:$Vo1,126:$VS,155:$VW,175:790,260:$Vd1,293:1377,367:168,368:$Vg1,372:$Vh1},{123:[1,1378]},o($Vk4,[2,646]),o($Vk4,[2,648]),{67:$Vc5,71:[1,1379]},{325:[1,1380]},{3:1381,4:$V1,5:$V2,107:[1,1382]},o($V73,[2,466]),o($V73,[2,467]),o($V73,[2,468]),o($Vk4,[2,650],{67:$Vg4}),o($Vk4,[2,652]),o($V73,[2,459]),{3:217,4:$V1,5:$V2,173:1383},o($Vy4,[2,471]),o($Vy4,[2,472]),o($Vd5,[2,750],{326:1384,328:1385,70:[1,1386]}),o($V73,[2,462],{327:1387,329:1388,202:[1,1389]}),o($Vd5,[2,751]),{3:1362,4:$V1,5:$V2,107:$Vb5,323:1390},o($V73,[2,460]),{202:[1,1392],330:1391},{290:[1,1393]},{67:$Vc5,71:[1,1394]},o($V73,[2,463]),{287:[1,1395]},{331:[1,1396]},o($Vd5,[2,461]),{331:[1,1397]},{332:[1,1398]},{332:[1,1399]},{202:[2,464]},o($V73,[2,465])],defaultActions:{97:[2,3],171:[2,307],172:[2,308],173:[2,309],174:[2,310],175:[2,311],176:[2,312],177:[2,313],178:[2,314],179:[2,315],184:[2,631],281:[2,777],288:[2,771],335:[2,738],336:[2,739],389:[2,632],455:[2,712],456:[2,713],560:[2,397],561:[2,398],562:[2,399],615:[2,633],956:[2,699],1010:[2,781],1110:[2,448],1311:[2,753],1345:[2,749],1398:[2,464]},parseError:function parseError(str,hash){if(hash.recoverable){this.trace(str)
-}else{throw new Error(str)}},parse:function parse(input){var self=this,stack=[0],tstack=[],vstack=[null],lstack=[],table=this.table,yytext="",yylineno=0,yyleng=0,recovering=0,TERROR=2,EOF=1;var args=lstack.slice.call(arguments,1);var lexer=Object.create(this.lexer);var sharedState={yy:{}};for(var k in this.yy){if(Object.prototype.hasOwnProperty.call(this.yy,k)){sharedState.yy[k]=this.yy[k]}}lexer.setInput(input,sharedState.yy);sharedState.yy.lexer=lexer;sharedState.yy.parser=this;if(typeof lexer.yylloc=="undefined"){lexer.yylloc={}}var yyloc=lexer.yylloc;lstack.push(yyloc);var ranges=lexer.options&&lexer.options.ranges;if(typeof sharedState.yy.parseError==="function"){this.parseError=sharedState.yy.parseError}else{this.parseError=Object.getPrototypeOf(this).parseError}function popStack(n){stack.length=stack.length-2*n;vstack.length=vstack.length-n;lstack.length=lstack.length-n}_token_stack:function lex(){var token;token=lexer.lex()||EOF;if(typeof token!=="number"){token=self.symbols_[token]||token}return token}var symbol,preErrorSymbol,state,action,a,r,yyval={},p,len,newState,expected;while(true){state=stack[stack.length-1];if(this.defaultActions[state]){action=this.defaultActions[state]}else{if(symbol===null||typeof symbol=="undefined"){symbol=lex()}action=table[state]&&table[state][symbol]}if(typeof action==="undefined"||!action.length||!action[0]){var errStr="";expected=[];for(p in table[state]){if(this.terminals_[p]&&p>TERROR){expected.push("'"+this.terminals_[p]+"'")}}if(lexer.showPosition){errStr="Parse error on line "+(yylineno+1)+":\n"+lexer.showPosition()+"\nExpecting "+expected.join(", ")+", got '"+(this.terminals_[symbol]||symbol)+"'"}else{errStr="Parse error on line "+(yylineno+1)+": Unexpected "+(symbol==EOF?"end of input":"'"+(this.terminals_[symbol]||symbol)+"'")}this.parseError(errStr,{text:lexer.match,token:this.terminals_[symbol]||symbol,line:lexer.yylineno,loc:yyloc,expected:expected})}if(action[0]instanceof Array&&action.length>1){throw new Error("Parse Error: multiple actions possible at state: "+state+", token: "+symbol)}switch(action[0]){case 1:stack.push(symbol);vstack.push(lexer.yytext);lstack.push(lexer.yylloc);stack.push(action[1]);symbol=null;if(!preErrorSymbol){yyleng=lexer.yyleng;yytext=lexer.yytext;yylineno=lexer.yylineno;yyloc=lexer.yylloc;if(recovering>0){recovering--}}else{symbol=preErrorSymbol;preErrorSymbol=null}break;case 2:len=this.productions_[action[1]][1];yyval.$=vstack[vstack.length-len];yyval._$={first_line:lstack[lstack.length-(len||1)].first_line,last_line:lstack[lstack.length-1].last_line,first_column:lstack[lstack.length-(len||1)].first_column,last_column:lstack[lstack.length-1].last_column};if(ranges){yyval._$.range=[lstack[lstack.length-(len||1)].range[0],lstack[lstack.length-1].range[1]]}r=this.performAction.apply(yyval,[yytext,yyleng,yylineno,sharedState.yy,action[1],vstack,lstack].concat(args));if(typeof r!=="undefined"){return r}if(len){stack=stack.slice(0,-1*len*2);vstack=vstack.slice(0,-1*len);lstack=lstack.slice(0,-1*len)}stack.push(this.productions_[action[1]][0]);vstack.push(yyval.$);lstack.push(yyval._$);newState=table[stack[stack.length-2]][stack[stack.length-1]];stack.push(newState);break;case 3:return true}}return true}};var lexer=function(){var lexer={EOF:1,parseError:function parseError(str,hash){if(this.yy.parser){this.yy.parser.parseError(str,hash)}else{throw new Error(str)}},setInput:function(input,yy){this.yy=yy||this.yy||{};this._input=input;this._more=this._backtrack=this.done=false;this.yylineno=this.yyleng=0;this.yytext=this.matched=this.match="";this.conditionStack=["INITIAL"];this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0};if(this.options.ranges){this.yylloc.range=[0,0]}this.offset=0;return this},input:function(){var ch=this._input[0];this.yytext+=ch;this.yyleng++;this.offset++;this.match+=ch;this.matched+=ch;var lines=ch.match(/(?:\r\n?|\n).*/g);if(lines){this.yylineno++;this.yylloc.last_line++}else{this.yylloc.last_column++}if(this.options.ranges){this.yylloc.range[1]++}this._input=this._input.slice(1);return ch},unput:function(ch){var len=ch.length;var lines=ch.split(/(?:\r\n?|\n)/g);this._input=ch+this._input;this.yytext=this.yytext.substr(0,this.yytext.length-len);this.offset-=len;var oldLines=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1);this.matched=this.matched.substr(0,this.matched.length-1);if(lines.length-1){this.yylineno-=lines.length-1}var r=this.yylloc.range;this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:lines?(lines.length===oldLines.length?this.yylloc.first_column:0)+oldLines[oldLines.length-lines.length].length-lines[0].length:this.yylloc.first_column-len};if(this.options.ranges){this.yylloc.range=[r[0],r[0]+this.yyleng-len]}this.yyleng=this.yytext.length;return this},more:function(){this._more=true;return this},reject:function(){if(this.options.backtrack_lexer){this._backtrack=true}else{return this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})}return this},less:function(n){this.unput(this.match.slice(n))},pastInput:function(){var past=this.matched.substr(0,this.matched.length-this.match.length);return(past.length>20?"...":"")+past.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var next=this.match;if(next.length<20){next+=this._input.substr(0,20-next.length)}return(next.substr(0,20)+(next.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var pre=this.pastInput();var c=new Array(pre.length+1).join("-");return pre+this.upcomingInput()+"\n"+c+"^"},test_match:function(match,indexed_rule){var token,lines,backup;if(this.options.backtrack_lexer){backup={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done};if(this.options.ranges){backup.yylloc.range=this.yylloc.range.slice(0)}}lines=match[0].match(/(?:\r\n?|\n).*/g);if(lines){this.yylineno+=lines.length}this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:lines?lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+match[0].length};this.yytext+=match[0];this.match+=match[0];this.matches=match;this.yyleng=this.yytext.length;if(this.options.ranges){this.yylloc.range=[this.offset,this.offset+=this.yyleng]}this._more=false;this._backtrack=false;this._input=this._input.slice(match[0].length);this.matched+=match[0];token=this.performAction.call(this,this.yy,this,indexed_rule,this.conditionStack[this.conditionStack.length-1]);if(this.done&&this._input){this.done=false}if(token){return token}else if(this._backtrack){for(var k in backup){this[k]=backup[k]}return false}return false},next:function(){if(this.done){return this.EOF}if(!this._input){this.done=true}var token,match,tempMatch,index;if(!this._more){this.yytext="";this.match=""}var rules=this._currentRules();for(var i=0;i<rules.length;i++){tempMatch=this._input.match(this.rules[rules[i]]);if(tempMatch&&(!match||tempMatch[0].length>match[0].length)){match=tempMatch;index=i;if(this.options.backtrack_lexer){token=this.test_match(tempMatch,rules[i]);if(token!==false){return token}else if(this._backtrack){match=false;continue}else{return false}}else if(!this.options.flex){break}}}if(match){token=this.test_match(match,rules[index]);if(token!==false){return token}return false}if(this._input===""){return this.EOF}else{return this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})}},lex:function lex(){var r=this.next();if(r){return r}else{return this.lex()}},begin:function begin(condition){this.conditionStack.push(condition)},popState:function popState(){var n=this.conditionStack.length-1;if(n>0){return this.conditionStack.pop()}else{return this.conditionStack[0]}},_currentRules:function _currentRules(){if(this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules}else{return this.conditions["INITIAL"].rules}},topState:function topState(n){n=this.conditionStack.length-1-Math.abs(n||0);if(n>=0){return this.conditionStack[n]}else{return"INITIAL"}},pushState:function pushState(condition){this.begin(condition)},stateStackSize:function stateStackSize(){return this.conditionStack.length},options:{"case-insensitive":true},performAction:function anonymous(yy,yy_,$avoiding_name_collisions,YY_START){var YYSTATE=YY_START;switch($avoiding_name_collisions){case 0:return 233;break;case 1:return 260;break;case 2:return 368;break;case 3:return 5;break;case 4:return 5;break;case 5:return 256;break;case 6:return 256;break;case 7:return 107;break;case 8:return 107;break;case 9:return;break;case 10:break;case 11:return 145;break;case 12:return 144;break;case 13:yy_.yytext="VALUE";return 163;break;case 14:yy_.yytext="ROW";return 163;break;case 15:yy_.yytext="COLUMN";return 163;break;case 16:yy_.yytext="MATRIX";return 163;break;case 17:yy_.yytext="INDEX";return 163;break;case 18:yy_.yytext="RECORDSET";return 163;break;case 19:yy_.yytext="TEXT";return 163;break;case 20:yy_.yytext="SELECT";return 163;break;case 21:return"ABSOLUTE";break;case 22:return 332;break;case 23:return 349;break;case 24:return 251;break;case 25:return 138;break;case 26:return 347;break;case 27:return 144;break;case 28:return 201;break;case 29:return 140;break;case 30:return 181;break;case 31:return 252;break;case 32:return 69;break;case 33:return 366;break;case 34:return 215;break;case 35:return 351;break;case 36:return 307;break;case 37:return 248;break;case 38:return 386;break;case 39:return 282;break;case 40:return 390;break;case 41:return 283;break;case 42:return 269;break;case 43:return 94;break;case 44:return 261;break;case 45:return 235;break;case 46:return 319;break;case 47:return 105;break;case 48:return"CLOSE";break;case 49:return 216;break;case 50:return 164;break;case 51:return 164;break;case 52:return 383;break;case 53:return 318;break;case 54:return 419;break;case 55:return 389;break;case 56:return 237;break;case 57:return 213;break;case 58:return 245;break;case 59:return 298;break;case 60:return 180;break;case 61:return 211;break;case 62:return 232;break;case 63:return"CURSOR";break;case 64:return 352;break;case 65:return 397;break;case 66:return 294;break;case 67:return 290;break;case 68:return"DELETED";break;case 69:return 215;break;case 70:return 353;break;case 71:return 159;break;case 72:return 344;break;case 73:return 396;break;case 74:return 110;break;case 75:return 264;break;case 76:return 338;break;case 77:return 268;break;case 78:return 143;break;case 79:return 258;break;case 80:return 12;break;case 81:return 255;break;case 82:return"FETCH";break;case 83:return 249;break;case 84:return 324;break;case 85:return 157;break;case 86:return 451;break;case 87:return 421;break;case 88:return 205;break;case 89:return 209;break;case 90:return 212;break;case 91:return 364;break;case 92:return 130;break;case 93:return 307;break;case 94:return 284;break;case 95:return 280;break;case 96:return 167;break;case 97:return 196;break;case 98:return 291;break;case 99:return"INSERTED";break;case 100:return 142;break;case 101:return 172;break;case 102:return 195;break;case 103:return 321;break;case 104:return 250;break;case 105:return"LET";break;case 106:return 197;break;case 107:return 90;break;case 108:return 218;break;case 109:return 409;break;case 110:return 165;break;case 111:return 247;break;case 112:return 401;break;case 113:return 246;break;case 114:return 143;break;case 115:return 350;break;case 116:return 194;break;case 117:return"NEXT";break;case 118:return 234;break;case 119:return 217;break;case 120:return 331;break;case 121:return 128;break;case 122:return 257;break;case 123:return 382;break;case 124:return 202;break;case 125:return 362;break;case 126:return 220;break;case 127:return"OPEN";break;case 128:return 363;break;case 129:return 145;break;case 130:return 93;break;case 131:return 182;break;case 132:return 240;break;case 133:return 146;break;case 134:return 243;break;case 135:return 452;break;case 136:return 14;break;case 137:return 320;break;case 138:return 391;break;case 139:return"PRIOR";break;case 140:return 13;break;case 141:return 361;break;case 142:return 168;break;case 143:return"REDUCE";break;case 144:return 325;break;case 145:return"RELATIVE";break;case 146:return 85;break;case 147:return 348;break;case 148:return 149;break;case 149:return 392;break;case 150:return"RESTORE";break;case 151:return 147;break;case 152:return 147;break;case 153:return 198;break;case 154:return 385;break;case 155:return 210;break;case 156:return 124;break;case 157:return 352;break;case 158:return 81;break;case 159:return 200;break;case 160:return 120;break;case 161:return 120;break;case 162:return 356;break;case 163:return 286;break;case 164:return 365;break;case 165:return"STRATEGY";break;case 166:return"STORE";break;case 167:return 244;break;case 168:return 304;break;case 169:return 304;break;case 170:return 412;break;case 171:return 308;break;case 172:return 308;break;case 173:return 166;break;case 174:return 267;break;case 175:return"TIMEOUT";break;case 176:return 122;break;case 177:return 169;break;case 178:return 384;break;case 179:return 384;break;case 180:return 254;break;case 181:return 400;break;case 182:return 136;break;case 183:return 161;break;case 184:return 287;break;case 185:return 355;break;case 186:return 203;break;case 187:return 123;break;case 188:return 109;break;case 189:return 357;break;case 190:return 266;break;case 191:return 104;break;case 192:return 388;break;case 193:return 65;break;case 194:return 384;break;case 195:return 106;break;case 196:return 106;break;case 197:return 97;break;case 198:return 112;break;case 199:return 153;break;case 200:return 270;break;case 201:return 154;break;case 202:return 108;break;case 203:return 113;break;case 204:return 277;break;case 205:return 274;break;case 206:return 276;break;case 207:return 273;break;case 208:return 271;break;case 209:return 114;break;case 210:return 272;break;case 211:return 275;break;case 212:return 115;break;case 213:return 99;break;case 214:return 275;break;case 215:return 70;break;case 216:return 71;break;case 217:return 119;break;case 218:return 372;break;case 219:return 374;break;case 220:return 376;break;case 221:return 447;break;case 222:return 449;break;case 223:return 117;break;case 224:return 67;break;case 225:return 285;break;case 226:return 126;break;case 227:return 450;break;case 228:return 116;break;case 229:return 155;break;case 230:return 111;break;case 231:return 98;break;case 232:return 4;break;case 233:return 8;break;case 234:return"INVALID";break}},rules:[/^(?:``([^\`])+``)/i,/^(?:\[\?\])/i,/^(?:@\[)/i,/^(?:\[([^\]])*?\])/i,/^(?:`([^\`])*?`)/i,/^(?:N(['](\\.|[^']|\\')*?['])+)/i,/^(?:X(['](\\.|[^']|\\')*?['])+)/i,/^(?:(['](\\.|[^']|\\')*?['])+)/i,/^(?:(["](\\.|[^"]|\\")*?["])+)/i,/^(?:--(.*?)($|\r\n|\r|\n))/i,/^(?:\s+)/i,/^(?:\|\|)/i,/^(?:&&)/i,/^(?:VALUE\s+OF\s+SELECT\b)/i,/^(?:ROW\s+OF\s+SELECT\b)/i,/^(?:COLUMN\s+OF\s+SELECT\b)/i,/^(?:MATRIX\s+OF\s+SELECT\b)/i,/^(?:INDEX\s+OF\s+SELECT\b)/i,/^(?:RECORDSET\s+OF\s+SELECT\b)/i,/^(?:TEXT\s+OF\s+SELECT\b)/i,/^(?:SELECT\b)/i,/^(?:ABSOLUTE\b)/i,/^(?:ACTION\b)/i,/^(?:ADD\b)/i,/^(?:AGGR\b)/i,/^(?:ALL\b)/i,/^(?:ALTER\b)/i,/^(?:AND\b)/i,/^(?:ANTI\b)/i,/^(?:ANY\b)/i,/^(?:APPLY\b)/i,/^(?:ARRAY\b)/i,/^(?:AS\b)/i,/^(?:ASSERT\b)/i,/^(?:ASC\b)/i,/^(?:ATTACH\b)/i,/^(?:AUTO(_)?INCREMENT\b)/i,/^(?:AVG\b)/i,/^(?:BEGIN\b)/i,/^(?:BETWEEN\b)/i,/^(?:BREAK\b)/i,/^(?:NOT\s+BETWEEN\b)/i,/^(?:NOT\s+LIKE\b)/i,/^(?:BY\b)/i,/^(?:CASE\b)/i,/^(?:CAST\b)/i,/^(?:CHECK\b)/i,/^(?:CLASS\b)/i,/^(?:CLOSE\b)/i,/^(?:COLLATE\b)/i,/^(?:COLUMN\b)/i,/^(?:COLUMNS\b)/i,/^(?:COMMIT\b)/i,/^(?:CONSTRAINT\b)/i,/^(?:CONTENT\b)/i,/^(?:CONTINUE\b)/i,/^(?:CONVERT\b)/i,/^(?:CORRESPONDING\b)/i,/^(?:COUNT\b)/i,/^(?:CREATE\b)/i,/^(?:CROSS\b)/i,/^(?:CUBE\b)/i,/^(?:CURRENT_TIMESTAMP\b)/i,/^(?:CURSOR\b)/i,/^(?:DATABASE(S)?)/i,/^(?:DECLARE\b)/i,/^(?:DEFAULT\b)/i,/^(?:DELETE\b)/i,/^(?:DELETED\b)/i,/^(?:DESC\b)/i,/^(?:DETACH\b)/i,/^(?:DISTINCT\b)/i,/^(?:DROP\b)/i,/^(?:ECHO\b)/i,/^(?:EDGE\b)/i,/^(?:END\b)/i,/^(?:ENUM\b)/i,/^(?:ELSE\b)/i,/^(?:EXCEPT\b)/i,/^(?:EXISTS\b)/i,/^(?:EXPLAIN\b)/i,/^(?:FALSE\b)/i,/^(?:FETCH\b)/i,/^(?:FIRST\b)/i,/^(?:FOREIGN\b)/i,/^(?:FROM\b)/i,/^(?:GO\b)/i,/^(?:GRAPH\b)/i,/^(?:GROUP\b)/i,/^(?:GROUPING\b)/i,/^(?:HAVING\b)/i,/^(?:HELP\b)/i,/^(?:IF\b)/i,/^(?:IDENTITY\b)/i,/^(?:IS\b)/i,/^(?:IN\b)/i,/^(?:INDEX\b)/i,/^(?:INNER\b)/i,/^(?:INSERT\b)/i,/^(?:INSERTED\b)/i,/^(?:INTERSECT\b)/i,/^(?:INTO\b)/i,/^(?:JOIN\b)/i,/^(?:KEY\b)/i,/^(?:LAST\b)/i,/^(?:LET\b)/i,/^(?:LEFT\b)/i,/^(?:LIKE\b)/i,/^(?:LIMIT\b)/i,/^(?:MATCHED\b)/i,/^(?:MATRIX\b)/i,/^(?:MAX\b)/i,/^(?:MERGE\b)/i,/^(?:MIN\b)/i,/^(?:MINUS\b)/i,/^(?:MODIFY\b)/i,/^(?:NATURAL\b)/i,/^(?:NEXT\b)/i,/^(?:NEW\b)/i,/^(?:NOCASE\b)/i,/^(?:NO\b)/i,/^(?:NOT\b)/i,/^(?:NULL\b)/i,/^(?:OFF\b)/i,/^(?:ON\b)/i,/^(?:ONLY\b)/i,/^(?:OFFSET\b)/i,/^(?:OPEN\b)/i,/^(?:OPTION\b)/i,/^(?:OR\b)/i,/^(?:ORDER\b)/i,/^(?:OUTER\b)/i,/^(?:OVER\b)/i,/^(?:PATH\b)/i,/^(?:PARTITION\b)/i,/^(?:PERCENT\b)/i,/^(?:PLAN\b)/i,/^(?:PRIMARY\b)/i,/^(?:PRINT\b)/i,/^(?:PRIOR\b)/i,/^(?:QUERY\b)/i,/^(?:READ\b)/i,/^(?:RECORDSET\b)/i,/^(?:REDUCE\b)/i,/^(?:REFERENCES\b)/i,/^(?:RELATIVE\b)/i,/^(?:REMOVE\b)/i,/^(?:RENAME\b)/i,/^(?:REPEAT\b)/i,/^(?:REQUIRE\b)/i,/^(?:RESTORE\b)/i,/^(?:RETURN\b)/i,/^(?:RETURNS\b)/i,/^(?:RIGHT\b)/i,/^(?:ROLLBACK\b)/i,/^(?:ROLLUP\b)/i,/^(?:ROW\b)/i,/^(?:SCHEMA(S)?)/i,/^(?:SEARCH\b)/i,/^(?:SEMI\b)/i,/^(?:SET\b)/i,/^(?:SETS\b)/i,/^(?:SHOW\b)/i,/^(?:SOME\b)/i,/^(?:SOURCE\b)/i,/^(?:STRATEGY\b)/i,/^(?:STORE\b)/i,/^(?:SUM\b)/i,/^(?:TABLE\b)/i,/^(?:TABLES\b)/i,/^(?:TARGET\b)/i,/^(?:TEMP\b)/i,/^(?:TEMPORARY\b)/i,/^(?:TEXTSTRING\b)/i,/^(?:THEN\b)/i,/^(?:TIMEOUT\b)/i,/^(?:TO\b)/i,/^(?:TOP\b)/i,/^(?:TRAN\b)/i,/^(?:TRANSACTION\b)/i,/^(?:TRUE\b)/i,/^(?:TRUNCATE\b)/i,/^(?:UNION\b)/i,/^(?:UNIQUE\b)/i,/^(?:UPDATE\b)/i,/^(?:USE\b)/i,/^(?:USING\b)/i,/^(?:VALUE(S)?)/i,/^(?:VERTEX\b)/i,/^(?:VIEW\b)/i,/^(?:WHEN\b)/i,/^(?:WHERE\b)/i,/^(?:WHILE\b)/i,/^(?:WITH\b)/i,/^(?:WORK\b)/i,/^(?:(\d*[.])?\d+[eE]\d+)/i,/^(?:(\d*[.])?\d+)/i,/^(?:->)/i,/^(?:#)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:\*)/i,/^(?:\/)/i,/^(?:%)/i,/^(?:!===)/i,/^(?:===)/i,/^(?:!==)/i,/^(?:==)/i,/^(?:>=)/i,/^(?:>)/i,/^(?:<=)/i,/^(?:<>)/i,/^(?:<)/i,/^(?:=)/i,/^(?:!=)/i,/^(?:\()/i,/^(?:\))/i,/^(?:@)/i,/^(?:\{)/i,/^(?:\})/i,/^(?:\])/i,/^(?::-)/i,/^(?:\?-)/i,/^(?:\.)/i,/^(?:,)/i,/^(?:::)/i,/^(?::)/i,/^(?:;)/i,/^(?:\$)/i,/^(?:\?)/i,/^(?:!)/i,/^(?:\^)/i,/^(?:[a-zA-Z_][a-zA-Z_0-9]*)/i,/^(?:$)/i,/^(?:.)/i],conditions:{INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234],inclusive:true}}};return lexer}();parser.lexer=lexer;function Parser(){this.yy={}}Parser.prototype=parser;parser.Parser=Parser;return new Parser}();if(typeof require!=="undefined"&&typeof exports!=="undefined"){exports.parser=parser;exports.Parser=parser.Parser;exports.parse=function(){return parser.parse.apply(parser,arguments)};exports.main=function commonjsMain(args){if(!args[1]){console.log("Usage: "+args[0]+" FILE");process.exit(1)}var source=require("fs").readFileSync(require("path").normalize(args[1]),"utf8");return exports.parser.parse(source)};if(typeof module!=="undefined"&&require.main===module){exports.main(process.argv.slice(1))}}alasql.prettyflag=false;alasql.pretty=function(sql,flag){var pf=alasql.prettyflag;alasql.prettyflag=!flag;var s=alasql.parse(sql).toString();alasql.prettyflag=pf;return s};function K(s){if(alasql.prettyflag){return'<b style="color:blue">'+s.toUpperCase()+"</b>"}else{return s}}function P(s){if(alasql.prettyflag){return'<span style="color:green">'+s+"</span>"}else{return s}}function L(s){if(alasql.prettyflag){return'<span style="color:red">'+s+"</span>"}else{return s}}function N(s){if(alasql.prettyflag){return'<span style="color:green">'+s+"</span>"}else{return s}}function S(s){if(alasql.prettyflag){return'<span style="color:green">'+s+"</span>"}else{return s}}function NL(){if(alasql.prettyflag){return"<br/>"}else{return" "}}function ID(){if(alasql.prettyflag){return"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"}else{return""}}var utils=alasql.utils={};function n2u(s){return"(y="+s+",y===y?y:undefined)"}function und(s,r){return"(y="+s+',typeof y=="undefined"?undefined:'+r+")"}function returnTrue(){return true}function returnUndefined(){}var escapeq=utils.escapeq=function(s){return s.replace(/\'/g,"\\'")};var escapeqq=utils.undoubleq=function(s){return s.replace(/(\')/g,"''")};var doubleq=utils.doubleq=function(s){return s.replace(/(\'\')/g,"\\'")};var doubleqq=utils.doubleqq=function(s){return s.replace(/\'/g,"'")};var cutbom=function(s){if(s[0]==String.fromCharCode(65279))s=s.substr(1);return s};var loadFile=utils.loadFile=function(path,asy,success,error){if(typeof exports=="object"||typeof Meteor!="undefined"&&Meteor.isServer){if(typeof Meteor!="undefined"){var fs=Npm.require("fs")}else{var fs=require("fs")}if(typeof path=="undefined"){var buff="";process.stdin.setEncoding("utf8");process.stdin.on("readable",function(){var chunk=process.stdin.read();if(chunk!==null){buff+=chunk.toString()}});process.stdin.on("end",function(){success(cutbom(buff))})}else{if(asy){fs.readFile(path,function(err,data){if(err){throw err}success(cutbom(data.toString()))})}else{var data=fs.readFileSync(path);success(cutbom(data.toString()))}}}else if(typeof cordova=="object"){window.requestFileSystem(LocalFileSystem.PERSISTENT,0,function(fileSystem){fileSystem.root.getFile(path,{create:false},function(fileEntry){fileEntry.file(function(file){var fileReader=new FileReader;fileReader.onloadend=function(e){success(cutbom(this.result))};fileReader.readAsText(file)})})})}else{if(typeof path=="string"){if(path.substr(0,1)=="#"&&typeof document!="undefined"){var data=document.querySelector(path).textContent;success(data)}else{var xhr=new XMLHttpRequest;xhr.onreadystatechange=function(){if(xhr.readyState===XMLHttpRequest.DONE){if(xhr.status===200){if(success)success(cutbom(xhr.responseText))}else{if(error)error(xhr)}}};xhr.open("GET",path,asy);xhr.send()}}else if(path instanceof Event){var files=path.target.files;var reader=new FileReader;var name=files[0].name;reader.onload=function(e){var data=e.target.result;success(cutbom(data))};reader.readAsText(files[0])}}};var loadBinaryFile=utils.loadBinaryFile=function(path,asy,success,error){if(typeof exports=="object"||typeof Meteor!="undefined"&&Meteor.isServer){if(typeof Meteor!="undefined"){var fs=Npm.require("fs")}else{var fs=require("fs")}if(asy){fs.readFile(path,function(err,data){if(err){throw err}var arr=new Array;for(var i=0;i!=data.length;++i)arr[i]=String.fromCharCode(data[i]);success(arr.join(""))})}else{var data=fs.readFileSync(path);var arr=new Array;for(var i=0;i!=data.length;++i)arr[i]=String.fromCharCode(data[i]);success(arr.join(""))}}else{if(typeof path=="string"){var xhr=new XMLHttpRequest;xhr.open("GET",path,asy);xhr.responseType="arraybuffer";xhr.onload=function(){var data=new Uint8Array(xhr.response);var arr=new Array;for(var i=0;i!=data.length;++i)arr[i]=String.fromCharCode(data[i]);success(arr.join(""))};xhr.send()}else if(path instanceof Event){var files=path.target.files;var reader=new FileReader;var name=files[0].name;reader.onload=function(e){var data=e.target.result;success(data)};reader.readAsBinaryString(files[0])}}};var removeFile=utils.removeFile=function(path,cb){if(typeof exports=="object"){var fs=require("fs");fs.remove(path,cb)}else if(typeof cordova=="object"){window.requestFileSystem(LocalFileSystem.PERSISTENT,0,function(fileSystem){fileSystem.root.getFile(path,{create:false},function(fileEntry){fileEntry.remove(cb);if(cb)cb()},function(){if(cb)cb()})})}else{throw new Error("You can remove files only in Node.js and Apache Cordova")}};var deleteFile=utils.deleteFile=function(path,cb){if(typeof exports=="object"){var fs=require("fs");fs.unlink(path,cb)}};var fileExists=utils.fileExists=function(path,cb){if(typeof exports=="object"){var fs=require("fs");fs.exists(path,cb)}else if(typeof cordova=="object"){window.requestFileSystem(LocalFileSystem.PERSISTENT,0,function(fileSystem){fileSystem.root.getFile(path,{create:false},function(fileEntry){cb(true)},function(){cb(false)})})}else{throw new Error("You can use exists() only in Node.js or Apach Cordova")}};var saveFile=utils.saveFile=function(path,data,cb){var res=1;if(typeof path=="undefined"){res=data;if(cb)res=cb(res)}else{if(typeof exports=="object"){var fs=require("fs");var data=fs.writeFileSync(path,data);if(cb)res=cb(res)}else if(typeof cordova=="object"){window.requestFileSystem(LocalFileSystem.PERSISTENT,0,function(fileSystem){fileSystem.root.getFile(path,{create:true},function(fileEntry){fileEntry.createWriter(function(fileWriter){fileWriter.onwriteend=function(){if(cb)res=cb(res)};fileWriter.write(data)})})})}else{if(isIE()==9){var ndata=data.replace(/\r\n/g,"&#A;&#D;");ndata=ndata.replace(/\n/g,"&#D;");ndata=ndata.replace(/\t/g,"&#9;");var testlink=window.open("about:blank","_blank");testlink.document.write(ndata);testlink.document.close();testlink.document.execCommand("SaveAs",false,path);testlink.close()}else{var blob=new Blob([data],{type:"text/plain;charset=utf-8"});saveAs(blob,path);if(cb)res=cb(res)}}}return res};function isIE(){var myNav=navigator.userAgent.toLowerCase();return myNav.indexOf("msie")!=-1?parseInt(myNav.split("msie")[1]):false}var hash=utils.hash=function hash(str){var h=0;if(str.length==0)return h;for(var i=0;i<str.length;i++){h=(h<<5)-h+str.charCodeAt(i);h=h&h}return h};var arrayUnion=utils.arrayUnion=function(a,b){var r=b.slice(0);a.forEach(function(i){if(r.indexOf(i)<0)r.push(i)});return r};var arrayDiff=utils.arrayDiff=function(a,b){return a.filter(function(i){return b.indexOf(i)<0})};var arrayIntersect=utils.arrayIntersect=function(a,b){var r=[];a.forEach(function(ai){var found=false;b.forEach(function(bi){found=found||ai==bi});if(found){r.push(ai)}});return r};var arrayUnionDeep=utils.arrayUnionDeep=function(a,b){var r=b.slice(0);a.forEach(function(ai){var found=false;r.forEach(function(ri){found=found||deepEqual(ai,ri)});if(!found){r.push(ai)}});return r};var arrayExceptDeep=utils.arrayExceptDeep=function(a,b){var r=[];a.forEach(function(ai){var found=false;b.forEach(function(bi){found=found||deepEqual(ai,bi)});if(!found){r.push(ai)}});return r};var arrayIntersectDeep=utils.arrayIntersectDeep=function(a,b){var r=[];a.forEach(function(ai){var found=false;b.forEach(function(bi){found=found||deepEqual(ai,bi,true)});if(found){r.push(ai)}});return r};var cloneDeep=utils.cloneDeep=function cloneDeep(obj){if(obj==null||typeof obj!="object")return obj;var temp=obj.constructor();for(var key in obj){if(obj.hasOwnProperty(key)){temp[key]=cloneDeep(obj[key])}}return temp};var equalDeep=utils.equalDeep=function equalDeep(x,y,deep){if(deep){if(x==y)return true;var p;for(p in y){if(typeof x[p]=="undefined"){return false}}for(p in y){if(y[p]){switch(typeof y[p]){case"object":if(!equalDeep(y[p],x[p])){return false}break;case"function":if(typeof x[p]=="undefined"||p!="equals"&&y[p].toString()!=x[p].toString())return false;break;default:if(y[p]!=x[p]){return false}}}else{if(x[p])return false}}for(p in x){if(typeof y[p]=="undefined"){return false}}return true}return x==y};var deepEqual=utils.deepEqual=function(x,y){if(typeof x=="object"&&x!=null&&(typeof y=="object"&&y!=null)){if(Object.keys(x).length!=Object.keys(y).length)return false;for(var prop in x){if(y.hasOwnProperty(prop)){if(!deepEqual(x[prop],y[prop]))return false}else return false}return true}else if(x!==y)return false;else return true};var distinctArray=utils.distinctArray=function(data){var uniq={};for(var i=0,ilen=data.length;i<ilen;i++){if(typeof data[i]=="object"){var uix=Object.keys(data[i]).sort().map(function(k){return k+"`"+data[i][k]}).join("`")}else{var uix=data[i]}uniq[uix]=data[i]}var res=[];for(var key in uniq)res.push(uniq[key]);return res};var extend=utils.extend=function extend(a,b){if(typeof a=="undefined")a={};for(var key in b){if(b.hasOwnProperty(key)){a[key]=b[key]}}return a};var flatArray=utils.flatArray=function(a){if(!a||a.length==0)return[];if(typeof a=="object"&&a instanceof alasql.Recordset){return a.data.map(function(ai){return ai[a.columns[0].columnid]})}var key=Object.keys(a[0])[0];if(typeof key=="undefined")return[];return a.map(function(ai){return ai[key]})};var arrayOfArrays=utils.arrayOfArrays=function(a){return a.map(function(aa){var ar=[];for(var key in aa)ar.push(aa[key]);return ar})};var xlsnc=utils.xlsnc=function(i){var addr=String.fromCharCode(65+i%26);if(i>=26){i=(i/26|0)-1;addr=String.fromCharCode(65+i%26)+addr;if(i>26){i=(i/26|0)-1;addr=String.fromCharCode(65+i%26)+addr}}return addr};var xlscn=utils.xlscn=function(s){var n=s.charCodeAt(0)-65;if(s.length>1){n=(n+1)*26+s.charCodeAt(1)-65;if(s.length>2){n=(n+1)*26+s.charCodeAt(2)-65}}return n};var domEmptyChildren=utils.domEmptyChildren=function(container){var len=container.childNodes.length;while(len--){container.removeChild(container.lastChild)}};alasql.utils.uncomment=function uncomment(str){str=("__"+str+"__").split("");var quote=false,quoteSign,blockComment=false,lineComment=false;for(var i=0,l=str.length;i<l;i++){var unescaped=str[i-1]!=="\\"||str[i-2]==="\\";if(quote){if(str[i]===quoteSign&&unescaped)quote=false}else if(blockComment){if(str[i]==="*"&&str[i+1]==="/"){str[i]=str[i+1]="";blockComment=false;i++}else{str[i]=""}}else if(lineComment){if(str[i+1]=="\n"||str[i+1]=="\r")lineComment=false;str[i]=""}else{if(str[i]=='"'||str[i]=="'"){quote=true;quoteSign=str[i]}else if(str[i]=="["&&str[i-1]!="@"){quote=true;quoteSign="]"}else if(str[i]==="/"&&str[i+1]==="*"){str[i]="";blockComment=true}}}str=str.join("").slice(2,-2);return str};alasql.parser=parser;alasql.parse=function(sql){return parser.parse(alasql.utils.uncomment(sql))};alasql.engines={};alasql.databases={};alasql.databasenum=0;alasql.options={};alasql.options.errorlog=false;alasql.options.valueof=false;alasql.options.dropifnotexists=false;alasql.options.datetimeformat="sql";alasql.options.casesensitive=true;alasql.options.logtarget="output";alasql.options.logprompt=true;alasql.options.modifier=undefined;alasql.options.columnlookup=10;alasql.options.autovertex=true;alasql.options.usedbo=true;alasql.options.autocommit=true;alasql.options.cache=true;alasql.options.tsql=true;alasql.options.mysql=true;alasql.options.postgres=true;alasql.options.oracle=true;alasql.options.sqlite=true;alasql.options.orientdb=true;alasql.options.nocount=false;alasql.options.nan=false;alasql.vars={};alasql.declares={};alasql.prompthistory=[];alasql.plugins={};alasql.from={};alasql.into={};alasql.fn={};alasql.aggr={};alasql.busy=0;alasql.MAXSQLCACHESIZE=1e4;
-alasql.DEFAULTDATABASEID="alasql";alasql.lastid=0;alasql.buffer={};alasql.use=function(databaseid){if(!databaseid)databaseid=alasql.DEFAULTDATABASEID;if(alasql.useid==databaseid)return;alasql.useid=databaseid;var db=alasql.databases[alasql.useid];alasql.tables=db.tables;db.resetSqlCache();if(alasql.options.usedbo){alasql.databases.dbo=db}};alasql.exec=function(sql,params,cb,scope){delete alasql.error;if(typeof params=="undefined")params={};if(alasql.options.errorlog){try{return alasql.dexec(alasql.useid,sql,params,cb,scope)}catch(err){alasql.error=err;if(cb)cb(null,alasql.error)}}else{return alasql.dexec(alasql.useid,sql,params,cb,scope)}};alasql.dexec=function(databaseid,sql,params,cb,scope){var db=alasql.databases[databaseid];if(alasql.options.cache){var hh=hash(sql);var statement=db.sqlCache[hh];if(statement&&db.dbversion==statement.dbversion){return statement(params,cb)}}var ast=alasql.parse(sql);if(!ast.statements)return;if(ast.statements.length==0)return 0;else if(ast.statements.length==1){if(ast.statements[0].compile){var statement=ast.statements[0].compile(databaseid);if(!statement)return;statement.sql=sql;statement.dbversion=db.dbversion;if(alasql.options.cache){if(db.sqlCacheSize>alasql.MAXSQLCACHESIZE){db.resetSqlCache()}db.sqlCacheSize++;db.sqlCache[hh]=statement}var res=alasql.res=statement(params,cb,scope);return res}else{alasql.precompile(ast.statements[0],alasql.useid,params);var res=alasql.res=ast.statements[0].execute(databaseid,params,cb,scope);return res}}else{if(cb){alasql.adrun(databaseid,ast,params,cb,scope)}else{return alasql.drun(databaseid,ast,params,cb,scope)}}};alasql.drun=function(databaseid,ast,params,cb,scope){var useid=alasql.useid;if(useid!=databaseid)alasql.use(databaseid);var res=[];for(var i=0,ilen=ast.statements.length;i<ilen;i++){if(ast.statements[i]){if(ast.statements[i].compile){var statement=ast.statements[i].compile(alasql.useid);res.push(alasql.res=statement(params,null,scope))}else{alasql.precompile(ast.statements[i],alasql.useid,params);res.push(alasql.res=ast.statements[i].execute(alasql.useid,params))}}}if(useid!=databaseid)alasql.use(useid);if(cb)cb(res);alasql.res=res;return res};alasql.adrun=function(databaseid,ast,params,cb,scope){var useid=alasql.useid;if(useid!=databaseid)alasql.use(databaseid);var res=[];adrunone();function adrunone(data){if(typeof data!="undefined")res.push(data);var astatement=ast.statements.shift();if(!astatement){if(useid!=databaseid)alasql.use(useid);cb(res)}else{if(astatement.compile){var statement=astatement.compile(alasql.useid);statement(params,adrunone,scope)}else{alasql.precompile(ast.statements[0],alasql.useid,params);astatement.execute(alasql.useid,params,adrunone)}}}};alasql.compile=function(sql,databaseid){if(!databaseid)databaseid=alasql.useid;var ast=alasql.parse(sql);if(ast.statements.length==1){return ast.statements[0].compile(databaseid)}else{throw new Error("Cannot compile, because number of statments in SQL is not equal to 1")}};if(typeof exports=="object"){var Promise=require("es6-promise").Promise}if(typeof Promise=="function"){alasql.promise=function(sql,params){return new Promise(function(resolve,reject){alasql(sql,params,function(data,err){if(err){reject(err)}else{resolve(data)}})})}}var Database=alasql.Database=function(databaseid){var self=this;if(self===alasql){if(databaseid){self=alasql.databases[databaseid];alasql.databases[databaseid]=self;if(!self){throw new Error('Database "'+databaseid+'" not found')}}else{self=alasql.databases.alasql;if(alasql.options.tsql)alasql.databases.tempdb=alasql.databases.alasql}}if(!databaseid){databaseid="db"+alasql.databasenum++}self.databaseid=databaseid;alasql.databases[databaseid]=self;self.tables={};self.views={};self.objects={};self.counter=0;self.indices={};self.resetSqlCache();self.dbversion=0;return self};Database.prototype.resetSqlCache=function(){this.sqlCache={};this.sqlCacheSize=0};Database.prototype.exec=function(sql,params,cb){return alasql.dexec(this.databaseid,sql,params,cb)};Database.prototype.transaction=function(cb){var tx=new alasql.Transaction(this.databaseid);var res=cb(tx);return res};var Transaction=alasql.Transaction=function(databaseid){this.transactionid=Date.now();this.databaseid=databaseid;this.commited=false;this.dbversion=alasql.databases[databaseid].dbversion;this.bank=JSON.stringify(alasql.databases[databaseid]);return this};Transaction.prototype.commit=function(){this.commited=true;alasql.databases[this.databaseid].dbversion=Date.now();delete this.bank};Transaction.prototype.rollback=function(){if(!this.commited){alasql.databases[this.databaseid]=JSON.parse(this.bank);delete this.bank}else{throw new Error("Transaction already commited")}};Transaction.prototype.exec=function(sql,params,cb){return alasql.dexec(this.databaseid,sql,params,cb)};Transaction.prototype.executeSQL=Transaction.prototype.exec;var Table=alasql.Table=function(params){this.columns=[];this.xcolumns={};this.data=[];this.inddefs={};this.indices={};this.uniqs={};this.uniqdefs={};extend(this,params)};Table.prototype.indexColumns=function(){var self=this;self.xcolumns={};self.columns.forEach(function(col){self.xcolumns[col.columnid]=col})};var View=alasql.View=function(params){this.columns=[];this.xcolumns={};this.query=[];extend(this,params)};var Query=alasql.Query=function(params){this.alasql=alasql;this.columns=[];this.xcolumns={};this.selectGroup=[];this.groupColumns={};extend(this,params)};var Recordset=alasql.Recordset=function(params){extend(this,params)};var yy=parser.yy=alasql.yy={};yy.extend=extend;yy.casesensitive=alasql.options.casesensitive;var Base=yy.Base=function(params){return yy.extend(this,params)};Base.prototype.toString=function(){};Base.prototype.toType=function(){};Base.prototype.toJavaScript=function(){};Base.prototype.compile=returnUndefined;Base.prototype.exec=function(){};Base.prototype.compile=returnUndefined;Base.prototype.exec=function(){};yy.Statements=function(params){return yy.extend(this,params)};yy.Statements.prototype.toString=function(){return this.statements.map(function(st){return st.toString()}).join(";"+NL())};yy.Statements.prototype.compile=function(db){var statements=this.statements.map(function(st){return st.compile(db)});if(statements.length==1){return statements[0]}else{return function(params,cb){var res=statements.map(function(st){return st(params)});if(cb)cb(res);return res}}};yy.Search=function(params){return yy.extend(this,params)};yy.Search.prototype.toString=function(){var s=K("SEARCH")+" ";if(this.selectors)s+=this.selectors.toString();if(this.from)s+=K("FROM")+" "+this.from.toString();return s};yy.Search.prototype.toJavaScript=function(context,tableid,defcols){var s="this.queriesfn["+(this.queriesidx-1)+"](this.params,null,"+context+")";return s};yy.Search.prototype.compile=function(databaseid){var dbid=databaseid;var self=this;var statement=function(params,cb){var res;doSearch.bind(self)(dbid,params,function(data){res=modify(statement.query,data);if(cb)res=cb(res)});return res};statement.query={};return statement};function doSearch(databaseid,params,cb){var res;var stope={};var fromdata;var selectors=cloneDeep(this.selectors);if(typeof selectors!="undefined"&&selectors.length>0){if(selectors&&selectors[0]&&selectors[0].srchid=="PROP"&&selectors[0].args&&selectors[0].args[0]){if(selectors[0].args[0].toUpperCase()=="XML"){stope.mode="XML";selectors.shift()}else if(selectors[0].args[0].toUpperCase()=="HTML"){stope.mode="HTML";selectors.shift()}else if(selectors[0].args[0].toUpperCase()=="JSON"){stope.mode="JSON";selectors.shift()}}if(selectors.length>0&&selectors[0].srchid=="VALUE"){stope.value=true;selectors.shift()}}if(this.from instanceof yy.Column){var dbid=this.from.databaseid||databaseid;fromdata=alasql.databases[dbid].tables[this.from.columnid].data}else if(this.from instanceof yy.FuncValue&&alasql.from[this.from.funcid.toUpperCase()]){var args=this.from.args.map(function(arg){var as=arg.toJavaScript();var fn=new Function("params,alasql","var y;return "+as).bind(this);return fn(params,alasql)});fromdata=alasql.from[this.from.funcid.toUpperCase()].apply(this,args)}else if(typeof this.from=="undefined"){fromdata=alasql.databases[databaseid].objects}else{var fromfn=new Function("params,alasql","var y;return "+this.from.toJavaScript());fromdata=fromfn(params,alasql);if(typeof Mongo=="object"&&typeof Mongo.Collection!="object"&&fromdata instanceof Mongo.Collection){fromdata=fromdata.find().fetch()}}if(typeof selectors!="undefined"&&selectors.length>0){if(false){selectors.forEach(function(selector){if(selector.srchid=="TO"){alasql.vars[selector.args[0]]=[]}})}res=processSelector(selectors,0,fromdata)}else{res=fromdata}if(this.into){var a1,a2;if(typeof this.into.args[0]!="undefined"){a1=new Function("params,alasql","var y;return "+this.into.args[0].toJavaScript())(params,alasql)}if(typeof this.into.args[1]!="undefined"){a2=new Function("params,alasql","var y;return "+this.into.args[1].toJavaScript())(params,alasql)}res=alasql.into[this.into.funcid.toUpperCase()](a1,a2,res,[],cb)}else{if(stope.value&&res.length>0)res=res[0];if(cb)res=cb(res)}return res;function processSelector(selectors,sidx,value){var sel=selectors[sidx];var SECURITY_BREAK=1e5;if(sel.selid){if(sel.selid=="PATH"){var queue=[{node:value,stack:[]}];var visited={};var path=[];var objects=alasql.databases[alasql.useid].objects;while(queue.length>0){var q=queue.shift();var node=q.node;var stack=q.stack;var r=processSelector(sel.args,0,node);if(r.length>0){if(sidx+1+1>selectors.length){return stack}else{var rv=[];if(stack&&stack.length>0){stack.forEach(function(stv){rv=rv.concat(processSelector(selectors,sidx+1,stv))})}return rv}}else{if(typeof visited[node.$id]!="undefined"){continue}else{visited[node.$id]=true;if(node.$out&&node.$out.length>0){node.$out.forEach(function(edgeid){var edge=objects[edgeid];var stack2=stack.concat(edge);stack2.push(objects[edge.$out[0]]);queue.push({node:objects[edge.$out[0]],stack:stack2})})}}}}return[]}if(sel.selid=="NOT"){var nest=processSelector(sel.args,0,value);if(nest.length>0){return[]}else{if(sidx+1+1>selectors.length){return[value]}else{return processSelector(selectors,sidx+1,value)}}}else if(sel.selid=="DISTINCT"){if(typeof sel.args=="undefined"||sel.args.length==0){var nest=distinctArray(value)}else{var nest=processSelector(sel.args,0,value)}if(nest.length==0){return[]}else{var res=distinctArray(nest);if(sidx+1+1>selectors.length){return res}else{return processSelector(selectors,sidx+1,res)}}}else if(sel.selid=="AND"){var res=true;sel.args.forEach(function(se){res=res&&processSelector(se,0,value).length>0});if(!res){return[]}else{if(sidx+1+1>selectors.length){return[value]}else{return processSelector(selectors,sidx+1,value)}}}else if(sel.selid=="OR"){var res=false;sel.args.forEach(function(se){res=res||processSelector(se,0,value).length>0});if(!res){return[]}else{if(sidx+1+1>selectors.length){return[value]}else{return processSelector(selectors,sidx+1,value)}}}else if(sel.selid=="ALL"){var nest=processSelector(sel.args[0],0,value);if(nest.length==0){return[]}else{if(sidx+1+1>selectors.length){return nest}else{return processSelector(selectors,sidx+1,nest)}}}else if(sel.selid=="ANY"){var nest=processSelector(sel.args[0],0,value);if(nest.length==0){return[]}else{if(sidx+1+1>selectors.length){return[nest[0]]}else{return processSelector(selectors,sidx+1,[nest[0]])}}}else if(sel.selid=="UNIONALL"){var nest=[];sel.args.forEach(function(se){nest=nest.concat(processSelector(se,0,value))});if(nest.length==0){return[]}else{if(sidx+1+1>selectors.length){return nest}else{return processSelector(selectors,sidx+1,nest)}}}else if(sel.selid=="UNION"){var nest=[];sel.args.forEach(function(se){nest=nest.concat(processSelector(se,0,value))});var nest=distinctArray(nest);if(nest.length==0){return[]}else{if(sidx+1+1>selectors.length){return nest}else{return processSelector(selectors,sidx+1,nest)}}}else if(sel.selid=="IF"){var nest=processSelector(sel.args,0,value);if(nest.length==0){return[]}else{if(sidx+1+1>selectors.length){return[value]}else{return processSelector(selectors,sidx+1,value)}}}else if(sel.selid=="REPEAT"){var lmin=sel.args[0].value;if(!sel.args[1]){var lmax=lmin}else{var lmax=sel.args[1].value}if(sel.args[2]){var lvar=sel.args[2].variable}else{var lvar}var lsel=sel.sels;var retval=[];if(lmin==0){if(sidx+1+1>selectors.length){retval=[value]}else{if(lvar)alasql.vars[lvar]=0;retval=retval.concat(processSelector(selectors,sidx+1,value))}}if(lmax>0){var nests=[{value:value,lvl:1}];var i=0;while(nests.length>0){var nest=nests[0];nests.shift();if(nest.lvl<=lmax){if(lvar)alasql.vars[lvar]=nest.lvl;var nest1=processSelector(sel.sels,0,nest.value);nest1.forEach(function(n){nests.push({value:n,lvl:nest.lvl+1})});if(nest.lvl>=lmin){if(sidx+1+1>selectors.length){retval=retval.concat(nest1)}else{nest1.forEach(function(n){retval=retval.concat(processSelector(selectors,sidx+1,n))})}}}i++;if(i>SECURITY_BREAK){throw new Error("Security brake. Number of iterations = "+i)}}}return retval}else if(sel.selid=="TO"){var oldv=alasql.vars[sel.args[0]];var newv=[];if(typeof oldv!="undefined"){newv=oldv.slice(0)}else{newv=[]}newv.push(value);if(sidx+1+1>selectors.length){return[value]}else{alasql.vars[sel.args[0]]=newv;var r1=processSelector(selectors,sidx+1,value);alasql.vars[sel.args[0]]=oldv;return r1}}else if(sel.selid=="ARRAY"){var nest=processSelector(sel.args,0,value);if(nest.length>0){var val=nest}else{return[]}if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="SUM"){var nest=processSelector(sel.args,0,value);if(nest.length>0){var val=nest.reduce(function(sum,current){return sum+current},0)}else{return[]}if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="AVG"){var nest=processSelector(sel.args,0,value);if(nest.length>0){var val=nest.reduce(function(sum,current){return sum+current},0)/nest.length}else{return[]}if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="COUNT"){var nest=processSelector(sel.args,0,value);if(nest.length>0){var val=nest.length}else{return[]}if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="FIRST"){var nest=processSelector(sel.args,0,value);if(nest.length>0)var val=nest[0];else return[];if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="LAST"){var nest=processSelector(sel.args,0,value);if(nest.length>0)var val=nest[nest.length-1];else return[];if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="MIN"){var nest=processSelector(sel.args,0,value);if(nest.length==0)return[];var val=nest.reduce(function(min,current){return Math.min(min,current)},Infinity);if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="MAX"){var nest=processSelector(sel.args,0,value);if(nest.length==0)return[];var val=nest.reduce(function(max,current){return Math.max(max,current)},-Infinity);if(sidx+1+1>selectors.length){return[val]}else{return processSelector(selectors,sidx+1,val)}}else if(sel.selid=="PLUS"){var retval=[];var nests=processSelector(sel.args,0,value).slice();if(sidx+1+1>selectors.length){retval=retval.concat(nests)}else{nests.forEach(function(n){retval=retval.concat(processSelector(selectors,sidx+1,n))})}var i=0;while(nests.length>0){var nest=nests.shift();nest=processSelector(sel.args,0,nest);nests=nests.concat(nest);if(sidx+1+1>selectors.length){retval=retval.concat(nest)}else{nest.forEach(function(n){var rn=processSelector(selectors,sidx+1,n);retval=retval.concat(rn)})}i++;if(i>SECURITY_BREAK){throw new Error("Security brake. Number of iterations = "+i)}}return retval}else if(sel.selid=="STAR"){var retval=[];retval=processSelector(selectors,sidx+1,value);var nests=processSelector(sel.args,0,value).slice();if(sidx+1+1>selectors.length){retval=retval.concat(nests)}else{nests.forEach(function(n){retval=retval.concat(processSelector(selectors,sidx+1,n))})}var i=0;while(nests.length>0){var nest=nests[0];nests.shift();nest=processSelector(sel.args,0,nest);nests=nests.concat(nest);if(sidx+1+1>selectors.length){}else{nest.forEach(function(n){retval=retval.concat(processSelector(selectors,sidx+1,n))})}i++;if(i>SECURITY_BREAK){throw new Error("Security brake. Number of iterations = "+i)}}return retval}else if(sel.selid=="QUESTION"){var retval=[];retval=retval.concat(processSelector(selectors,sidx+1,value));var nest=processSelector(sel.args,0,value);if(sidx+1+1>selectors.length){}else{nest.forEach(function(n){retval=retval.concat(processSelector(selectors,sidx+1,n))})}return retval}else if(sel.selid=="WITH"){var nest=processSelector(sel.args,0,value);if(nest.length==0){return[]}else{var r={status:1,values:nest}}}else if(sel.selid=="ROOT"){if(sidx+1+1>selectors.length){return[value]}else{return processSelector(selectors,sidx+1,fromdata)}}else{throw new Error("Wrong selector "+sel.selid)}}else if(sel.srchid){var r=alasql.srch[sel.srchid.toUpperCase()](value,sel.args,stope,params)}else{throw new Error("Selector not found")}if(typeof r=="undefined"){r={status:1,values:[value]}}var res=[];if(r.status==1){var arr=r.values;if(sidx+1+1>selectors.length){res=arr}else{for(var i=0;i<r.values.length;i++){res=res.concat(processSelector(selectors,sidx+1,arr[i]))}}}return res}}alasql.srch={};alasql.srch.PROP=function(val,args,stope){if(stope.mode=="XML"){var arr=[];val.children.forEach(function(v){if(v.name.toUpperCase()==args[0].toUpperCase()){arr.push(v)}});if(arr.length>0){return{status:1,values:arr}}else{return{status:-1,values:[]}}}else{if(typeof val!="object"||val===null||typeof args!="object"||typeof val[args[0]]=="undefined"){return{status:-1,values:[]}}else{return{status:1,values:[val[args[0]]]}}}};alasql.srch.APROP=function(val,args,stope){if(typeof val!="object"||val===null||typeof args!="object"||typeof val[args[0]]=="undefined"){return{status:1,values:[undefined]}}else{return{status:1,values:[val[args[0]]]}}};alasql.srch.ORDERBY=function(val,args,stope){var res=val.sort(compileSearchOrder(args));return{status:1,values:res}};alasql.srch.EQ=function(val,args,stope,params){var exprs=args[0].toJavaScript("x","");var exprfn=new Function("x,alasql,params","return "+exprs);if(val==exprfn(val,alasql,params)){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.LIKE=function(val,args,stope,params){var exprs=args[0].toJavaScript("x","");var exprfn=new Function("x,alasql,params","return "+exprs);if(val.toUpperCase().match(new RegExp("^"+exprfn(val,alasql,params).toUpperCase().replace(/%/g,".*")+"$"),"g")){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.ATTR=function(val,args,stope){if(stope.mode=="XML"){if(typeof args=="undefined"){return{status:1,values:[val.attributes]}}else{if(typeof val=="object"&&typeof val.attributes=="object"&&typeof val.attributes[args[0]]!="undefined"){return{status:1,values:[val.attributes[args[0]]]}}else{return{status:-1,values:[]}}}}else{throw new Error("ATTR is not using in usual mode")}};alasql.srch.CONTENT=function(val,args,stope){if(stope.mode=="XML"){return{status:1,values:[val.content]}}else{throw new Error("ATTR is not using in usual mode")}};alasql.srch.SHARP=function(val,args,stope){var obj=alasql.databases[alasql.useid].objects[args[0]];if(typeof val!="undefined"&&val===obj){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.PARENT=function(val,args,stope){console.log("PARENT");return{status:-1,values:[]}};alasql.srch.CHILD=function(val,args,stope){if(typeof val=="object"){if(val instanceof Array){return{status:1,values:val}}else{if(stope.mode=="XML"){return{status:1,values:Object.keys(val.children).map(function(key){return val.children[key]})}}else{return{status:1,values:Object.keys(val).map(function(key){return val[key]})}}}}else{return{status:1,values:[]}}};alasql.srch.KEYS=function(val,args){if(typeof val=="object"&&val!==null){return{status:1,values:Object.keys(val)}}else{return{status:1,values:[]}}};alasql.srch.WHERE=function(val,args){var exprs=args[0].toJavaScript("x","");var exprfn=new Function("x,alasql","return "+exprs);if(exprfn(val,alasql)){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.NAME=function(val,args){if(val.name==args[0]){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.CLASS=function(val,args){if(val.$class==args){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.VERTEX=function(val,args){if(val.$node=="VERTEX"){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.INSTANCEOF=function(val,args){if(val instanceof alasql.fn[args[0]]){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.EDGE=function(val,args){if(val.$node=="EDGE"){return{status:1,values:[val]}}else{return{status:-1,values:[]}}};alasql.srch.EX=function(val,args,stope,params){var exprs=args[0].toJavaScript("x","");var exprfn=new Function("x,alasql,params","return "+exprs);return{status:1,values:[exprfn(val,alasql,params)]}};alasql.srch.RETURN=function(val,args,stope,params){var res={};if(args&&args.length>0){args.forEach(function(arg){var exprs=arg.toJavaScript("x","");var exprfn=new Function("x,alasql,params","return "+exprs);if(typeof arg.as=="undefined")arg.as=arg.toString();res[arg.as]=exprfn(val,alasql,params)})}return{status:1,values:[res]}};alasql.srch.REF=function(val,args){return{status:1,values:[alasql.databases[alasql.useid].objects[val]]}};alasql.srch.OUT=function(val,args){if(val.$out&&val.$out.length>0){var res=val.$out.map(function(v){return alasql.databases[alasql.useid].objects[v]});return{status:1,values:res}}else{return{status:-1,values:[]}}};alasql.srch.IN=function(val,args){if(val.$in&&val.$in.length>0){var res=val.$in.map(function(v){return alasql.databases[alasql.useid].objects[v]});return{status:1,values:res}}else{return{status:-1,values:[]}}};alasql.srch.AS=function(val,args){alasql.vars[args[0]]=val;return{status:1,values:[val]}};alasql.srch.AT=function(val,args){var v=alasql.vars[args[0]];return{status:1,values:[v]}};alasql.srch.CLONEDEEP=function(val,args){var z=cloneDeep(val);return{status:1,values:[z]}};alasql.srch.SET=function(val,args,stope,params){var s=args.map(function(st){if(st.method=="@"){return"alasql.vars['"+st.variable+"']="+st.expression.toJavaScript("x","")}else if(st.method=="$"){return"params['"+st.variable+"']="+st.expression.toJavaScript("x","")}else{return"x['"+st.column.columnid+"']="+st.expression.toJavaScript("x","")}}).join(";");var setfn=new Function("x,params,alasql",s);setfn(val,params,alasql);return{status:1,values:[val]}};alasql.srch.ROW=function(val,args,stope,params){var s="var y;return [";s+=args.map(function(arg){return arg.toJavaScript("x","")}).join(",");s+="]";var setfn=new Function("x,params,alasql",s);var rv=setfn(val,params,alasql);return{status:1,values:[rv]}};alasql.srch.D3=function(val,args,stope,params){if(val.$node=="VERTEX"){}else if(val.$node=="EDGE"){val.source=val.$in[0];val.target=val.$out[0]}return{status:1,values:[val]}};compileSearchOrder=function(order){if(order){if(order&&order.length==1&&order[0].expression&&typeof order[0].expression=="function"){var func=order[0].expression;return function(a,b){var ra=func(a),rb=func(b);if(ra>rb)return 1;if(ra==rb)return 0;return-1}}var s="";var sk="";order.forEach(function(ord,idx){var dg="";if(ord.expression instanceof yy.NumValue){ord.expression=self.columns[ord.expression.value-1]}if(ord.expression instanceof yy.Column){var columnid=ord.expression.columnid;if(alasql.options.valueof)dg=".valueOf()";if(ord.nocase)dg+=".toUpperCase()";if(columnid=="_"){s+="if(a"+dg+(ord.direction=="ASC"?">":"<")+"b"+dg+")return 1;";s+="if(a"+dg+"==b"+dg+"){"}else{s+="if((a['"+columnid+"']||'')"+dg+(ord.direction=="ASC"?">":"<")+"(b['"+columnid+"']||'')"+dg+")return 1;";s+="if((a['"+columnid+"']||'')"+dg+"==(b['"+columnid+"']||'')"+dg+"){"}}else{dg=".valueOf()";if(ord.nocase)dg+=".toUpperCase()";s+="if(("+ord.toJavaScript("a","")+"||'')"+dg+(ord.direction=="ASC"?">(":"<(")+ord.toJavaScript("b","")+"||'')"+dg+")return 1;";s+="if(("+ord.toJavaScript("a","")+"||'')"+dg+"==("+ord.toJavaScript("b","")+"||'')"+dg+"){"}sk+="}"});s+="return 0;";s+=sk+"return -1";return new Function("a,b",s)}};function queryfn(query,oldscope,cb,A,B){var ms;query.sourceslen=query.sources.length;var slen=query.sourceslen;query.query=query;query.A=A;query.B=B;query.cb=cb;query.oldscope=oldscope;if(query.queriesfn){query.sourceslen+=query.queriesfn.length;slen+=query.queriesfn.length;query.queriesdata=[];query.queriesfn.forEach(function(q,idx){q.query.params=query.params;if(false){queryfn(q.query,query.oldscope,queryfn2,-idx-1,query)}else{queryfn2([],-idx-1,query)}})}var scope;if(!oldscope)scope={};else scope=cloneDeep(oldscope);query.scope=scope;var result;query.sources.forEach(function(source,idx){source.query=query;var rs=source.datafn(query,query.params,queryfn2,idx,alasql);if(typeof rs!=undefined){if((query.intofn||query.intoallfn)&&rs instanceof Array)rs=rs.length;result=rs}source.queriesdata=query.queriesdata});if(slen==0)result=queryfn3(query);return result}function queryfn2(data,idx,query){if(idx>=0){var source=query.sources[idx];source.data=data;if(typeof source.data=="function"){source.getfn=source.data;source.dontcache=source.getfn.dontcache;if(source.joinmode=="OUTER"||source.joinmode=="RIGHT"||source.joinmode=="ANTI"){source.dontcache=false}source.data={}}}else{query.queriesdata[-idx-1]=flatArray(data)}query.sourceslen--;if(query.sourceslen>0)return;return queryfn3(query)}function queryfn3(query){var scope=query.scope;preIndex(query);query.data=[];query.xgroups={};query.groups=[];var h=0;doJoin(query,scope,h);if(query.groupfn){query.data=[];if(query.groups.length==0){var g={};if(query.selectGroup.length>0){query.selectGroup.forEach(function(sg){if(sg.aggregatorid=="COUNT"||sg.aggregatorid=="SUM"){g[sg.nick]=0}else{g[sg.nick]=undefined}})}query.groups=[g]}for(var i=0,ilen=query.groups.length;i<ilen;i++){var g=query.groups[i];if(!query.havingfn||query.havingfn(g,query.params,alasql)){var d=query.selectgfn(g,query.params,alasql);query.data.push(d)}}}doDistinct(query);if(query.unionallfn){if(query.corresponding){if(!query.unionallfn.query.modifier)query.unionallfn.query.modifier=undefined;var ud=query.unionallfn(query.params)}else{if(!query.unionallfn.query.modifier)query.unionallfn.query.modifier="RECORDSET";var nd=query.unionallfn(query.params);var ud=[];for(var i=0,ilen=nd.data.length;i<ilen;i++){var r={};for(var j=0,jlen=Math.min(query.columns.length,nd.columns.length);j<jlen;j++){r[query.columns[j].columnid]=nd.data[i][nd.columns[j].columnid]}ud.push(r)}}query.data=query.data.concat(ud)}else if(query.unionfn){if(query.corresponding){if(!query.unionfn.query.modifier)query.unionfn.query.modifier="ARRAY";var ud=query.unionfn(query.params)}else{if(!query.unionfn.query.modifier)query.unionfn.query.modifier="RECORDSET";var nd=query.unionfn(query.params);var ud=[];for(var i=0,ilen=nd.data.length;i<ilen;i++){var r={};for(var j=0,jlen=Math.min(query.columns.length,nd.columns.length);j<jlen;j++){r[query.columns[j].columnid]=nd.data[i][nd.columns[j].columnid]}ud.push(r)}}query.data=arrayUnionDeep(query.data,ud)}else if(query.exceptfn){if(query.corresponding){if(!query.exceptfn.query.modifier)query.exceptfn.query.modifier="ARRAY";var ud=query.exceptfn(query.params)}else{if(!query.exceptfn.query.modifier)query.exceptfn.query.modifier="RECORDSET";var nd=query.exceptfn(query.params);var ud=[];for(var i=0,ilen=nd.data.length;i<ilen;i++){var r={};for(var j=0,jlen=Math.min(query.columns.length,nd.columns.length);j<jlen;j++){r[query.columns[j].columnid]=nd.data[i][nd.columns[j].columnid]}ud.push(r)}}query.data=arrayExceptDeep(query.data,ud)}else if(query.intersectfn){if(query.corresponding){if(!query.intersectfn.query.modifier)query.intersectfn.query.modifier=undefined;var ud=query.intersectfn(query.params)}else{if(!query.intersectfn.query.modifier)query.intersectfn.query.modifier="RECORDSET";var nd=query.intersectfn(query.params);var ud=[];for(var i=0,ilen=nd.data.length;i<ilen;i++){var r={};for(var j=0,jlen=Math.min(query.columns.length,nd.columns.length);j<jlen;j++){r[query.columns[j].columnid]=nd.data[i][nd.columns[j].columnid]}ud.push(r)}}query.data=arrayIntersectDeep(query.data,ud)}if(query.orderfn){if(query.explain)var ms=Date.now();query.data=query.data.sort(query.orderfn);if(query.explain){query.explaination.push({explid:query.explid++,description:"QUERY BY",ms:Date.now()-ms})}}doLimit(query);if(typeof angular!="undefined"){query.removeKeys.push("$$hashKey")}if(query.removeKeys.length>0){var removeKeys=query.removeKeys;var jlen=removeKeys.length;if(jlen>0){for(var i=0,ilen=query.data.length;i<ilen;i++){for(var j=0;j<jlen;j++){delete query.data[i][removeKeys[j]]}}}if(query.columns.length>0){query.columns=query.columns.filter(function(column){var found=false;removeKeys.forEach(function(key){if(column.columnid==key)found=true});return!found})}}if(typeof query.removeLikeKeys!="undefined"&&query.removeLikeKeys.length>0){var removeLikeKeys=query.removeLikeKeys;for(var i=0,ilen=query.data.length;i<ilen;i++){var r=query.data[i];for(var k in r){for(var j=0;j<query.removeLikeKeys.length;j++){if(k.match(query.removeLikeKeys[j])){delete r[k]}}}}if(query.columns.length>0){query.columns=query.columns.filter(function(column){var found=false;removeLikeKeys.forEach(function(key){if(column.columnid.match(key))found=true});return!found})}}if(query.intoallfn){var res=query.intoallfn(query.columns,query.cb,query.params,query.alasql);return res}else if(query.intofn){for(var i=0,ilen=query.data.length;i<ilen;i++){query.intofn(query.data[i],i,query.params,query.alasql)}if(query.cb)query.cb(query.data.length,query.A,query.B);return query.data.length}else{var res=query.data;if(query.cb)res=query.cb(query.data,query.A,query.B);return res}}function doLimit(query){if(query.limit){var offset=0;if(query.offset)offset=(query.offset|0)-1||0;var limit;if(query.percent){limit=(query.data.length*query.limit/100|0)+offset}else{limit=(query.limit|0)+offset}query.data=query.data.slice(offset,limit)}}function doDistinct(query){if(query.distinct){var uniq={};for(var i=0,ilen=query.data.length;i<ilen;i++){var uix=Object.keys(query.data[i]).map(function(k){return query.data[i][k]}).join("`");uniq[uix]=query.data[i]}query.data=[];for(var key in uniq)query.data.push(uniq[key])}}preIndex=function(query){for(var k=0,klen=query.sources.length;k<klen;k++){var source=query.sources[k];if(k>0&&source.optimization=="ix"&&source.onleftfn&&source.onrightfn){if(source.databaseid&&alasql.databases[source.databaseid].tables[source.tableid]){if(!alasql.databases[source.databaseid].tables[source.tableid].indices)query.database.tables[source.tableid].indices={};var ixx=alasql.databases[source.databaseid].tables[source.tableid].indices[hash(source.onrightfns+"`"+source.srcwherefns)];if(!alasql.databases[source.databaseid].tables[source.tableid].dirty&&ixx){source.ix=ixx}}if(!source.ix){source.ix={};var scope={};var i=0;var ilen=source.data.length;var dataw;while((dataw=source.data[i])||source.getfn&&(dataw=source.getfn(i))||i<ilen){if(source.getfn&&!source.dontcache)source.data[i]=dataw;scope[source.alias||source.tableid]=dataw;if(source.srcwherefn(scope,query.params,alasql)){var addr=source.onrightfn(scope,query.params,alasql);var group=source.ix[addr];if(!group){group=source.ix[addr]=[]}group.push(dataw)}i++}if(source.databaseid&&alasql.databases[source.databaseid].tables[source.tableid]){alasql.databases[source.databaseid].tables[source.tableid].indices[hash(source.onrightfns+"`"+source.srcwherefns)]=source.ix}}}else if(source.wxleftfn){if(!alasql.databases[source.databaseid].engineid){var ixx=alasql.databases[source.databaseid].tables[source.tableid].indices[hash(source.wxleftfns+"`")]
-}if(!alasql.databases[source.databaseid].tables[source.tableid].dirty&&ixx){source.ix=ixx;source.data=source.ix[source.wxrightfn(null,query.params,alasql)]}else{source.ix={};var scope={};var i=0;var ilen=source.data.length;var dataw;while((dataw=source.data[i])||source.getfn&&(dataw=source.getfn(i))||i<ilen){if(source.getfn&&!source.dontcache)source.data[i]=dataw;scope[source.alias||source.tableid]=source.data[i];var addr=source.wxleftfn(scope,query.params,alasql);var group=source.ix[addr];if(!group){group=source.ix[addr]=[]}group.push(source.data[i]);i++}if(!alasql.databases[source.databaseid].engineid){alasql.databases[source.databaseid].tables[source.tableid].indices[hash(source.wxleftfns+"`")]=source.ix}}if(source.srcwherefns){if(source.data){var scope={};source.data=source.data.filter(function(r){scope[source.alias]=r;return source.srcwherefn(scope,query.params,alasql)})}else{source.data=[]}}}else if(source.srcwherefns&&!source.dontcache){if(source.data){var scope={};source.data=source.data.filter(function(r){scope[source.alias]=r;return source.srcwherefn(scope,query.params,alasql)});var scope={};var i=0;var ilen=source.data.length;var dataw;var res=[];while((dataw=source.data[i])||source.getfn&&(dataw=source.getfn(i))||i<ilen){if(source.getfn&&!source.dontcache)source.data[i]=dataw;scope[source.alias]=dataw;if(source.srcwherefn(scope,query.params,alasql))res.push(dataw);i++}source.data=res}else{source.data=[]}}if(source.databaseid&&alasql.databases[source.databaseid].tables[source.tableid]){}else{}}};function doJoin(query,scope,h){if(h>=query.sources.length){if(query.wherefn(scope,query.params,alasql)){if(query.groupfn){query.groupfn(scope,query.params,alasql)}else{query.data.push(query.selectfn(scope,query.params,alasql))}}}else if(query.sources[h].applyselect){var source=query.sources[h];source.applyselect(query.params,function(data){if(data.length>0){for(var i=0;i<data.length;i++){scope[source.alias]=data[i];doJoin(query,scope,h+1)}}else{if(source.applymode=="OUTER"){scope[source.alias]={};doJoin(query,scope,h+1)}}},scope)}else{var source=query.sources[h];var nextsource=query.sources[h+1];if(true){var tableid=source.alias||source.tableid;var pass=false;var data=source.data;var opt=false;if(!source.getfn||source.getfn&&!source.dontcache){if(source.joinmode!="RIGHT"&&source.joinmode!="OUTER"&&source.joinmode!="ANTI"&&source.optimization=="ix"){data=source.ix[source.onleftfn(scope,query.params,alasql)]||[];opt=true}}var i=0;if(typeof data=="undefined"){throw new Error("Data source number "+h+" in undefined")}var ilen=data.length;var dataw;while((dataw=data[i])||!opt&&(source.getfn&&(dataw=source.getfn(i)))||i<ilen){if(!opt&&source.getfn&&!source.dontcache)data[i]=dataw;scope[tableid]=dataw;if(!source.onleftfn||source.onleftfn(scope,query.params,alasql)==source.onrightfn(scope,query.params,alasql)){if(source.onmiddlefn(scope,query.params,alasql)){if(source.joinmode!="SEMI"&&source.joinmode!="ANTI"){doJoin(query,scope,h+1)}if(source.joinmode!="LEFT"&&source.joinmode!="INNER"){dataw._rightjoin=true}pass=true}}i++}if((source.joinmode=="LEFT"||source.joinmode=="OUTER"||source.joinmode=="SEMI")&&!pass){scope[tableid]={};doJoin(query,scope,h+1)}}if(h+1<query.sources.length){if(nextsource.joinmode=="OUTER"||nextsource.joinmode=="RIGHT"||nextsource.joinmode=="ANTI"){scope[source.alias]={};var j=0;var jlen=nextsource.data.length;var dataw;while((dataw=nextsource.data[j])||nextsource.getfn&&(dataw=nextsource.getfn(j))||j<jlen){if(nextsource.getfn&&!nextsource.dontcache)nextsource.data[j]=dataw;if(!dataw._rightjoin){scope[nextsource.alias]=dataw;doJoin(query,scope,h+2)}else{delete dataw._rightjoin}j++}}}scope[tableid]=undefined}}function swapSources(query,h){var source=query.sources[h];var nextsource=query.sources[h+1];var onleftfn=source.onleftfn;var onleftfns=source.onleftfns;var onrightfn=source.onrightfn;var onrightfns=source.onrightfns;var optimization=source.optimization;source.onleftfn=nextsource.onrightfn;source.onleftfns=nextsource.onrightfns;source.onrightfn=nextsource.onleftfn;source.onrightfns=nextsource.onleftfns;source.optimization=nextsource.optimization;nextsource.onleftfn=onleftfn;nextsource.onleftfns=onleftfns;nextsource.onrightfn=onrightfn;nextsource.onrightfns=onrightfns;nextsource.optimization=optimization;query.sources[h]=nextsource;query.sources[h+1]=source}yy.Select=function(params){return yy.extend(this,params)};yy.Select.prototype.toString=function(){var s="";if(this.explain)s+=K("EXPLAIN")+" ";s+=K("SELECT")+" ";if(this.modifier)s+=K(this.modifier)+" ";if(this.top){s+=K("TOP")+" "+N(this.top.value)+" ";if(this.percent)s+=K("PERCENT")+" "}s+=this.columns.map(function(col){var s=col.toString();if(typeof col.as!="undefined")s+=" "+K("AS")+" "+L(col.as);return s}).join(", ");if(this.from){s+=NL()+ID()+K("FROM")+" "+this.from.map(function(f){var ss=f.toString();if(f.as)ss+=" "+K("AS")+" "+f.as;return ss}).join(",")}if(this.joins){s+=this.joins.map(function(jn){var ss=NL()+ID();if(jn.joinmode)ss+=K(jn.joinmode)+" ";if(jn.table)ss+=K("JOIN")+" "+jn.table.toString();else if(jn instanceof yy.Apply)ss+=jn.toString();else{throw new Error("Wrong type in JOIN mode")}if(jn.using)ss+=" "+K("USING")+" "+jn.using.toString();if(jn.on)ss+=" "+K("ON")+" "+jn.on.toString();return ss})}if(this.where)s+=NL()+ID()+K("WHERE")+" "+this.where.toString();if(this.group&&this.group.length>0){s+=NL()+ID()+K("GROUP BY")+" "+this.group.map(function(grp){return grp.toString()}).join(", ")}if(this.having)s+=NL()+ID()+K("HAVING")+" "+this.having.toString();if(this.order&&this.order.length>0){s+=NL()+ID()+K("ORDER BY")+" "+this.order.map(function(ord){return ord.toString()}).join(", ")}if(this.limit)s+=NL()+ID()+K("LIMIT")+" "+this.limit.value;if(this.offset)s+=NL()+ID()+K("OFFSET")+" "+this.offset.value;if(this.union)s+=NL()+K("UNION")+(this.corresponding?" "+K("CORRESPONDING"):"")+NL()+this.union.toString();if(this.unionall)s+=NL()+K("UNION ALL")+(this.corresponding?" "+K("CORRESPONDING"):"")+NL()+this.unionall.toString();if(this.except)s+=NL()+K("EXCEPT")+(this.corresponding?" "+K("CORRESPONDING"):"")+NL()+this.except.toString();if(this.intersect)s+=NL()+K("INTERSECT")+(this.corresponding?" "+K("CORRESPONDING"):"")+NL()+this.intersect.toString();return s};yy.Select.prototype.toJavaScript=function(context,tableid,defcols){var s="alasql.utils.flatArray(this.queriesfn["+(this.queriesidx-1)+"](this.params,null,"+context+"))[0]";return s};yy.Select.prototype.compile=function(databaseid){var db=alasql.databases[databaseid];var query=new Query;query.removeKeys=[];query.explain=this.explain;query.explaination=[];query.explid=1;query.modifier=this.modifier;query.database=db;this.compileWhereExists(query);this.compileQueries(query);query.defcols=this.compileDefCols(query,databaseid);query.fromfn=this.compileFrom(query);if(this.joins)this.compileJoins(query);query.rownums=[];this.compileSelectGroup0(query);if(this.group||query.selectGroup.length>0){query.selectgfns=this.compileSelectGroup1(query)}else{query.selectfns=this.compileSelect1(query)}this.compileRemoveColumns(query);if(this.where)this.compileWhereJoins(query);query.wherefn=this.compileWhere(query);if(this.group||query.selectGroup.length>0)query.groupfn=this.compileGroup(query);if(this.having)query.havingfn=this.compileHaving(query);if(this.group||query.selectGroup.length>0){query.selectgfn=this.compileSelectGroup2(query)}else{query.selectfn=this.compileSelect2(query)}query.distinct=this.distinct;if(this.order)query.orderfn=this.compileOrder(query);if(this.top){query.limit=this.top.value}else if(this.limit){query.limit=this.limit.value;if(this.offset){query.offset=this.offset.value}}query.percent=this.percent;query.corresponding=this.corresponding;if(this.union){query.unionfn=this.union.compile(databaseid);if(this.union.order){query.orderfn=this.union.compileOrder(query)}else{query.orderfn=null}}else if(this.unionall){query.unionallfn=this.unionall.compile(databaseid);if(this.unionall.order){query.orderfn=this.unionall.compileOrder(query)}else{query.orderfn=null}}else if(this.except){query.exceptfn=this.except.compile(databaseid);if(this.except.order){query.orderfn=this.except.compileOrder(query)}else{query.orderfn=null}}else if(this.intersect){query.intersectfn=this.intersect.compile(databaseid);if(this.intersect.order){query.intersectfn=this.intersect.compileOrder(query)}else{query.orderfn=null}}if(this.into){if(this.into instanceof yy.Table){if(alasql.options.autocommit&&alasql.databases[this.into.databaseid||databaseid].engineid){query.intoallfns='return alasql.engines["'+alasql.databases[this.into.databaseid||databaseid].engineid+'"]'+'.intoTable("'+(this.into.databaseid||databaseid)+'","'+this.into.tableid+'",this.data, columns, cb);'}else{query.intofns="alasql.databases['"+(this.into.databaseid||databaseid)+"'].tables"+"['"+this.into.tableid+"'].data.push(r);"}}else if(this.into instanceof yy.VarValue){query.intoallfns='alasql.vars["'+this.into.variable+'"]=this.data;res=this.data.length;if(cb)res=cb(res);return res;'}else if(this.into instanceof yy.FuncValue){var qs="return alasql.into['"+this.into.funcid.toUpperCase()+"'](";if(this.into.args&&this.into.args.length>0){qs+=this.into.args[0].toJavaScript()+",";if(this.into.args.length>1){qs+=this.into.args[1].toJavaScript()+","}else{qs+="undefined,"}}else{qs+="undefined, undefined,"}query.intoallfns=qs+"this.data,columns,cb)"}else if(this.into instanceof yy.ParamValue){query.intofns="params['"+this.into.param+"'].push(r)"}if(query.intofns){query.intofn=new Function("r,i,params,alasql","var y;"+query.intofns)}else if(query.intoallfns){query.intoallfn=new Function("columns,cb,params,alasql","var y;"+query.intoallfns)}}var statement=function(params,cb,oldscope){query.params=params;var res1=queryfn(query,oldscope,function(res){if(query.rownums.length>0){for(var i=0,ilen=res.length;i<ilen;i++){for(var j=0,jlen=query.rownums.length;j<jlen;j++){res[i][query.rownums[j]]=i+1}}}var res2=modify(query,res);if(cb)cb(res2);return res2});return res1};statement.query=query;return statement};function modify(query,res){if(typeof res=="undefined"||typeof res=="number"||typeof res=="string"||typeof res=="boolean"){return res}var modifier=query.modifier||alasql.options.modifier;var columns=query.columns;if(typeof columns=="undefined"||columns.length==0){if(res.length>0){var allcol={};for(var i=0;i<Math.min(res.length,alasql.options.columnlookup||10);i++){for(var key in res[i]){allcol[key]=true}}columns=Object.keys(allcol).map(function(columnid){return{columnid:columnid}})}else{columns=[]}}if(modifier=="VALUE"){if(res.length>0){var key;if(columns&&columns.length>0)key=columns[0].columnid;else key=Object.keys(res[0])[0];res=res[0][key]}else{res=undefined}}if(modifier=="ROW"){if(res.length>0){var key;var a=[];for(var key in res[0]){a.push(res[0][key])}res=a}else{res=undefined}}if(modifier=="COLUMN"){var ar=[];if(res.length>0){var key;if(columns&&columns.length>0)key=columns[0].columnid;else key=Object.keys(res[0])[0];for(var i=0,ilen=res.length;i<ilen;i++){ar.push(res[i][key])}}res=ar}if(modifier=="MATRIX"){var ar=[];for(var i=0;i<res.length;i++){var a=[];var r=res[i];for(var j=0;j<columns.length;j++){a.push(r[columns[j].columnid])}ar.push(a)}res=ar}if(modifier=="INDEX"){var ar={};var key,val;if(columns&&columns.length>0){key=columns[0].columnid;val=columns[1].columnid}else{var okeys=Object.keys(res[0]);key=okeys[0];val=okeys[1]}for(var i=0,ilen=res.length;i<ilen;i++){ar[res[i][key]]=res[i][val]}res=ar}if(modifier=="RECORDSET"){res=new alasql.Recordset({data:res,columns:columns})}if(modifier=="TEXTSTRING"){var key;if(columns&&columns.length>0)key=columns[0].columnid;else key=Object.keys(res[0])[0];var s="";for(var i=0,ilen=res.length;i<ilen;i++){res[i]=res[i][key]}res=res.join("\n")}return res}yy.Select.prototype.execute=function(databaseid,params,cb){return this.compile(databaseid)(params,cb)};yy.ExistsValue=function(params){return yy.extend(this,params)};yy.ExistsValue.prototype.toString=function(){return"EXISTS("+this.value.toString()+")"};yy.ExistsValue.prototype.toType=function(){return"boolean"};yy.ExistsValue.prototype.toJavaScript=function(context,tableid,defcols){return"this.existsfn["+this.existsidx+"](params,null,"+context+").data.length"};yy.Select.prototype.compileWhereExists=function(query){if(!this.exists)return;query.existsfn=this.exists.map(function(ex){var nq=ex.compile(query.database.databaseid);nq.query.modifier="RECORDSET";return nq})};yy.Select.prototype.compileQueries=function(query){if(!this.queries)return;query.queriesfn=this.queries.map(function(q){var nq=q.compile(query.database.databaseid);nq.query.modifier="RECORDSET";return nq})};alasql.precompile=function(statement,databaseid,params){if(!statement)return;statement.params=params;if(statement.queries){statement.queriesfn=statement.queries.map(function(q){var nq=q.compile(databaseid||statement.database.databaseid);nq.query.modifier="RECORDSET";return nq})}if(statement.exists){statement.existsfn=statement.exists.map(function(ex){var nq=ex.compile(databaseid||statement.database.databaseid);nq.query.modifier="RECORDSET";return nq})}};yy.Select.prototype.compileFrom=function(query){var self=this;query.sources=[];query.aliases={};if(!self.from)return;self.from.forEach(function(tq){var alias=tq.as||tq.tableid;if(tq instanceof yy.Table){query.aliases[alias]={tableid:tq.tableid,databaseid:tq.databaseid||query.database.databaseid,type:"table"}}else if(tq instanceof yy.Select){query.aliases[alias]={type:"subquery"}}else if(tq instanceof yy.Search){query.aliases[alias]={type:"subsearch"}}else if(tq instanceof yy.ParamValue){query.aliases[alias]={type:"paramvalue"}}else if(tq instanceof yy.FuncValue){query.aliases[alias]={type:"funcvalue"}}else if(tq instanceof yy.VarValue){query.aliases[alias]={type:"varvalue"}}else if(tq instanceof yy.FromData){query.aliases[alias]={type:"fromdata"}}else if(tq instanceof yy.Json){query.aliases[alias]={type:"json"}}else{throw new Error("Wrong table at FROM")}var source={alias:alias,databaseid:tq.databaseid||query.database.databaseid,tableid:tq.tableid,joinmode:"INNER",onmiddlefn:returnTrue,srcwherefns:"",srcwherefn:returnTrue};if(tq instanceof yy.Table){source.columns=alasql.databases[source.databaseid].tables[source.tableid].columns;if(alasql.options.autocommit&&alasql.databases[source.databaseid].engineid){source.datafn=function(query,params,cb,idx,alasql){return alasql.engines[alasql.databases[source.databaseid].engineid].fromTable(source.databaseid,source.tableid,cb,idx,query)}}else if(alasql.databases[source.databaseid].tables[source.tableid].view){source.datafn=function(query,params,cb,idx,alasql){var res=alasql.databases[source.databaseid].tables[source.tableid].select(params);if(cb)res=cb(res,idx,query);return res}}else{source.datafn=function(query,params,cb,idx,alasql){var res=alasql.databases[source.databaseid].tables[source.tableid].data;if(cb)res=cb(res,idx,query);return res}}}else if(tq instanceof yy.Select){source.subquery=tq.compile(query.database.databaseid);if(typeof source.subquery.query.modifier=="undefined"){source.subquery.query.modifier="RECORDSET"}source.columns=source.subquery.query.columns;source.datafn=function(query,params,cb,idx,alasql){var res;source.subquery(query.params,function(data){res=data.data;if(cb)res=cb(res,idx,query);return res});return res}}else if(tq instanceof yy.Search){source.subsearch=tq;source.columns=[];source.datafn=function(query,params,cb,idx,alasql){var res;source.subsearch.execute(query.database.databaseid,query.params,function(data){res=data;if(cb)res=cb(res,idx,query);return res});return res}}else if(tq instanceof yy.ParamValue){var ps="var res = alasql.prepareFromData(params['"+tq.param+"']";if(tq.array)ps+=",true";ps+=");if(cb)res=cb(res,idx,query);return res";source.datafn=new Function("query,params,cb,idx,alasql",ps)}else if(tq instanceof yy.Json){var ps="var res = alasql.prepareFromData("+tq.toJavaScript();if(tq.array)ps+=",true";ps+=");if(cb)res=cb(res,idx,query);return res";source.datafn=new Function("query,params,cb,idx,alasql",ps)}else if(tq instanceof yy.VarValue){var ps="var res = alasql.prepareFromData(alasql.vars['"+tq.variable+"']";if(tq.array)ps+=",true";ps+=");if(cb)res=cb(res,idx,query);return res";source.datafn=new Function("query,params,cb,idx,alasql",ps)}else if(tq instanceof yy.FuncValue){var s="var res=alasql.from['"+tq.funcid.toUpperCase()+"'](";if(tq.args&&tq.args.length>0){if(tq.args[0]){s+=tq.args[0].toJavaScript("query.oldscope")+","}else{s+="null,"}if(tq.args[1]){s+=tq.args[1].toJavaScript("query.oldscope")+","}else{s+="null,"}}else{s+="null,null,"}s+="cb,idx,query";s+=");/*if(cb)res=cb(res,idx,query);*/return res";source.datafn=new Function("query, params, cb, idx, alasql",s)}else if(tq instanceof yy.FromData){source.datafn=function(query,params,cb,idx,alasql){var res=tq.data;if(cb)res=cb(res,idx,query);return res}}else{throw new Error("Wrong table at FROM")}query.sources.push(source)});query.defaultTableid=query.sources[0].alias};alasql.prepareFromData=function(data,array){var res=data;if(typeof data=="string"){res=data.split(/\r?\n/);if(array){for(var i=0,ilen=res.length;i<ilen;i++){res[i]=[res[i]]}}}else if(array){res=[];for(var i=0,ilen=data.length;i<ilen;i++){res.push([data[i]])}}else if(typeof data=="object"&&!(data instanceof Array)){if(typeof Mongo!="undefined"&&typeof Mongo.Collection!="undefined"&&data instanceof Mongo.Collection){res=data.find().fetch()}else{res=[];for(var key in data){if(data.hasOwnProperty(key))res.push([key,data[key]])}}}return res};yy.Select.prototype.compileJoins=function(query){var self=this;this.joins.forEach(function(jn){if(jn.joinmode=="CROSS"){if(jn.using||jn.on){throw new Error("CROSS JOIN cannot have USING or ON clauses")}else{jn.joinmode=="INNER"}}var source;var tq;if(jn instanceof yy.Apply){source={alias:jn.as,applymode:jn.applymode,onmiddlefn:returnTrue,srcwherefns:"",srcwherefn:returnTrue,columns:[]};source.applyselect=jn.select.compile(query.database.databaseid);source.columns=source.applyselect.query.columns;source.datafn=function(query,params,cb,idx,alasql){var res;if(cb)res=cb(res,idx,query);return res};query.sources.push(source)}else{if(jn.table){tq=jn.table;source={alias:jn.as||tq.tableid,databaseid:tq.databaseid||query.database.databaseid,tableid:tq.tableid,joinmode:jn.joinmode,onmiddlefn:returnTrue,srcwherefns:"",srcwherefn:returnTrue,columns:[]};if(!alasql.databases[source.databaseid].tables[source.tableid]){throw new Error("Table '"+source.tableid+"' is not exists in database '"+source.databaseid)+"'"}source.columns=alasql.databases[source.databaseid].tables[source.tableid].columns;if(alasql.options.autocommit&&alasql.databases[source.databaseid].engineid){source.datafn=function(query,params,cb,idx,alasql){return alasql.engines[alasql.databases[source.databaseid].engineid].fromTable(source.databaseid,source.tableid,cb,idx,query)}}else if(alasql.databases[source.databaseid].tables[source.tableid].view){source.datafn=function(query,params,cb,idx,alasql){var res=alasql.databases[source.databaseid].tables[source.tableid].select(params);if(cb)res=cb(res,idx,query);return res}}else{source.datafn=function(query,params,cb,idx,alasql){var res=alasql.databases[source.databaseid].tables[source.tableid].data;if(cb)res=cb(res,idx,query);return res}}query.aliases[source.alias]={tableid:tq.tableid,databaseid:tq.databaseid||query.database.databaseid}}else if(jn.select){var tq=jn.select;source={alias:jn.as,joinmode:jn.joinmode,onmiddlefn:returnTrue,srcwherefns:"",srcwherefn:returnTrue,columns:[]};source.subquery=tq.compile(query.database.databaseid);if(typeof source.subquery.query.modifier=="undefined"){source.subquery.query.modifier="RECORDSET"}source.columns=source.subquery.query.columns;source.datafn=function(query,params,cb,idx,alasql){return source.subquery(query.params,null,cb,idx).data};query.aliases[source.alias]={type:"subquery"}}else if(jn.param){source={alias:jn.as,joinmode:jn.joinmode,onmiddlefn:returnTrue,srcwherefns:"",srcwherefn:returnTrue};var jnparam=jn.param.param;var ps="var res=alasql.prepareFromData(params['"+jnparam+"']";if(jn.array)ps+=",true";ps+=");if(cb)res=cb(res, idx, query);return res";source.datafn=new Function("query,params,cb,idx, alasql",ps);query.aliases[source.alias]={type:"paramvalue"}}else if(jn.variable){source={alias:jn.as,joinmode:jn.joinmode,onmiddlefn:returnTrue,srcwherefns:"",srcwherefn:returnTrue};var ps="var res=alasql.prepareFromData(alasql.vars['"+jn.variable+"']";if(jn.array)ps+=",true";ps+=");if(cb)res=cb(res, idx, query);return res";source.datafn=new Function("query,params,cb,idx, alasql",ps);query.aliases[source.alias]={type:"varvalue"}}else if(jn.funcid){source={alias:jn.as,joinmode:jn.joinmode,onmiddlefn:returnTrue,srcwherefns:"",srcwherefn:returnTrue};var s="var res=alasql.from['"+js.funcid.toUpperCase()+"'](";if(jn.args&&jn.args.length>0){if(jn.args[0]){s+=jn.args[0].toJavaScript("query.oldscope")+","}else{s+="null,"}if(jn.args[1]){s+=jn.args[1].toJavaScript("query.oldscope")+","}else{s+="null,"}}else{s+="null,null,"}s+="cb,idx,query";s+=");/*if(cb)res=cb(res,idx,query);*/return res";source.datafn=new Function("query, params, cb, idx, alasql",s);query.aliases[source.alias]={type:"funcvalue"}}var alias=source.alias;if(jn.natural){if(jn.using||jn.on){throw new Error("NATURAL JOIN cannot have USING or ON clauses")}else{if(query.sources.length>0){var prevSource=query.sources[query.sources.length-1];var prevTable=alasql.databases[prevSource.databaseid].tables[prevSource.tableid];var table=alasql.databases[source.databaseid].tables[source.tableid];if(prevTable&&table){var c1=prevTable.columns.map(function(col){return col.columnid});var c2=table.columns.map(function(col){return col.columnid});jn.using=arrayIntersect(c1,c2).map(function(colid){return{columnid:colid}})}else{throw new Error("In this version of Alasql NATURAL JOIN "+"works for tables with predefined columns only")}}}}if(jn.using){var prevSource=query.sources[query.sources.length-1];source.onleftfns=jn.using.map(function(col){return"p['"+(prevSource.alias||prevSource.tableid)+"']['"+col.columnid+"']"}).join('+"`"+');source.onleftfn=new Function("p,params,alasql","var y;return "+source.onleftfns);source.onrightfns=jn.using.map(function(col){return"p['"+(source.alias||source.tableid)+"']['"+col.columnid+"']"}).join('+"`"+');source.onrightfn=new Function("p,params,alasql","var y;return "+source.onrightfns);source.optimization="ix"}else if(jn.on){if(jn.on instanceof yy.Op&&jn.on.op=="="&&!jn.on.allsome){source.optimization="ix";var lefts="";var rights="";var middles="";var middlef=false;var ls=jn.on.left.toJavaScript("p",query.defaultTableid,query.defcols);var rs=jn.on.right.toJavaScript("p",query.defaultTableid,query.defcols);if(ls.indexOf("p['"+alias+"']")>-1&&!(rs.indexOf("p['"+alias+"']")>-1)){if((ls.match(/p\[\'.*?\'\]/g)||[]).every(function(s){return s=="p['"+alias+"']"})){rights=ls}else{middlef=true}}else if(!(ls.indexOf("p['"+alias+"']")>-1)&&rs.indexOf("p['"+alias+"']")>-1){if((rs.match(/p\[\'.*?\'\]/g)||[]).every(function(s){return s=="p['"+alias+"']"})){lefts=ls}else{middlef=true}}else{middlef=true}if(rs.indexOf("p['"+alias+"']")>-1&&!(ls.indexOf("p['"+alias+"']")>-1)){if((rs.match(/p\[\'.*?\'\]/g)||[]).every(function(s){return s=="p['"+alias+"']"})){rights=rs}else{middlef=true}}else if(!(rs.indexOf("p['"+alias+"']")>-1)&&ls.indexOf("p['"+alias+"']")>-1){if((ls.match(/p\[\'.*?\'\]/g)||[]).every(function(s){return s=="p['"+alias+"']"})){lefts=rs}else{middlef=true}}else{middlef=true}if(middlef){rights="";lefts="";middles=jn.on.toJavaScript("p",query.defaultTableid,query.defcols);source.optimization="no"}source.onleftfns=lefts;source.onrightfns=rights;source.onmiddlefns=middles||"true";source.onleftfn=new Function("p,params,alasql","var y;return "+source.onleftfns);source.onrightfn=new Function("p,params,alasql","var y;return "+source.onrightfns);source.onmiddlefn=new Function("p,params,alasql","var y;return "+source.onmiddlefns)}else{source.optimization="no";source.onmiddlefns=jn.on.toJavaScript("p",query.defaultTableid,query.defcols);source.onmiddlefn=new Function("p,params,alasql","var y;return "+jn.on.toJavaScript("p",query.defaultTableid,query.defcols))}}query.sources.push(source)}})};yy.Select.prototype.compileWhere=function(query){if(this.where){if(typeof this.where=="function"){return this.where}else{s=this.where.toJavaScript("p",query.defaultTableid,query.defcols);query.wherefns=s;return new Function("p,params,alasql","var y;return "+s)}}else return function(){return true}};yy.Select.prototype.compileWhereJoins=function(query){return;optimizeWhereJoin(query,this.where.expression);query.sources.forEach(function(source){if(source.srcwherefns){source.srcwherefn=new Function("p,params,alasql","var y;return "+source.srcwherefns)}if(source.wxleftfns){source.wxleftfn=new Function("p,params,alasql","var y;return "+source.wxleftfns)}if(source.wxrightfns){source.wxrightfn=new Function("p,params,alasql","var y;return "+source.wxrightfns)}})};function optimizeWhereJoin(query,ast){if(!ast)return false;if(!(ast instanceof yy.Op))return;if(ast.op!="="&&ast.op!="AND")return;if(ast.allsome)return;var s=ast.toJavaScript("p",query.defaultTableid,query.defcols);var fsrc=[];query.sources.forEach(function(source,idx){if(source.tableid){if(s.indexOf("p['"+source.alias+"']")>-1)fsrc.push(source)}});if(fsrc.length==0){return}else if(fsrc.length==1){if(!(s.match(/p\[\'.*?\'\]/g)||[]).every(function(s){return s=="p['"+fsrc[0].alias+"']"})){return}var src=fsrc[0];src.srcwherefns=src.srcwherefns?src.srcwherefns+"&&"+s:s;if(ast instanceof yy.Op&&(ast.op=="="&&!ast.allsome)){if(ast.left instanceof yy.Column){var ls=ast.left.toJavaScript("p",query.defaultTableid,query.defcols);var rs=ast.right.toJavaScript("p",query.defaultTableid,query.defcols);if(rs.indexOf("p['"+fsrc[0].alias+"']")==-1){fsrc[0].wxleftfns=ls;fsrc[0].wxrightfns=rs}}if(ast.right instanceof yy.Column){var ls=ast.left.toJavaScript("p",query.defaultTableid,query.defcols);var rs=ast.right.toJavaScript("p",query.defaultTableid,query.defcols);if(ls.indexOf("p['"+fsrc[0].alias+"']")==-1){fsrc[0].wxleftfns=rs;fsrc[0].wxrightfns=ls}}}ast.reduced=true;return}else{if(ast.op="AND"){optimizeWhereJoin(query,ast.left);optimizeWhereJoin(query,ast.right)}}}yy.Select.prototype.compileGroup=function(query){var self=this;if(query.sources.length>0){var tableid=query.sources[0].alias}else{var tableid=""}var defcols=query.defcols;var allgroup=[[]];if(this.group){allgroup=decartes(this.group,query)}var allgroups=[];allgroup.forEach(function(a){allgroups=arrayUnion(allgroups,a)});query.allgroups=allgroups;if(false){allgroups.forEach(function(col2){if(query.selectColumns[colid]){}else{var tmpid="default";if(query.sources.length>0)tmpid=query.sources[0].alias;if(Object.keys(query.selectColumns).length!=0)query.removeKeys.push(colid);query.selectfns+="r['"+escapeq(colid)+"']="+new yy.Column({columnid:colid}).toJavaScript("p",tmpid)+";"}})}var s="";allgroup.forEach(function(agroup){s+="var acc,g=this.xgroups[";var rg=agroup.map(function(col2){var columnid=col2.split("	")[0];var coljs=col2.split("	")[1];if(columnid=="")return"1";else return coljs});if(rg.length==0)rg=["''"];s+=rg.join('+"`"+');s+="];if(!g) {this.groups.push((g=this.xgroups[";s+=rg.join('+"`"+');s+="] = {";s+=agroup.map(function(col2){var columnid=col2.split("	")[0];var coljs=col2.split("	")[1];if(columnid=="")return"";else return"'"+columnid+"':"+coljs+","}).join("");var neggroup=arrayDiff(allgroups,agroup);s+=neggroup.map(function(col2){var columnid=col2.split("	")[0];var coljs=col2.split("	")[1];return"'"+columnid+"':null,"}).join("");var aft="";s+=query.selectGroup.map(function(col,idx){var colexp=col.expression.toJavaScript("p",tableid,defcols);var colas=col.nick;if(col instanceof yy.AggrValue){if(col.distinct){aft+=",g['$$_VALUES_"+colas+"']={},g['$$_VALUES_"+colas+"']["+colexp+"]=true"}if(col.aggregatorid=="SUM"){return"'"+colas+"':("+colexp+")||0,"}else if(col.aggregatorid=="MIN"||col.aggregatorid=="MAX"||col.aggregatorid=="FIRST"||col.aggregatorid=="LAST"){return"'"+colas+"':"+colexp+","}else if(col.aggregatorid=="ARRAY"){return"'"+colas+"':["+colexp+"],"}else if(col.aggregatorid=="COUNT"){if(col.expression.columnid=="*"){return"'"+colas+"':1,"}else{return"'"+colas+"':(typeof "+colexp+' != "undefined")?1:0,'}}else if(col.aggregatorid=="AVG"){query.removeKeys.push("_SUM_"+colas);query.removeKeys.push("_COUNT_"+colas);return"'"+colas+"':"+colexp+",'_SUM_"+colas+"':("+colexp+")||0,'_COUNT_"+colas+"':(typeof "+colexp+' != "undefined")?1:0,'}else if(col.aggregatorid=="AGGR"){aft+=",g['"+colas+"']="+col.expression.toJavaScript("g",-1);return""}else if(col.aggregatorid=="REDUCE"){query.removeKeys.push("_REDUCE_"+colas);return"'"+colas+"':alasql.aggr['"+col.funcid+"']("+colexp+",undefined,(acc={})),"+"'__REDUCE__"+colas+"':acc,"}return""}else return""}).join("");s+="}"+aft+",g));} else {";s+=query.selectGroup.map(function(col,idx){var colas=col.nick;var colexp=col.expression.toJavaScript("p",tableid,defcols);if(col instanceof yy.AggrValue){if(col.distinct){var pre="if(typeof "+colexp+'!="undefined" && (!g[\'$$_VALUES_'+colas+"']["+colexp+"])) 				 		 {";var post="g['$$_VALUES_"+colas+"']["+colexp+"]=true;}"}else{var pre="",post=""}if(col.aggregatorid=="SUM"){return pre+"g['"+colas+"']+=("+colexp+"||0);"+post}else if(col.aggregatorid=="COUNT"){if(col.expression.columnid=="*")return pre+"g['"+colas+"']++;"+post;else{return pre+"if(typeof "+colexp+'!="undefined") g[\''+colas+"']++;"+post}}else if(col.aggregatorid=="ARRAY"){return pre+"g['"+colas+"'].push("+colexp+");"+post}else if(col.aggregatorid=="MIN"){return pre+"g['"+colas+"']=Math.min(g['"+colas+"'],"+colexp+");"+post}else if(col.aggregatorid=="MAX"){return pre+"g['"+colas+"']=Math.max(g['"+colas+"'],"+colexp+");"+post}else if(col.aggregatorid=="FIRST"){return""}else if(col.aggregatorid=="LAST"){return pre+"g['"+colas+"']="+colexp+";"+post}else if(col.aggregatorid=="AVG"){return pre+"g['_SUM_"+colas+"']+=(y="+colexp+")||0;"+"g['_COUNT_"+colas+'\']+=(typeof y!="undefined")?1:0;'+"g['"+colas+"']=g['_SUM_"+colas+"']/g['_COUNT_"+colas+"'];"+post}else if(col.aggregatorid=="AGGR"){return pre+"g['"+colas+"']="+col.expression.toJavaScript("g",-1)+";"+post}else if(col.aggregatorid=="REDUCE"){return pre+"g['"+colas+"']=alasql.aggr."+col.funcid+"("+colexp+",g['"+colas+"'],g['__REDUCE__"+colas+"']);"+post}return""}else return""}).join("");s+="}"});return new Function("p,params,alasql",s)};function compileSelectStar(query,alias){var s="",sp="",ss=[];query.ixsources={};query.sources.forEach(function(source){query.ixsources[source.alias]=source});var columns;if(query.ixsources[alias]){var columns=query.ixsources[alias].columns}if(columns&&columns.length>0){columns.forEach(function(tcol){ss.push("'"+tcol.columnid+"':p['"+alias+"']['"+tcol.columnid+"']");query.selectColumns[escapeq(tcol.columnid)]=true;var coldef={columnid:tcol.columnid,dbtypeid:tcol.dbtypeid,dbsize:tcol.dbsize,dbprecision:tcol.dbprecision,dbenum:tcol.dbenum};query.columns.push(coldef);query.xcolumns[coldef.columnid]=coldef})}else{sp+='var w=p["'+alias+'"];for(var k in w){r[k]=w[k]};';query.dirtyColumns=true}return{s:ss.join(","),sp:sp}}yy.Select.prototype.compileSelect1=function(query){var self=this;query.columns=[];query.xcolumns={};query.selectColumns={};query.dirtyColumns=false;var s="var r={";var sp="";var ss=[];this.columns.forEach(function(col){if(col instanceof yy.Column){if(col.columnid=="*"){if(col.func){sp+="r=params['"+col.param+"'](p['"+query.sources[0].alias+"'],p,params,alasql);"}else if(col.tableid){var ret=compileSelectStar(query,col.tableid);if(ret.s)ss=ss.concat(ret.s);sp+=ret.sp}else{for(var alias in query.aliases){var ret=compileSelectStar(query,alias);if(ret.s)ss=ss.concat(ret.s);sp+=ret.sp}}}else{var tbid=col.tableid;var dbid=col.databaseid||query.sources[0].databaseid||query.database.databaseid;if(!tbid)tbid=query.defcols[col.columnid];if(!tbid)tbid=query.defaultTableid;if(col.columnid!="_"){ss.push("'"+escapeq(col.as||col.columnid)+"':p['"+tbid+"']['"+col.columnid+"']")
-}else{ss.push("'"+escapeq(col.as||col.columnid)+"':p['"+tbid+"']")}query.selectColumns[escapeq(col.as||col.columnid)]=true;if(query.aliases[tbid]&&query.aliases[tbid].type=="table"){if(!alasql.databases[dbid].tables[query.aliases[tbid].tableid]){throw new Error("Table '"+tbid+"' does not exists in database")}var columns=alasql.databases[dbid].tables[query.aliases[tbid].tableid].columns;var xcolumns=alasql.databases[dbid].tables[query.aliases[tbid].tableid].xcolumns;if(xcolumns&&columns.length>0){var tcol=xcolumns[col.columnid];var coldef={columnid:col.as||col.columnid,dbtypeid:tcol.dbtypeid,dbsize:tcol.dbsize,dbpecision:tcol.dbprecision,dbenum:tcol.dbenum};query.columns.push(coldef);query.xcolumns[coldef.columnid]=coldef}else{var coldef={columnid:col.as||col.columnid};query.columns.push(coldef);query.xcolumns[coldef.columnid]=coldef;query.dirtyColumns=true}}else{var coldef={columnid:col.as||col.columnid};query.columns.push(coldef);query.xcolumns[coldef.columnid]=coldef}}}else if(col instanceof yy.AggrValue){if(!self.group){self.group=[""]}if(!col.as)col.as=escapeq(col.toString());if(col.aggregatorid=="SUM"||col.aggregatorid=="MAX"||col.aggregatorid=="MIN"||col.aggregatorid=="FIRST"||col.aggregatorid=="LAST"||col.aggregatorid=="AVG"||col.aggregatorid=="ARRAY"||col.aggregatorid=="REDUCE"){ss.push("'"+escapeq(col.as)+"':"+n2u(col.expression.toJavaScript("p",query.defaultTableid,query.defcols)))}else if(col.aggregatorid=="COUNT"){ss.push("'"+escapeq(col.as)+"':1")}query.selectColumns[col.aggregatorid+"("+escapeq(col.expression.toString())+")"]=thtd;var coldef={columnid:col.as||col.columnid||col.toString()};query.columns.push(coldef);query.xcolumns[coldef.columnid]=coldef}else{ss.push("'"+escapeq(col.as||col.columnid||col.toString())+"':"+n2u(col.toJavaScript("p",query.defaultTableid,query.defcols)));query.selectColumns[escapeq(col.as||col.columnid||col.toString())]=true;var coldef={columnid:col.as||col.columnid||col.toString()};query.columns.push(coldef);query.xcolumns[coldef.columnid]=coldef}});s+=ss.join(",")+"};"+sp;return s};yy.Select.prototype.compileSelect2=function(query){var s=query.selectfns;return new Function("p,params,alasql","var y;"+s+"return r")};yy.Select.prototype.compileSelectGroup0=function(query){var self=this;self.columns.forEach(function(col,idx){if(col instanceof yy.Column&&col.columnid=="*"){}else{var colas;if(col instanceof yy.Column){colas=escapeq(col.columnid)}else{colas=escapeq(col.toString())}for(var i=0;i<idx;i++){if(colas==self.columns[i].nick){colas=self.columns[i].nick+":"+idx;break}}col.nick=colas;if(col.funcid&&(col.funcid.toUpperCase()=="ROWNUM"||col.funcid.toUpperCase()=="ROW_NUMBER")){query.rownums.push(col.as)}}});this.columns.forEach(function(col){if(col.findAggregator)col.findAggregator(query)});if(this.having){if(this.having.findAggregator)this.having.findAggregator(query)}};yy.Select.prototype.compileSelectGroup1=function(query){var self=this;var s="var r = {};";self.columns.forEach(function(col,idx){if(col instanceof yy.Column&&col.columnid=="*"){s+="for(var k in this.query.groupColumns){r[k]=g[this.query.groupColumns[k]]};"}else{var colas=col.as;if(typeof colas=="undefined"){if(col instanceof yy.Column)colas=escapeq(col.columnid);else colas=col.nick}query.groupColumns[colas]=col.nick;s+="r['"+colas+"']=";s+=n2u(col.toJavaScript("g",""))+";";for(var i=0;i<query.removeKeys.length;i++){if(query.removeKeys[i]==colas){query.removeKeys.splice(i,1);break}}}});return s};yy.Select.prototype.compileSelectGroup2=function(query){var s=query.selectgfns;return new Function("g,params,alasql","var y;"+s+"return r")};yy.Select.prototype.compileRemoveColumns=function(query){if(typeof this.removecolumns!="undefined"){query.removeKeys=query.removeKeys.concat(this.removecolumns.filter(function(column){return typeof column.like=="undefined"}).map(function(column){return column.columnid}));query.removeLikeKeys=this.removecolumns.filter(function(column){return typeof column.like!="undefined"}).map(function(column){return new RegExp(column.like.value.replace(/\%/g,".*"),"g")})}};yy.Select.prototype.compileHaving=function(query){if(this.having){s=this.having.toJavaScript("g",-1);query.havingfns=s;return new Function("g,params,alasql","var y;return "+s)}else return function(){return true}};yy.Select.prototype.compileOrder=function(query){var self=this;if(this.order){if(this.order&&this.order.length==1&&this.order[0].expression&&typeof this.order[0].expression=="function"){var func=this.order[0].expression;return function(a,b){var ra=func(a),rb=func(b);if(ra>rb)return 1;if(ra==rb)return 0;return-1}}var s="";var sk="";this.order.forEach(function(ord,idx){var dg="";if(ord.expression instanceof yy.NumValue){ord.expression=self.columns[ord.expression.value-1];ord.expression=new yy.Column({columnid:ord.expression.nick})}if(ord.expression instanceof yy.Column){var columnid=ord.expression.columnid;if(query.xcolumns[columnid]){var dbtypeid=query.xcolumns[columnid].dbtypeid;if(dbtypeid=="DATE"||dbtypeid=="DATETIME")dg=".valueOf()"}else{if(alasql.options.valueof)dg=".valueOf()"}if(ord.nocase)dg+=".toUpperCase()";s+="if((a['"+columnid+"']||'')"+dg+(ord.direction=="ASC"?">":"<")+"(b['"+columnid+"']||'')"+dg+")return 1;";s+="if((a['"+columnid+"']||'')"+dg+"==(b['"+columnid+"']||'')"+dg+"){"}else{dg=".valueOf()";if(ord.nocase)dg+=".toUpperCase()";s+="if(("+ord.toJavaScript("a","")+"||'')"+dg+(ord.direction=="ASC"?">(":"<(")+ord.toJavaScript("b","")+"||'')"+dg+")return 1;";s+="if(("+ord.toJavaScript("a","")+"||'')"+dg+"==("+ord.toJavaScript("b","")+"||'')"+dg+"){"}sk+="}"});s+="return 0;";s+=sk+"return -1";query.orderfns=s;return new Function("a,b","var y;"+s)}};var rollup=function(a,query){var rr=[];var mask=0;var glen=a.length;for(var g=0;g<glen+1;g++){var ss=[];for(var i=0;i<glen;i++){if(a[i]instanceof yy.Column){a[i].nick=escapeq(a[i].columnid);query.groupColumns[escapeq(a[i].columnid)]=a[i].nick;var aaa=a[i].nick+"	"+a[i].toJavaScript("p",query.sources[0].alias,query.defcols)}else{query.groupColumns[escapeq(a[i].toString())]=escapeq(a[i].toString());var aaa=escapeq(a[i].toString())+"	"+a[i].toJavaScript("p",query.sources[0].alias,query.defcols)}if(mask&1<<i)ss.push(aaa)}rr.push(ss);mask=(mask<<1)+1}return rr};var cube=function(a,query){var rr=[];var glen=a.length;for(var g=0;g<1<<glen;g++){var ss=[];for(var i=0;i<glen;i++){if(g&1<<i)ss=ss.concat(decartes(a[i],query))}rr.push(ss)}return rr};var groupingsets=function(a,query){return a.reduce(function(acc,d){acc=acc.concat(decartes(d,query));return acc},[])};var cartes=function(a1,a2){var rrr=[];for(var i1=0;i1<a1.length;i1++){for(var i2=0;i2<a2.length;i2++){rrr.push(a1[i1].concat(a2[i2]))}}return rrr};function decartes(gv,query){if(gv instanceof Array){var res=[[]];for(var t=0;t<gv.length;t++){if(gv[t]instanceof yy.Column){gv[t].nick=escapeq(gv[t].columnid);query.groupColumns[gv[t].nick]=gv[t].nick;res=res.map(function(r){return r.concat(gv[t].nick+"	"+gv[t].toJavaScript("p",query.sources[0].alias,query.defcols))})}else if(gv[t]instanceof yy.FuncValue){query.groupColumns[escapeq(gv[t].toString())]=escapeq(gv[t].toString());res=res.map(function(r){return r.concat(escapeq(gv[t].toString())+"	"+gv[t].toJavaScript("p",query.sources[0].alias,query.defcols))})}else if(gv[t]instanceof yy.GroupExpression){if(gv[t].type=="ROLLUP")res=cartes(res,rollup(gv[t].group,query));else if(gv[t].type=="CUBE")res=cartes(res,cube(gv[t].group,query));else if(gv[t].type=="GROUPING SETS")res=cartes(res,groupingsets(gv[t].group,query));else throw new Error("Unknown grouping function")}else if(gv[t]===""){res=[["1	1"]]}else{res=res.map(function(r){query.groupColumns[escapeq(gv[t].toString())]=escapeq(gv[t].toString());return r.concat(escapeq(gv[t].toString())+"	"+gv[t].toJavaScript("p",query.sources[0].alias,query.defcols))})}}return res}else if(gv instanceof yy.FuncValue){query.groupColumns[escapeq(gv.toString())]=escapeq(gv.toString());return[gv.toString()+"	"+gv.toJavaScript("p",query.sources[0].alias,query.defcols)]}else if(gv instanceof yy.Column){gv.nick=escapeq(gv.columnid);query.groupColumns[gv.nick]=gv.nick;return[gv.nick+"	"+gv.toJavaScript("p",query.sources[0].alias,query.defcols)]}else{query.groupColumns[escapeq(gv.toString())]=escapeq(gv.toString());return[escapeq(gv.toString())+"	"+gv.toJavaScript("p",query.sources[0].alias,query.defcols)]}}yy.Select.prototype.compileDefCols=function(query,databaseid){var defcols={};if(this.from){this.from.forEach(function(fr){if(fr instanceof yy.Table){var alias=fr.as||fr.tableid;var table=alasql.databases[fr.databaseid||databaseid].tables[fr.tableid];if(table.columns){table.columns.forEach(function(col){if(defcols[col.columnid]){defcols[col.columnid]="-"}else{defcols[col.columnid]=alias}})}}else if(fr instanceof yy.Select){}else if(fr instanceof yy.Search){}else if(fr instanceof yy.ParamValue){}else if(fr instanceof yy.VarValue){}else if(fr instanceof yy.FuncValue){}else if(fr instanceof yy.FromData){}else if(fr instanceof yy.Json){}else{throw new Error("Unknown type of FROM clause")}})}if(this.joins){this.joins.forEach(function(jn){if(jn.table){var alias=jn.table.tableid;if(jn.as)alias=jn.as;var alias=jn.as||jn.table.tableid;var table=alasql.databases[jn.table.databaseid||databaseid].tables[jn.table.tableid];if(table.columns){table.columns.forEach(function(col){if(defcols[col.columnid]){defcols[col.columnid]="-"}else{defcols[col.columnid]=alias}})}}else if(jn.select){}else if(jn.param){}else if(jn.func){}else{throw new Error("Unknown type of FROM clause")}})}return defcols};yy.Union=function(params){return yy.extend(this,params)};yy.Union.prototype.toString=function(){return K("UNION")};yy.Union.prototype.compile=function(tableid){return null};yy.Apply=function(params){return yy.extend(this,params)};yy.Apply.prototype.toString=function(){var s=K(this.applymode)+" "+K("APPLY")+" (";s+=this.select.toString()+")";if(this.as)s+=" "+K("AS")+" "+L(this.as);return s};yy.Over=function(params){return yy.extend(this,params)};yy.Over.prototype.toString=function(){var s=K("OVER")+" (";if(this.partition){s+=K("PARTITION")+" "+K("BY")+" "+this.partition.toString();if(this.order)s+=" "}if(this.order){s+=K("ORDER")+" "+K("BY")+" "+this.order.toString()}s+=")";return s};yy.ExpressionStatement=function(params){return yy.extend(this,params)};yy.ExpressionStatement.prototype.toString=function(){return this.expression.toString()};yy.ExpressionStatement.prototype.execute=function(databaseid,params,cb){if(this.expression){alasql.precompile(this,databaseid,params);var exprfn=new Function("params,alasql,p","var y;return "+this.expression.toJavaScript("({})","",null)).bind(this);var res=exprfn(params,alasql);if(cb)res=cb(res);return res}};yy.Expression=function(params){return yy.extend(this,params)};yy.Expression.prototype.toString=function(){var s=this.expression.toString();if(this.order)s+=" "+this.order.toString();if(this.nocase)s+=" "+K("COLLATE")+" "+K("NOCASE");return s};yy.Expression.prototype.findAggregator=function(query){if(this.expression.findAggregator)this.expression.findAggregator(query)};yy.Expression.prototype.toJavaScript=function(context,tableid,defcols){if(this.expression.reduced)return"true";return this.expression.toJavaScript(context,tableid,defcols)};yy.Expression.prototype.compile=function(context,tableid,defcols){if(this.reduced)return returnTrue();return new Function("p","var y;return "+this.toJavaScript(context,tableid,defcols))};yy.JavaScript=function(params){return yy.extend(this,params)};yy.JavaScript.prototype.toString=function(){var s="``"+this.value+"``";return s};yy.JavaScript.prototype.toJavaScript=function(context,tableid,defcols){return"("+this.value+")"};yy.JavaScript.prototype.execute=function(databaseid,params,cb){var res=1;var expr=new Function("params,alasql,p",this.value);expr(params,alasql);if(cb)res=cb(res);return res};yy.Literal=function(params){return yy.extend(this,params)};yy.Literal.prototype.toString=function(){var s=this.value;if(this.value1)s=this.value1+"."+s;return L(s)};yy.Join=function(params){return yy.extend(this,params)};yy.Join.prototype.toString=function(){var s=NL()+ID();if(this.joinmode)s+=K(this.joinmode)+" ";s+=K("JOIN")+this.table.toString();return s};yy.Table=function(params){return yy.extend(this,params)};yy.Table.prototype.toString=function(){var s=this.tableid;if(this.databaseid)s=this.databaseid+"."+s;return L(s)};yy.View=function(params){return yy.extend(this,params)};yy.View.prototype.toString=function(){var s=this.viewid;if(this.databaseid)s=this.databaseid+"."+s;return L(s)};yy.Op=function(params){return yy.extend(this,params)};yy.Op.prototype.toString=function(){if(this.op=="IN"||this.op=="NOT IN"){return this.left.toString()+" "+P(this.op)+" ("+this.right.toString()+")"}if(this.allsome){return this.left.toString()+" "+P(this.op)+" "+this.allsome+" ("+this.right.toString()+")"}if(this.op=="->"||this.op=="!"){var s=this.left.toString()+this.op;if(typeof this.right!="string"&&typeof this.right!="number")s+="(";s+=this.right.toString();if(typeof this.right!="string"&&typeof this.right!="number")s+=")";return s}return this.left.toString()+" "+P(this.op)+" "+(this.allsome?this.allsome+" ":"")+this.right.toString()};yy.Op.prototype.findAggregator=function(query){if(this.left&&this.left.findAggregator)this.left.findAggregator(query);if(this.right&&this.right.findAggregator&&!this.allsome){this.right.findAggregator(query)}};yy.Op.prototype.toType=function(tableid){if(["-","*","/","%","^"].indexOf(this.op)>-1)return"number";if(this.op=="+"){if(this.left.toType(tableid)=="string"||this.right.toType(tableid)=="string")return"string";if(this.left.toType(tableid)=="number"||this.right.toType(tableid)=="number")return"number"}if(["AND","OR","NOT","=","==","===","!=","!==","!===",">",">=","<","<=","IN","NOT IN","LIKE","NOT LIKE"].indexOf(this.op)>-1)return"boolean";if(this.op=="BETWEEN"||this.op=="NOT BETWEEN"||this.op=="IS NULL"||this.op=="IS NOT NULL")return"boolean";if(this.allsome)return"boolean";if(!this.op)return this.left.toType();return"unknown"};yy.Op.prototype.toJavaScript=function(context,tableid,defcols){var op=this.op;if(this.op=="=")op="===";else if(this.op=="<>")op="!=";else if(this.op=="OR")op="||";if(this.op=="->"){if(typeof this.right=="string"){return this.left.toJavaScript(context,tableid,defcols)+'["'+this.right+'"]'}else if(typeof this.right=="number"){return this.left.toJavaScript(context,tableid,defcols)+"["+this.right+"]"}else if(this.right instanceof yy.FuncValue){ss=[];if(!this.right.args||this.right.args.length==0){}else{var ss=this.right.args.map(function(arg){return arg.toJavaScript(context,tableid,defcols)})}return this.left.toJavaScript(context,tableid,defcols)+"['"+this.right.funcid+"']("+ss.join(",")+")"}else{return this.left.toJavaScript(context,tableid,defcols)+"["+this.right.toJavaScript(context,tableid,defcols)+"]"}}if(this.op=="!"){if(typeof this.right=="string"){return"alasql.databases[alasql.useid].objects["+this.left.toJavaScript(context,tableid,defcols)+']["'+this.right+'"]'}}if(this.op=="IS"){return"((typeof "+this.left.toJavaScript(context,tableid,defcols)+"=='undefined') == "+"(typeof "+this.right.toJavaScript(context,tableid,defcols)+"=='undefined'))"}if(this.op=="=="){return"alasql.utils.deepEqual("+this.left.toJavaScript(context,tableid,defcols)+","+this.right.toJavaScript(context,tableid,defcols)+")"}if(this.op=="==="){return"(("+this.left.toJavaScript(context,tableid,defcols)+").valueOf()===("+this.right.toJavaScript(context,tableid,defcols)+".valueOf()))"}if(this.op=="!==="){return"!(("+this.left.toJavaScript(context,tableid,defcols)+").valueOf()===("+this.right.toJavaScript(context,tableid,defcols)+".valueOf()))"}if(this.op=="!=="){return"(!alasql.utils.deepEqual("+this.left.toJavaScript(context,tableid,defcols)+","+this.right.toJavaScript(context,tableid,defcols)+"))"}if(this.op=="LIKE"){var s="("+this.left.toJavaScript(context,tableid,defcols)+"+'')"+".toUpperCase().match(new RegExp('^'+("+this.right.toJavaScript(context,tableid,defcols)+").replace(/\\%/g,'.*').toUpperCase()+'$','g'))";return s}if(this.op=="NOT LIKE"){var s="!(("+this.left.toJavaScript(context,tableid,defcols)+"+'')"+".toUpperCase().match(new RegExp('^'+("+this.right.toJavaScript(context,tableid,defcols)+").replace(/\\%/g,'.*').toUpperCase()+'$','g')))";return s}if(this.op=="BETWEEN"){return"(("+this.right1.toJavaScript(context,tableid,defcols)+"<="+this.left.toJavaScript(context,tableid,defcols)+")&&"+"("+this.left.toJavaScript(context,tableid,defcols)+"<="+this.right2.toJavaScript(context,tableid,defcols)+"))"}if(this.op=="NOT BETWEEN"){return"!(("+this.right1.toJavaScript(context,tableid,defcols)+"<="+this.left.toJavaScript(context,tableid,defcols)+")&&"+"("+this.left.toJavaScript(context,tableid,defcols)+"<="+this.right2.toJavaScript(context,tableid,defcols)+"))"}if(this.op=="IN"){if(this.right instanceof yy.Select){var s="(";s+="alasql.utils.flatArray(this.queriesfn["+this.queriesidx+"](params,null,context))";s+=".indexOf(";s+=this.left.toJavaScript(context,tableid,defcols)+")>-1)";return s}else if(this.right instanceof Array){var s="(["+this.right.map(function(a){return a.toJavaScript(context,tableid,defcols)}).join(",")+"].indexOf(";s+=this.left.toJavaScript(context,tableid,defcols)+")>-1)";return s}else{var s="("+this.right.toJavaScript(context,tableid,defcols)+".indexOf(";s+=this.left.toJavaScript(context,tableid,defcols)+")>-1)";return s}}if(this.op=="NOT IN"){if(this.right instanceof yy.Select){var s="(";s+="alasql.utils.flatArray(this.queriesfn["+this.queriesidx+"](params,null,p))";s+=".indexOf(";s+=this.left.toJavaScript(context,tableid,defcols)+")<0)";return s}else if(this.right instanceof Array){var s="(["+this.right.map(function(a){return a.toJavaScript(context,tableid,defcols)}).join(",")+"].indexOf(";s+=this.left.toJavaScript(context,tableid,defcols)+")<0)";return s}else{var s="("+this.right.toJavaScript(context,tableid,defcols)+".indexOf(";s+=this.left.toJavaScript(context,tableid,defcols)+")==-1)";return s}}if(this.allsome=="ALL"){if(this.right instanceof yy.Select){var s="alasql.utils.flatArray(this.query.queriesfn["+this.queriesidx+"](params,null,p))";s+=".every(function(b){return (";s+=this.left.toJavaScript(context,tableid,defcols)+")"+op+"b})";return s}else if(this.right instanceof Array){var s="["+this.right.map(function(a){return a.toJavaScript(context,tableid,defcols)}).join(",")+"].every(function(b){return (";s+=this.left.toJavaScript(context,tableid,defcols)+")"+op+"b})";return s}else{throw new Error("Wrong NOT IN operator without SELECT part")}}if(this.allsome=="SOME"||this.allsome=="ANY"){if(this.right instanceof yy.Select){var s="alasql.utils.flatArray(this.query.queriesfn["+this.queriesidx+"](params,null,p))";s+=".some(function(b){return (";s+=this.left.toJavaScript(context,tableid,defcols)+")"+op+"b})";return s}else if(this.right instanceof Array){var s="["+this.right.map(function(a){return a.toJavaScript(context,tableid,defcols)}).join(",")+"].some(function(b){return (";s+=this.left.toJavaScript(context,tableid,defcols)+")"+op+"b})";return s}else{throw new Error("Wrong NOT IN operator without SELECT part")}}if(this.op=="AND"){if(this.left.reduced){if(this.right.reduced){return"true"}else{return this.right.toJavaScript(context,tableid,defcols)}}else if(this.right.reduced){return this.left.toJavaScript(context,tableid,defcols)}op="&&"}if(this.op=="^"){return"Math.pow("+this.left.toJavaScript(context,tableid,defcols)+","+this.right.toJavaScript(context,tableid,defcols)+")"}return"("+this.left.toJavaScript(context,tableid,defcols)+op+this.right.toJavaScript(context,tableid,defcols)+")"};yy.VarValue=function(params){return yy.extend(this,params)};yy.VarValue.prototype.toString=function(){return"@"+L(this.variable)};yy.VarValue.prototype.toType=function(){return"unknown"};yy.VarValue.prototype.toJavaScript=function(){return"alasql.vars['"+this.variable+"']"};yy.NumValue=function(params){return yy.extend(this,params)};yy.NumValue.prototype.toString=function(){return N(this.value.toString())};yy.NumValue.prototype.toType=function(){return"number"};yy.NumValue.prototype.toJavaScript=function(){return""+this.value};yy.StringValue=function(params){return yy.extend(this,params)};yy.StringValue.prototype.toString=function(){return"'"+S(this.value.toString())+"'"};yy.StringValue.prototype.toType=function(){return"string"};yy.StringValue.prototype.toJavaScript=function(){return"'"+escapeq(this.value)+"'"};yy.LogicValue=function(params){return yy.extend(this,params)};yy.LogicValue.prototype.toString=function(){return this.value?"TRUE":"FALSE"};yy.LogicValue.prototype.toType=function(){return"boolean"};yy.LogicValue.prototype.toJavaScript=function(){return this.value?"true":"false"};yy.NullValue=function(params){return yy.extend(this,params)};yy.NullValue.prototype.toString=function(){return"NULL"};yy.NullValue.prototype.toJavaScript=function(){return"undefined"};yy.ParamValue=function(params){return yy.extend(this,params)};yy.ParamValue.prototype.toString=function(){return"$"+this.param};yy.ParamValue.prototype.toJavaScript=function(){if(typeof this.param=="string")return"params['"+this.param+"']";else return"params["+this.param+"]"};yy.UniOp=function(params){return yy.extend(this,params)};yy.UniOp.prototype.toString=function(){if(this.op=="-")return this.op+this.right.toString();if(this.op=="+")return this.op+this.right.toString();if(this.op=="#")return this.op+this.right.toString();if(this.op=="NOT")return this.op+"("+this.right.toString()+")";else if(this.op==null)return"("+this.right.toString()+")"};yy.UniOp.prototype.findAggregator=function(query){if(this.right.findAggregator)this.right.findAggregator(query)};yy.UniOp.prototype.toType=function(tableid){if(this.op=="-")return"number";if(this.op=="+")return"number";if(this.op=="NOT")return"boolean"};yy.UniOp.prototype.toJavaScript=function(context,tableid,defcols){if(this.op=="-")return"(-("+this.right.toJavaScript(context,tableid,defcols)+"))";if(this.op=="+")return"("+this.right.toJavaScript(context,tableid,defcols)+")";if(this.op=="NOT")return"!("+this.right.toJavaScript(context,tableid,defcols)+")";if(this.op=="#"){if(this.right instanceof yy.Column){return"(alasql.databases[alasql.useid].objects['"+this.right.columnid+"'])"}else{return"(alasql.databases[alasql.useid].objects["+this.right.toJavaScript(context,tableid,defcols)+"])"}}else if(this.op==null)return"("+this.right.toJavaScript(context,tableid,defcols)+")"};yy.Column=function(params){return yy.extend(this,params)};yy.Column.prototype.toString=function(){var s;if(this.columnid==+this.columnid){s="["+this.columnid+"]"}else{s=this.columnid}if(this.tableid){if(+this.columnid==this.columnid){s=this.tableid+s}else{s=this.tableid+"."+s}if(this.databaseid){s=this.databaseid+"."+s}}return s};yy.Column.prototype.toJavaScript=function(context,tableid,defcols){var s="";if(!this.tableid&&tableid==""&&!defcols){if(this.columnid!="_"){s=context+"['"+this.columnid+"']"}else{if(context=="g"){s="g['_']"}else{s=context}}}else{if(context=="g"){s="g['"+this.nick+"']"}else if(this.tableid){if(this.columnid!="_"){s=context+"['"+this.tableid+"']['"+this.columnid+"']"}else{if(context=="g"){s="g['_']"}else{s=context+"['"+this.tableid+"']"}}}else if(defcols){var tbid=defcols[this.columnid];if(tbid=="-"){throw new Error('Cannot resolve column "'+this.columnid+'" because it exists in two source tables')}else if(tbid){if(this.columnid!="_"){s=context+"['"+tbid+"']['"+this.columnid+"']"}else{s=context+"['"+tbid+"']"}}else{if(this.columnid!="_"){s=context+"['"+(this.tableid||tableid)+"']['"+this.columnid+"']"}else{s=context+"['"+(this.tableid||tableid)+"']"}}}else if(tableid==-1){s=context+"['"+this.columnid+"']"}else{if(this.columnid!="_"){s=context+"['"+(this.tableid||tableid)+"']['"+this.columnid+"']"}else{s=context+"['"+(this.tableid||tableid)+"']"}}}return s};yy.AggrValue=function(params){return yy.extend(this,params)};yy.AggrValue.prototype.toString=function(){var s="";if(this.aggregatorid=="REDUCE")s+=L(this.funcid)+"(";else s+=this.aggregatorid+"(";if(this.distinct)s+=K("DISTINCT")+" ";if(this.expression)s+=this.expression.toString();s+=")";if(this.over)s+=" "+this.over.toString();return s};yy.AggrValue.prototype.findAggregator=function(query){var colas=escapeq(this.toString())+":"+query.selectGroup.length;var found=false;if(!found){if(!this.nick){this.nick=colas;var found=false;for(var i=0;i<query.removeKeys.length;i++){if(query.removeKeys[i]==colas){found=true;break}}if(!found)query.removeKeys.push(colas)}query.selectGroup.push(this)}return};yy.AggrValue.prototype.toType=function(){if(["SUM","COUNT","AVG","MIN","MAX","AGGR","VAR","STDDEV"].indexOf(this.aggregatorid)>-1)return"number";if(["ARRAY"].indexOf(this.aggregatorid)>-1)return"array";if(["FIRST","LAST"].indexOf(this.aggregatorid)>-1)return this.expression.toType()};yy.AggrValue.prototype.toJavaScript=function(context,tableid,defcols){var colas=this.nick;if(typeof colas=="undefined")colas=this.toString();return"g['"+colas+"']"};yy.OrderExpression=function(params){return yy.extend(this,params)};yy.OrderExpression.prototype.toString=function(){var s=this.expression.toString();if(this.order)s+=" "+this.order.toString();if(this.nocase)s+=" "+K("COLLATE")+" "+K("NOCASE");return s};yy.GroupExpression=function(params){return yy.extend(this,params)};yy.GroupExpression.prototype.toString=function(){return this.type+"("+this.group.toString()+")"};yy.ColumnDef=function(params){return yy.extend(this,params)};yy.ColumnDef.prototype.toString=function(){var s=this.columnid;if(this.dbtypeid)s+=" "+this.dbtypeid;if(this.dbsize){s+="("+this.dbsize;if(this.dbprecision)s+=","+this.dbprecision;s+=")"}if(this.primarykey)s+=" PRIMARY KEY";if(this.notnull)s+=" NOT NULL";return s};yy.FromData=function(params){return yy.extend(this,params)};yy.FromData.prototype.toString=function(){if(this.data)return K("DATA")+"("+(Math.random()*1e16|0)+")";else return"?"};yy.FromData.prototype.toJavaScript=function(){};yy.Select.prototype.exec=function(params,cb){if(this.preparams)params=this.preparams.concat(params);var databaseid=alasql.useid;db=alasql.databases[databaseid];var sql=this.toString();var hh=hash(sql);var statement=this.compile(databaseid);if(!statement)return;statement.sql=sql;statement.dbversion=db.dbversion;if(db.sqlCacheSize>alasql.MAXSQLCACHESIZE){db.resetSqlCache()}db.sqlCacheSize++;db.sqlCache[hh]=statement;var res=alasql.res=statement(params,cb);return res};yy.Select.prototype.Select=function(){var self=this;var agrs=[];if(arguments.length>1){args=Array.prototype.slice.call(arguments)}else if(arguments.length==1){if(arguments[0]instanceof Array){args=arguments[0]}else{args=[arguments[0]]}}else{throw new Error("Wrong number of arguments of Select() function")}self.columns=[];args.forEach(function(arg){if(typeof arg=="string"){self.columns.push(new yy.Column({columnid:arg}))}else if(typeof arg=="function"){var pari=0;if(self.preparams){pari=self.preparams.length}else{self.preparams=[]}self.preparams.push(arg);self.columns.push(new yy.Column({columnid:"*",func:arg,param:pari}))}else{}});return self};yy.Select.prototype.From=function(tableid){var self=this;if(!self.from)self.from=[];if(tableid instanceof Array){var pari=0;if(self.preparams){pari=self.preparams.length}else{self.preparams=[]}self.preparams.push(tableid);self.from.push(new yy.ParamValue({param:pari}))}else if(typeof tableid=="string"){self.from.push(new yy.Table({tableid:tableid}))}else{throw new Error("Unknown arguments in From() function")}return self};yy.Select.prototype.OrderBy=function(){var self=this;var agrs=[];self.order=[];if(arguments.length==0){args=["_"]}else if(arguments.length>1){args=Array.prototype.slice.call(arguments)}else if(arguments.length==1){if(arguments[0]instanceof Array){args=arguments[0]}else{args=[arguments[0]]}}else{throw new Error("Wrong number of arguments of Select() function")}if(args.length>0){args.forEach(function(arg){var expr=new yy.Column({columnid:arg});if(typeof arg=="function"){expr=arg}self.order.push(new yy.OrderExpression({expression:expr,direction:"ASC"}))})}return self};yy.Select.prototype.Top=function(topnum){var self=this;self.top=new yy.NumValue({value:topnum});return self};yy.Select.prototype.GroupBy=function(){var self=this;var agrs=[];if(arguments.length>1){args=Array.prototype.slice.call(arguments)}else if(arguments.length==1){if(arguments[0]instanceof Array){args=arguments[0]}else{args=[arguments[0]]}}else{throw new Error("Wrong number of arguments of Select() function")}self.group=[];args.forEach(function(arg){var expr=new yy.Column({columnid:arg});self.group.push(expr)});return self};yy.Select.prototype.Where=function(expr){var self=this;if(typeof expr=="function"){self.where=expr}return self};yy.FuncValue=function(params){return yy.extend(this,params)};yy.FuncValue.prototype.toString=function(){var s="";if(alasql.fn[this.funcid])s+=this.funcid;else if(alasql.aggr[this.funcid])s+=this.funcid;else if(alasql.stdlib[this.funcid.toUpperCase()]||alasql.stdfn[this.funcid.toUpperCase()])s+=this.funcid.toUpperCase();s+="(";if(this.args&&this.args.length>0){s+=this.args.map(function(arg){return arg.toString()}).join(",")}s+=")";if(this.as)s+=" AS "+this.as.toString();return s};yy.FuncValue.prototype.findAggregator=function(query){if(this.args&&this.args.length>0){this.args.forEach(function(arg){if(arg.findAggregator)arg.findAggregator(query)})}};yy.FuncValue.prototype.toJavaScript=function(context,tableid,defcols){var s="";var funcid=this.funcid;if(alasql.fn[funcid]){if(this.newid)s+="new ";s+="alasql.fn."+this.funcid+"(";if(this.args&&this.args.length>0){s+=this.args.map(function(arg){return arg.toJavaScript(context,tableid,defcols)}).join(",")}s+=")"}else if(alasql.stdlib[funcid.toUpperCase()]){if(this.args&&this.args.length>0){s+=alasql.stdlib[funcid.toUpperCase()].apply(this,this.args.map(function(arg){return arg.toJavaScript(context,tableid)}))}else{s+=alasql.stdlib[funcid.toUpperCase()]()}}else if(alasql.stdfn[funcid.toUpperCase()]){if(this.newid)s+="new ";s+="alasql.stdfn."+this.funcid.toUpperCase()+"(";if(this.args&&this.args.length>0){s+=this.args.map(function(arg){return arg.toJavaScript(context,tableid,defcols)}).join(",")}s+=")"}else{}return s};var stdlib=alasql.stdlib={};var stdfn=alasql.stdfn={};stdlib.ABS=function(a){return"Math.abs("+a+")"};stdlib.CLONEDEEP=function(a){return"alasql.utils.cloneDeep("+a+")"};stdlib.IIF=function(a,b,c){if(arguments.length==3){return"(("+a+")?("+b+"):("+c+"))"}else{throw new Error("Number of arguments of IFF is not equals to 3")}};stdlib.IFNULL=function(a,b){return"("+a+"||"+b+")"};stdlib.INSTR=function(s,p){return"(("+s+").indexOf("+p+")+1)"};stdlib.LEN=stdlib.LENGTH=function(s){return und(s,"y.length")};stdlib.LOWER=stdlib.LCASE=function(s){return und(s,"y.toLowerCase()")};stdlib.MAX=stdlib.GREATEST=function(){return"Math.max("+Array.prototype.join.call(arguments,",")+")"};stdlib.MIN=stdlib.LEAST=function(){return"Math.min("+Array.prototype.join.call(arguments,",")+")"};stdlib.SUBSTRING=stdlib.MID=function(a,b,c){if(arguments.length==2)return und(a,"y.substr("+b+"-1)");else if(arguments.length==3)return und(a,"y.substr("+b+"-1,"+c+")")};stdlib.ISNULL=stdlib.NULLIF=function(a,b){return"("+a+"=="+b+"?undefined:"+a+")"};stdlib.POWER=function(a,b){return"Math.pow("+a+","+b+")"};stdlib.RANDOM=function(r){if(arguments.length==0){return"Math.random()"}else{return"(Math.random()*("+r+")|0)"}};stdlib.ROUND=function(s,d){if(arguments.length==2){return"Math.round("+s+"*Math.pow(10,"+d+"))/Math.pow(10,"+d+")"}else{return"Math.round("+s+")"}};stdlib.ROWNUM=function(){return"1"};stdlib.ROW_NUMBER=function(){return"1"};stdlib.SQRT=function(s){return"Math.sqrt("+s+")"};stdlib.TRIM=function(s){return und(s,"y.trim()")};stdlib.UPPER=stdlib.UCASE=function(s){return und(s,"y.toUpperCase()")};alasql.aggr.GROUP_CONCAT=function(v,s){if(typeof s=="undefined")return v;else return s+","+v};alasql.aggr.MEDIAN=function(v,s,acc){if(typeof acc.arr=="undefined"){acc.arr=[v];
-return v}else{acc.arr.push(v);var p=acc.arr.sort();return p[p.length/2|0]}};alasql.aggr.VAR=function(v,s,acc){if(typeof acc.arr=="undefined"){acc.arr=[v];acc.sum=v}else{acc.arr.push(v);acc.sum+=v}var N=acc.arr.length;var avg=acc.sum/N;var std=0;for(var i=0;i<N;i++){std+=(acc.arr[i]-avg)*(acc.arr[i]-avg)}std=std/(N-1);return std};alasql.aggr.STDEV=function(v,s,acc){return Math.sqrt(alasql.aggr.VAR(v,s,acc))};alasql.aggr.VARP=function(v,s,acc){if(typeof acc.arr=="undefined"){acc.arr=[v];acc.sum=v}else{acc.arr.push(v);acc.sum+=v}var N=acc.arr.length;var avg=acc.sum/N;var std=0;for(var i=0;i<N;i++){std+=(acc.arr[i]-avg)*(acc.arr[i]-avg)}std=std/N;return std};alasql.aggr.STD=alasql.aggr.STDDEV=alasql.aggr.STDEVP=function(v,s,acc){return Math.sqrt(alasql.aggr.VARP(v,s,acc))};yy.CaseValue=function(params){return yy.extend(this,params)};yy.CaseValue.prototype.toString=function(){var s="CASE ";if(this.expression)s+=this.expression.toString();if(this.whens){s+=this.whens.map(function(w){return" WHEN "+w.when.toString()+" THEN "+w.then.toString()}).join()}s+=" END";return s};yy.CaseValue.prototype.findAggregator=function(query){if(this.expression&&this.expression.findAggregator)this.expression.findAggregator(query);if(this.whens&&this.whens.length>0){this.whens.forEach(function(w){if(w.when.findAggregator)w.when.findAggregator(query);if(w.then.findAggregator)w.then.findAggregator(query)})}if(this.elses&&this.elses.findAggregator)this.elses.findAggregator(query)};yy.CaseValue.prototype.toJavaScript=function(context,tableid,defcols){var s="((function("+context+",params,alasql){var r;";if(this.expression){s+="v="+this.expression.toJavaScript(context,tableid,defcols)+";";s+=(this.whens||[]).map(function(w){return" if(v=="+w.when.toJavaScript(context,tableid,defcols)+") {r="+w.then.toJavaScript(context,tableid,defcols)+"}"}).join(" else ");if(this.elses)s+=" else {r="+this.elses.toJavaScript(context,tableid,defcols)+"}"}else{s+=(this.whens||[]).map(function(w){return" if("+w.when.toJavaScript(context,tableid,defcols)+") {r="+w.then.toJavaScript(context,tableid,defcols)+"}"}).join(" else ");if(this.elses)s+=" else {r="+this.elses.toJavaScript(context,tableid,defcols)+"}"}s+=";return r;}).bind(this))("+context+",params,alasql)";return s};yy.Json=function(params){return yy.extend(this,params)};yy.Json.prototype.toString=function(){var s="";s+=JSONtoString(this.value);s+="";return s};var JSONtoString=alasql.utils.JSONtoString=function(obj){var s="";if(typeof obj=="string")s='"'+obj+'"';else if(typeof obj=="number")s=obj;else if(typeof obj=="boolean")s=obj;else if(typeof obj=="object"){if(obj instanceof Array){s+="["+obj.map(function(b){return JSONtoString(b)}).join(",")+"]"}else if(!obj.toJavaScript||obj instanceof yy.Json){s="{";var ss=[];for(var k in obj){var s1="";if(typeof k=="string")s1+='"'+k+'"';else if(typeof k=="number")s1+=k;else if(typeof k=="boolean")s1+=k;else{throw new Error("THis is not ES6... no expressions on left side yet")}s1+=":"+JSONtoString(obj[k]);ss.push(s1)}s+=ss.join(",")+"}"}else if(obj.toString){s=obj.toString()}else{throw new Error("1Can not show JSON object "+JSON.stringify(obj))}}else{throw new Error("2Can not show JSON object "+JSON.stringify(obj))}return s};function JSONtoJavaScript(obj,context,tableid,defcols){var s="";if(typeof obj=="string")s='"'+obj+'"';else if(typeof obj=="number")s="("+obj+")";else if(typeof obj=="boolean")s=obj;else if(typeof obj=="object"){if(obj instanceof Array){s+="["+obj.map(function(b){return JSONtoJavaScript(b,context,tableid,defcols)}).join(",")+"]"}else if(!obj.toJavaScript||obj instanceof yy.Json){s="{";var ss=[];for(var k in obj){var s1="";if(typeof k=="string")s1+='"'+k+'"';else if(typeof k=="number")s1+=k;else if(typeof k=="boolean")s1+=k;else{throw new Error("THis is not ES6... no expressions on left side yet")}s1+=":"+JSONtoJavaScript(obj[k],context,tableid,defcols);ss.push(s1)}s+=ss.join(",")+"}"}else if(obj.toJavaScript){s=obj.toJavaScript(context,tableid,defcols)}else{throw new Error("1Can not parse JSON object "+JSON.stringify(obj))}}else{throw new Error("2Can not parse JSON object "+JSON.stringify(obj))}return s}yy.Json.prototype.toJavaScript=function(context,tableid,defcols){return JSONtoJavaScript(this.value,context,tableid,defcols)};yy.Convert=function(params){return yy.extend(this,params)};yy.Convert.prototype.toString=function(){var s="CONVERT(";s+=this.dbtypeid;if(typeof this.dbsize!="undefined"){s+="("+this.dbsize;if(this.dbprecision)s+=","+dbprecision;s+=")"}s+=","+this.expression.toString();if(this.style)s+=","+this.style;s+=")";return s};yy.Convert.prototype.toJavaScript=function(context,tableid,defcols){return"alasql.stdfn.CONVERT("+this.expression.toJavaScript(context,tableid,defcols)+',{dbtypeid:"'+this.dbtypeid+'",dbsize:'+this.dbsize+",style:"+this.style+"})";throw new Error("There is not such type conversion for "+this.toString())};alasql.stdfn.CONVERT=function(value,args){var val=value;if(args.style){var t;if(/\d{8}/.test(val))t=new Date(+val.substr(0,4),+val.substr(4,2)-1,+val.substr(6,2));else t=new Date(val);if(args.style==1){val=("0"+(t.getMonth()+1)).substr(-2)+"/"+("0"+t.getDate()).substr(-2)+"/"+("0"+t.getYear()).substr(-2)}else if(args.style==2){val=("0"+t.getYear()).substr(-2)+"."+("0"+(t.getMonth()+1)).substr(-2)+"."+("0"+t.getDate()).substr(-2)}else if(args.style==3){val=("0"+t.getDate()).substr(-2)+"/"+("0"+(t.getMonth()+1)).substr(-2)+"/"+("0"+t.getYear()).substr(-2)}else if(args.style==4){val=("0"+t.getDate()).substr(-2)+"."+("0"+(t.getMonth()+1)).substr(-2)+"."+("0"+t.getYear()).substr(-2)}else if(args.style==5){val=("0"+t.getDate()).substr(-2)+"-"+("0"+(t.getMonth()+1)).substr(-2)+"-"+("0"+t.getYear()).substr(-2)}else if(args.style==6){val=("0"+t.getDate()).substr(-2)+" "+t.toString().substr(4,3).toLowerCase()+" "+("0"+t.getYear()).substr(-2)}else if(args.style==7){val=t.toString().substr(4,3)+" "+("0"+t.getDate()).substr(-2)+","+("0"+t.getYear()).substr(-2)}else if(args.style==8){val=("0"+t.getHours()).substr(-2)+":"+("0"+(t.getMinutes()+1)).substr(-2)+":"+("0"+t.getSeconds()).substr(-2)}else if(args.style==10){val=("0"+(t.getMonth()+1)).substr(-2)+"-"+("0"+t.getDate()).substr(-2)+"-"+("0"+t.getYear()).substr(-2)}else if(args.style==11){val=("0"+t.getYear()).substr(-2)+"/"+("0"+(t.getMonth()+1)).substr(-2)+"/"+("0"+t.getDate()).substr(-2)}else if(args.style==12){val=("0"+t.getYear()).substr(-2)+("0"+(t.getMonth()+1)).substr(-2)+("0"+t.getDate()).substr(-2)}else if(args.style==101){val=("0"+(t.getMonth()+1)).substr(-2)+"/"+("0"+t.getDate()).substr(-2)+"/"+t.getFullYear()}else if(args.style==102){val=t.getFullYear()+"."+("0"+(t.getMonth()+1)).substr(-2)+"."+("0"+t.getDate()).substr(-2)}else if(args.style==103){val=("0"+t.getDate()).substr(-2)+"/"+("0"+(t.getMonth()+1)).substr(-2)+"/"+t.getFullYear()}else if(args.style==104){val=("0"+t.getDate()).substr(-2)+"."+("0"+(t.getMonth()+1)).substr(-2)+"."+t.getFullYear()}else if(args.style==105){val=("0"+t.getDate()).substr(-2)+"-"+("0"+(t.getMonth()+1)).substr(-2)+"-"+t.getFullYear()}else if(args.style==106){val=("0"+t.getDate()).substr(-2)+" "+t.toString().substr(4,3).toLowerCase()+" "+t.getFullYear()}else if(args.style==107){val=t.toString().substr(4,3)+" "+("0"+t.getDate()).substr(-2)+","+t.getFullYear()}else if(args.style==108){val=("0"+t.getHours()).substr(-2)+":"+("0"+(t.getMinutes()+1)).substr(-2)+":"+("0"+t.getSeconds()).substr(-2)}else if(args.style==110){val=("0"+(t.getMonth()+1)).substr(-2)+"-"+("0"+t.getDate()).substr(-2)+"-"+t.getFullYear()}else if(args.style==111){val=t.getFullYear()+"/"+("0"+(t.getMonth()+1)).substr(-2)+"/"+("0"+t.getDate()).substr(-2)}else if(args.style==112){val=t.getFullYear()+("0"+(t.getMonth()+1)).substr(-2)+("0"+t.getDate()).substr(-2)}else{throw new Error("The CONVERT style "+args.style+" is not realized yet.")}}var udbtypeid=args.dbtypeid.toUpperCase();if(args.dbtypeid=="Date"){return new Date(val)}else if(udbtypeid=="DATE"){var d=new Date(val);var s=d.getFullYear()+"."+("0"+(d.getMonth()+1)).substr(-2)+"."+("0"+d.getDate()).substr(-2);return s}else if(udbtypeid=="DATETIME"){var d=new Date(val);var s=d.getFullYear()+"."+("0"+(d.getMonth()+1)).substr(-2)+"."+("0"+d.getDate()).substr(-2);s+=" "+("0"+d.getHours()).substr(-2)+":"+("0"+d.getMinutes()).substr(-2)+":"+("0"+d.getSeconds()).substr(-2);s+="."+("00"+d.getMilliseconds()).substr(-3);return s}else if(["NUMBER","FLOAT"].indexOf(udbtypeid)>-1){return+val}else if(["MONEY"].indexOf(udbtypeid)>-1){var m=+val;return(m|0)+m*100%100/100}else if(["BOOLEAN"].indexOf(udbtypeid)>-1){return!!val}else if(["INT","INTEGER","SMALLINT","BIGINT","SERIAL","SMALLSERIAL","BIGSERIAL"].indexOf(args.dbtypeid.toUpperCase())>-1){return val|0}else if(["STRING","VARCHAR","NVARCHAR","CHARACTER VARIABLE"].indexOf(args.dbtypeid.toUpperCase())>-1){if(args.dbsize)return(""+val).substr(0,args.dbsize);else return""+val}else if(["CHAR","CHARACTER","NCHAR"].indexOf(udbtypeid)>-1){return(val+new Array(args.dbsize+1).join(" ")).substr(0,args.dbsize)}else if(["DECIMAL","NUMERIC"].indexOf(udbtypeid)>-1){var m=+val;var fxd=Math.pow(10,args.dbpecision);return(m|0)+m*fxd%fxd/fxd}else if(["JSON"].indexOf(udbtypeid)>-1){if(typeof val=="object")return val;try{return JSON.parse(val)}catch(err){throw new Error("Cannot convert string to JSON")}}return val};yy.ColumnDef=function(params){return yy.extend(this,params)};yy.ColumnDef.prototype.toString=function(){var s=this.columnid;if(this.dbtypeid)s+=" "+this.dbtypeid;if(this.dbsize){s+="("+this.dbsize;if(this.dbprecision)s+=","+this.dbprecision;s+=")"}if(this.primarykey)s+=" PRIMARY KEY";if(this.notnull)s+=" NOT NULL";return s};yy.CreateTable=function(params){return yy.extend(this,params)};yy.CreateTable.prototype.toString=function(){var s=K("CREATE");if(this.temporary)s+=" "+K("TEMPORARY");if(this.view)s+=" "+K("VIEW");else s+=" "+(this.class?K("CLASS"):K("TABLE"));if(this.ifnotexists)s+=" "+K("IF")+" "+K("NOT")+" "+K("EXISTS");s+=" "+this.table.toString();if(this.viewcolumns){s+="("+this.viewcolumns.map(function(vcol){return vcol.toString()}).join(",")+")"}if(this.as)s+=" "+K("AS")+" "+L(this.as);else{var ss=this.columns.map(function(col){return col.toString()});s+=" ("+NL()+ID()+ss.join(","+NL()+ID())+")"}if(this.view&&this.select){s+=" AS "+this.select.toString()}return s};yy.CreateTable.prototype.execute=function(databaseid,params,cb){var db=alasql.databases[this.table.databaseid||databaseid];var tableid=this.table.tableid;if(!tableid){throw new Error("Table name is not defined")}var columns=this.columns;var constraints=this.constraints||[];if(this.ifnotexists&&db.tables[tableid])return 0;if(db.tables[tableid]){throw new Error("Can not create table '"+tableid+"', because it already exists in the database '"+db.databaseid+"'")}var table=db.tables[tableid]=new alasql.Table;if(this.class){table.isclass=true}table.identities={};table.checkfn=[];var ss=[];if(this.columns){this.columns.forEach(function(col){var dbtypeid=col.dbtypeid;if(!alasql.fn[dbtypeid])dbtypeid=dbtypeid.toUpperCase();if(["SERIAL","SMALLSERIAL","BIGSERIAL"].indexOf(dbtypeid)>-1)col.identity={value:1,step:1};var newcol={columnid:col.columnid,dbtypeid:dbtypeid,dbsize:col.dbsize,dbprecision:col.dbprecision,notnull:col.notnull,identity:col.identity};if(col.identity){table.identities[col.columnid]={value:+col.identity.value,step:+col.identity.step}}if(col.check){table.checkfn.push(new Function("r","var y;return "+col.check.expression.toJavaScript("r","")))}if(col.default){ss.push("'"+col.columnid+"':"+col.default.toJavaScript("r",""))}if(col.primarykey){var pk=table.pk={};pk.columns=[col.columnid];pk.onrightfns="r['"+col.columnid+"']";pk.onrightfn=new Function("r","var y;return "+pk.onrightfns);pk.hh=hash(pk.onrightfns);table.uniqs[pk.hh]={}}if(col.unique){var uk={};if(typeof table.uk=="undefined")table.uk=[];table.uk.push(uk);uk.columns=[col.columnid];uk.onrightfns="r['"+col.columnid+"']";uk.onrightfn=new Function("r","var y;return "+uk.onrightfns);uk.hh=hash(uk.onrightfns);table.uniqs[uk.hh]={}}if(col.foreignkey){var fk=col.foreignkey.table;var fktable=alasql.databases[fk.databaseid||alasql.useid].tables[fk.tableid];if(typeof fk.columnid=="undefined"){if(fktable.pk.columns&&fktable.pk.columns.length>0){fk.columnid=fktable.pk.columns[0]}else{throw new Error("FOREIGN KEY allowed only to tables with PRIMARY KEYs")}}var fkfn=function(r){var rr={};if(typeof r[col.columnid]=="undefined")return true;rr[fk.columnid]=r[col.columnid];var addr=fktable.pk.onrightfn(rr);if(!fktable.uniqs[fktable.pk.hh][addr]){throw new Error('Foreign key "'+r[col.columnid]+'" is not found in table '+fktable.tableid)}return true};table.checkfn.push(fkfn)}table.columns.push(newcol);table.xcolumns[newcol.columnid]=newcol})}table.defaultfns=ss.join(",");constraints.forEach(function(con){if(con.type=="PRIMARY KEY"){if(table.pk){throw new Error("Primary key already exists")}var pk=table.pk={};pk.columns=con.columns;pk.onrightfns=pk.columns.map(function(columnid){return"r['"+columnid+"']"}).join("+'`'+");pk.onrightfn=new Function("r","var y;return "+pk.onrightfns);pk.hh=hash(pk.onrightfns);table.uniqs[pk.hh]={}}else if(con.type=="CHECK"){table.checkfn.push(new Function("r","var y;return "+con.expression.toJavaScript("r","")))}else if(con.type=="UNIQUE"){var uk={};if(!table.uk)table.uk=[];table.uk.push(uk);uk.columns=con.columns;uk.onrightfns=uk.columns.map(function(columnid){return"r['"+columnid+"']"}).join("+'`'+");uk.onrightfn=new Function("r","var y;return "+uk.onrightfns);uk.hh=hash(uk.onrightfns);table.uniqs[uk.hh]={}}else if(con.type=="FOREIGN KEY"){var col=table.xcolumns[con.columns[0]];var fk=con.fktable;if(con.fkcolumns&&con.fkcolumns.length>0)fk.columnid=con.fkcolumns[0];var fktable=alasql.databases[fk.databaseid||alasql.useid].tables[fk.tableid];if(typeof fk.columnid=="undefined"){fk.columnid=fktable.pk.columns[0]}var fkfn=function(r){var rr={};if(typeof r[col.columnid]=="undefined")return true;rr[fk.columnid]=r[col.columnid];var addr=fktable.pk.onrightfn(rr);if(!fktable.uniqs[fktable.pk.hh][addr]){throw new Error('Foreign key "'+r[col.columnid]+'" is not found in table '+fktable.tableid)}return true};table.checkfn.push(fkfn)}});if(this.view&&this.viewcolumns){var self=this;this.viewcolumns.forEach(function(vcol,idx){self.select.columns[idx].as=vcol.columnid})}if(db.engineid){return alasql.engines[db.engineid].createTable(this.table.databaseid||databaseid,tableid,this.ifnotexists,cb)}table.insert=function(r){var table=this;for(var columnid in table.identities){var ident=table.identities[columnid];r[columnid]=ident.value}if(table.checkfn&&table.checkfn.length>0){table.checkfn.forEach(function(checkfn){if(!checkfn(r)){throw new Error("Violation of CHECK constraint")}})}table.columns.forEach(function(column){if(column.notnull&&typeof r[column.columnid]=="undefined"){throw new Error("Wrong NULL value in NOT NULL column "+column.columnid)}});if(table.pk){var pk=table.pk;var addr=pk.onrightfn(r);if(typeof table.uniqs[pk.hh][addr]!="undefined"){throw new Error("Cannot insert record, because it already exists in primary key index")}}if(table.uk&&table.uk.length){table.uk.forEach(function(uk){var ukaddr=uk.onrightfn(r);if(typeof table.uniqs[uk.hh][ukaddr]!="undefined"){throw new Error("Cannot insert record, because it already exists in unique index")}})}table.data.push(r);for(var columnid in table.identities){var ident=table.identities[columnid];ident.value+=ident.step}if(table.pk){var pk=table.pk;var addr=pk.onrightfn(r);table.uniqs[pk.hh][addr]=r}if(table.uk&&table.uk.length){table.uk.forEach(function(uk){var ukaddr=uk.onrightfn(r);table.uniqs[uk.hh][ukaddr]=r})}};table.delete=function(i,params,alasql){var table=this;var r=this.data[i];if(this.pk){var pk=this.pk;var addr=pk.onrightfn(r);if(typeof this.uniqs[pk.hh][addr]=="undefined"){throw new Error("Something wrong with primary key index on table")}else{this.uniqs[pk.hh][addr]=undefined}}if(table.uk&&table.uk.length){table.uk.forEach(function(uk){var ukaddr=uk.onrightfn(r);if(typeof table.uniqs[uk.hh][ukaddr]=="undefined"){throw new Error("Something wrong with unique index on table")}table.uniqs[uk.hh][ukaddr]=undefined})}};table.deleteall=function(){this.data.length=0;if(this.pk){this.uniqs[this.pk.hh]={}}if(table.uk&&table.uk.length){table.uk.forEach(function(uk){table.uniqs[uk.hh]={}})}};table.update=function(assignfn,i,params){var r=cloneDeep(this.data[i]);if(this.pk){var pk=this.pk;pk.pkaddr=pk.onrightfn(r,params);if(typeof this.uniqs[pk.hh][pk.pkaddr]=="undefined"){throw new Error("Something wrong with index on table")}else{}}if(table.uk&&table.uk.length){table.uk.forEach(function(uk){uk.ukaddr=uk.onrightfn(r);if(typeof table.uniqs[uk.hh][uk.ukaddr]=="undefined"){throw new Error("Something wrong with unique index on table")}})}assignfn(r,params,alasql);if(table.checkfn&&table.checkfn.length>0){table.checkfn.forEach(function(checkfn){if(!checkfn(r)){throw new Error("Violation of CHECK constraint")}})}table.columns.forEach(function(column){if(column.notnull&&typeof r[column.columnid]=="undefined"){throw new Error("Wrong NULL value in NOT NULL column "+column.columnid)}});if(this.pk){pk.newpkaddr=pk.onrightfn(r);if(typeof this.uniqs[pk.hh][pk.newpkaddr]!="undefined"&&pk.newpkaddr!=pk.pkaddr){throw new Error("Record already exists")}else{}}if(table.uk&&table.uk.length){table.uk.forEach(function(uk){uk.newukaddr=uk.onrightfn(r);if(typeof table.uniqs[uk.hh][uk.newukaddr]!="undefined"&&uk.newukaddr!=uk.ukaddr){throw new Error("Record already exists")}})}if(this.pk){this.uniqs[pk.hh][pk.pkaddr]=undefined;this.uniqs[pk.hh][pk.newpkaddr]=r}if(table.uk&&table.uk.length){table.uk.forEach(function(uk){table.uniqs[uk.hh][uk.ukaddr]=undefined;table.uniqs[uk.hh][uk.newukaddr]=r})}this.data[i]=r};if(this.view&&this.select){table.view=true;table.select=this.select.compile(this.table.databaseid||databaseid)}var res;if(!alasql.options.nocount)res=1;if(cb)res=cb(res);return res};alasql.fn.Date=Object;alasql.fn.Date=Date;alasql.fn.Number=Number;alasql.fn.String=String;alasql.fn.Boolean=Boolean;stdfn.EXTEND=alasql.utils.extend;stdfn.CHAR=String.fromCharCode.bind(String);stdfn.ASCII=function(a){return a.charCodeAt(0)};stdfn.COALESCE=function(){for(var i=0;i<arguments.length;i++){if(typeof arguments[i]=="undefined")continue;if(typeof arguments[i]=="number"&&isNaN(arguments[i]))continue;return arguments[i]}return undefined};stdfn.USER=function(){return"alasql"};stdfn.OBJECT_ID=function(objid){return!!alasql.tables[objid]};stdfn.DATE=function(d){if(/\d{8}/.test(d))return new Date(+d.substr(0,4),+d.substr(4,2)-1,+d.substr(6,2));return new Date(d)};stdfn.NOW=function(){var d=new Date;var s=d.getFullYear()+"."+("0"+(d.getMonth()+1)).substr(-2)+"."+("0"+d.getDate()).substr(-2);s+=" "+("0"+d.getHours()).substr(-2)+":"+("0"+d.getMinutes()).substr(-2)+":"+("0"+d.getSeconds()).substr(-2);s+="."+("00"+d.getMilliseconds()).substr(-3);return s};stdfn.GETDATE=stdfn.NOW;stdfn.CURRENT_TIMESTAMP=stdfn.NOW;stdfn.SECOND=function(d){var d=new Date(d);return d.getSeconds()};stdfn.MINUTE=function(d){var d=new Date(d);return d.getMinutes()};stdfn.HOUR=function(d){var d=new Date(d);return d.getHours()};stdfn.DAYOFWEEK=stdfn.WEEKDAY=function(d){var d=new Date(d);return d.getDay()};stdfn.DAY=stdfn.DAYOFMONTH=function(d){var d=new Date(d);return d.getDate()};stdfn.MONTH=function(d){var d=new Date(d);return d.getMonth()+1};stdfn.YEAR=function(d){var d=new Date(d);return d.getFullYear()};stdfn.DATEDIFF=function(a,b){return+new Date(a).valueOf()-new Date(b).valueOf()};yy.DropTable=function(params){return yy.extend(this,params)};yy.DropTable.prototype.toString=function(){var s=K("DROP")+" ";if(this.view)s+=K("VIEW");else s+=K("TABLE");if(this.ifexists)s+=" "+K("IF")+" "+K("EXISTS");s+=" "+this.table.toString();return s};yy.DropTable.prototype.execute=function(databaseid,params,cb){var db=alasql.databases[this.table.databaseid||databaseid];var tableid=this.table.tableid;if(db.engineid){return alasql.engines[db.engineid].dropTable(this.table.databaseid||databaseid,tableid,this.ifexists,cb)}if(!this.ifexists||this.ifexists&&db.tables[tableid]){if(!db.tables[tableid]){if(!alasql.options.dropifnotexists){throw new Error("Can not drop table '"+this.table.tableid+"', because it does not exist in the database.")}}else{delete db.tables[tableid];return 1}}return 0};yy.TruncateTable=function(params){return yy.extend(this,params)};yy.TruncateTable.prototype.toString=function(){var s=K("TRUNCATE")+" "+K("TABLE");s+=" "+this.table.toString();return s};yy.TruncateTable.prototype.execute=function(databaseid,params,cb){var db=alasql.databases[this.table.databaseid||databaseid];var tableid=this.table.tableid;if(db.engineid){return alasql.engines[db.engineid].truncateTable(this.table.databaseid||databaseid,tableid,this.ifexists,cb)}if(db.tables[tableid]){db.tables[tableid].data=[]}else{throw new Error("Cannot truncate table becaues it does not exist")}return 0};yy.CreateVertex=function(params){return yy.extend(this,params)};yy.CreateVertex.prototype.toString=function(){var s=K("CREATE")+" "+K("VERTEX")+" ";if(this.class)s+=L(this.class)+" ";if(this.sharp)s+="#"+L(this.sharp)+" ";if(this.sets){s+=this.sets.toString()}else if(this.content){s+=this.content.toString()}else if(this.select){s+=this.select.toString()}return s};yy.CreateVertex.prototype.toJavaScript=function(context,tableid,defcols){var s="this.queriesfn["+(this.queriesidx-1)+"](this.params,null,"+context+")";return s};yy.CreateVertex.prototype.compile=function(databaseid){var dbid=databaseid;var sharp=this.sharp;if(typeof this.name!="undefined"){var s="x.name="+this.name.toJavaScript();var namefn=new Function("x",s)}if(this.sets&&this.sets.length>0){var s=this.sets.map(function(st){return"x['"+st.column.columnid+"']="+st.expression.toJavaScript("x","")}).join(";");var setfn=new Function("x,params,alasql",s)}else if(this.content){}else if(this.select){}else{}var statement=function(params,cb){var res;var db=alasql.databases[dbid];if(typeof sharp!="undefined"){var id=sharp}else{var id=db.counter++}var vertex={$id:id,$node:"VERTEX"};db.objects[vertex.$id]=vertex;res=vertex;if(namefn)namefn(vertex);if(setfn)setfn(vertex,params,alasql);if(cb)res=cb(res);return res};return statement};yy.CreateEdge=function(params){return yy.extend(this,params)};yy.CreateEdge.prototype.toString=function(){var s=K("CREATE")+" "+K("EDGE")+" ";if(this.class)s+=L(this.class)+" ";return s};yy.CreateEdge.prototype.toJavaScript=function(context,tableid,defcols){var s="this.queriesfn["+(this.queriesidx-1)+"](this.params,null,"+context+")";return s};yy.CreateEdge.prototype.compile=function(databaseid){var dbid=databaseid;var fromfn=new Function("params,alasql","var y;return "+this.from.toJavaScript());var tofn=new Function("params,alasql","var y;return "+this.to.toJavaScript());if(typeof this.name!="undefined"){var s="x.name="+this.name.toJavaScript();var namefn=new Function("x",s)}if(this.sets&&this.sets.length>0){var s=this.sets.map(function(st){return"x['"+st.column.columnid+"']="+st.expression.toJavaScript("x","")}).join(";");var setfn=new Function("x,params,alasql","var y;"+s)}else if(this.content){}else if(this.select){}else{}var statement=function(params,cb){var res=0;var db=alasql.databases[dbid];var edge={$id:db.counter++,$node:"EDGE"};var v1=fromfn(params,alasql);var v2=tofn(params,alasql);edge.$in=[v1.$id];edge.$out=[v2.$id];if(typeof v1.$out=="undefined")v1.$out=[];v1.$out.push(edge.$id);if(typeof v2.$in=="undefined")v2.$in=[];v2.$in.push(edge.$id);db.objects[edge.$id]=edge;res=edge;if(namefn)namefn(edge);if(setfn)setfn(edge,params,alasql);if(cb)res=cb(res);return res};return statement};yy.CreateGraph=function(params){return yy.extend(this,params)};yy.CreateGraph.prototype.toString=function(){var s=K("CREATE")+" "+K("GRAPH")+" ";if(this.class)s+=L(this.class)+" ";return s};yy.CreateGraph.prototype.execute=function(databaseid,params,cb){var res=[];if(this.from){if(alasql.from[this.from.funcid]){this.graph=alasql.from[this.from.funcid.toUpperCase()](this.from.args[0].value);console.log(this.graph)}}this.graph.forEach(function(g){if(g.source){var e={};if(typeof g.as!="undefined")alasql.vars[g.as]=e;if(typeof g.prop!="undefined"){e.name=g.prop}if(typeof g.sharp!="undefined")e.$id=g.sharp;if(typeof g.name!="undefined")e.name=g.name;if(typeof g.class!="undefined")e.$class=g.class;var db=alasql.databases[databaseid];if(typeof e.$id=="undefined"){e.$id=db.counter++}e.$node="EDGE";if(typeof g.json!="undefined"){extend(e,new Function("params,alasql","var y;return "+g.json.toJavaScript())(params,alasql))}var v1;if(g.source.vars){var vo=alasql.vars[g.source.vars];if(typeof vo=="object")v1=vo;else v1=db.objects[vo]}else{var av1=g.source.sharp;if(typeof av1=="undefined")av1=g.source.prop;v1=alasql.databases[databaseid].objects[av1];if(typeof v1=="undefined"&&alasql.options.autovertex&&(typeof g.source.prop!="undefined"||typeof g.source.name!="undefined")){v1=findVertex(g.source.prop||g.source.name);if(typeof v1=="undefined"){v1=createVertex(g.source)}}}var v2;if(g.source.vars){var vo=alasql.vars[g.target.vars];if(typeof vo=="object")v2=vo;else v2=db.objects[vo]}else{var av2=g.target.sharp;if(typeof av2=="undefined")av2=g.target.prop;v2=alasql.databases[databaseid].objects[av2];if(typeof v2=="undefined"&&alasql.options.autovertex&&(typeof g.target.prop!="undefined"||typeof g.target.name!="undefined")){v2=findVertex(g.target.prop||g.target.name);if(typeof v2=="undefined"){v2=createVertex(g.target)}}}e.$in=[v1.$id];e.$out=[v2.$id];if(typeof v1.$out=="undefined")v1.$out=[];v1.$out.push(e.$id);if(typeof v2.$in=="undefined")v2.$in=[];v2.$in.push(e.$id);db.objects[e.$id]=e;if(typeof e.$class!="undefined"){if(typeof alasql.databases[databaseid].tables[e.$class]=="undefined"){throw new Error("No such class. Pleace use CREATE CLASS")}else{alasql.databases[databaseid].tables[e.$class].data.push(e)}}res.push(e.$id)}else{createVertex(g)}});if(cb)res=cb(res);return res;function findVertex(name){var objects=alasql.databases[alasql.useid].objects;for(var k in objects){if(objects[k].name==name){return objects[k]}}return undefined}function createVertex(g){var v={};if(typeof g.as!="undefined")alasql.vars[g.as]=v;if(typeof g.prop!="undefined"){v.$id=g.prop;v.name=g.prop}if(typeof g.sharp!="undefined")v.$id=g.sharp;if(typeof g.name!="undefined")v.name=g.name;if(typeof g.class!="undefined")v.$class=g.class;var db=alasql.databases[databaseid];if(typeof v.$id=="undefined"){v.$id=db.counter++}v.$node="VERTEX";if(typeof g.json!="undefined"){extend(v,new Function("params,alasql","var y;return "+g.json.toJavaScript())(params,alasql))}db.objects[v.$id]=v;if(typeof v.$class!="undefined"){if(typeof alasql.databases[databaseid].tables[v.$class]=="undefined"){throw new Error("No such class. Pleace use CREATE CLASS")}else{alasql.databases[databaseid].tables[v.$class].data.push(v)}}res.push(v.$id);return v}};yy.CreateGraph.prototype.compile1=function(databaseid){var dbid=databaseid;var fromfn=new Function("params,alasql","var y;return "+this.from.toJavaScript());var tofn=new Function("params,alasql","var y;return "+this.to.toJavaScript());if(typeof this.name!="undefined"){var s="x.name="+this.name.toJavaScript();var namefn=new Function("x",s)}if(this.sets&&this.sets.length>0){var s=this.sets.map(function(st){return"x['"+st.column.columnid+"']="+st.expression.toJavaScript("x","")}).join(";");var setfn=new Function("x,params,alasql","var y;"+s)}else if(this.content){}else if(this.select){}else{}var statement=function(params,cb){var res=0;var db=alasql.databases[dbid];var edge={$id:db.counter++,$node:"EDGE"};var v1=fromfn(params,alasql);var v2=tofn(params,alasql);edge.$in=[v1.$id];edge.$out=[v2.$id];if(typeof v1.$out=="undefined")v1.$out=[];v1.$out.push(edge.$id);if(typeof v2.$in=="undefined")v2.$in=[];v2.$in.push(edge.$id);db.objects[edge.$id]=edge;res=edge;if(namefn)namefn(edge);if(setfn)setfn(edge,params,alasql);if(cb)res=cb(res);return res};return statement};yy.AlterTable=function(params){return yy.extend(this,params)};yy.AlterTable.prototype.toString=function(){var s="ALTER TABLE "+this.table.toString();if(this.renameto)s+=" RENAME TO "+this.renameto;return s};yy.AlterTable.prototype.execute=function(databaseid,params,cb){var db=alasql.databases[databaseid];db.dbversion=Date.now();if(this.renameto){var oldtableid=this.table.tableid;var newtableid=this.renameto;var res=1;if(db.tables[newtableid]){throw new Error("Can not rename a table '"+oldtableid+"' to '"+newtableid+"', because the table with this name already exists")}else if(newtableid==oldtableid){throw new Error("Can not rename a table '"+oldtableid+"' to itself")}else{db.tables[newtableid]=db.tables[oldtableid];delete db.tables[oldtableid];res=1}if(cb)cb(res);return res}else if(this.addcolumn){var db=alasql.databases[this.table.databaseid||databaseid];db.dbversion++;var tableid=this.table.tableid;var table=db.tables[tableid];var columnid=this.addcolumn.columnid;if(table.xcolumns[columnid]){throw new Error('Cannot add column "'+columnid+'", because it already exists in the table "'+tableid+'"')}var col={columnid:columnid,dbtypeid:this.dbtypeid,dbsize:this.dbsize,dbprecision:this.dbprecision,dbenum:this.dbenum,defaultfns:null};var defaultfn=function(){};table.columns.push(col);table.xcolumns[columnid]=col;for(var i=0,ilen=table.data.length;i<ilen;i++){table.data[i][columnid]=defaultfn()}return 1}else if(this.modifycolumn){var db=alasql.databases[this.table.databaseid||databaseid];db.dbversion++;var tableid=this.table.tableid;var table=db.tables[tableid];var columnid=this.modifycolumn.columnid;if(!table.xcolumns[columnid]){throw new Error('Cannot modify column "'+columnid+'", because it was not found in the table "'+tableid+'"')}var col=table.xcolumns[columnid];col.dbtypeid=this.dbtypeid;col.dbsize=this.dbsize;col.dbprecision=this.dbprecision;col.dbenum=this.dbenum;return 1}else if(this.renamecolumn){var db=alasql.databases[this.table.databaseid||databaseid];db.dbversion++;var tableid=this.table.tableid;var table=db.tables[tableid];var columnid=this.renamecolumn;var tocolumnid=this.to;var col;if(!table.xcolumns[columnid]){throw new Error('Column "'+columnid+'" is not found in the table "'+tableid+'"')}if(table.xcolumns[tocolumnid]){throw new Error('Column "'+tocolumnid+'" already exists in the table "'+tableid+'"')}if(columnid!=tocolumnid){for(var j=0;j<table.columns.length;j++){if(table.columns[j].columnid==columnid){table.columns[j].columnid=tocolumnid}}table.xcolumns[tocolumnid]=table.xcolumns[columnid];delete table.xcolumns[columnid];for(var i=0,ilen=table.data.length;i<ilen;i++){table.data[i][tocolumnid]=table.data[i][columnid];delete table.data[i][columnid]}return table.data.length}else return 0}else if(this.dropcolumn){var db=alasql.databases[this.table.databaseid||databaseid];db.dbversion++;var tableid=this.table.tableid;var table=db.tables[tableid];var columnid=this.dropcolumn;var found=false;for(var j=0;j<table.columns.length;j++){if(table.columns[j].columnid==columnid){found=true;table.columns.splice(j,1);break}}if(!found){throw new Error('Cannot drop column "'+columnid+'", because it was not found in the table "'+tableid+'"')}delete table.xcolumns[columnid];for(var i=0,ilen=table.data.length;i<ilen;i++){delete table.data[i][columnid]}return table.data.length}else{throw Error("Unknown ALTER TABLE method")}};yy.CreateIndex=function(params){return yy.extend(this,params)};yy.CreateIndex.prototype.toString=function(){var s="CREATE";if(this.unique)s+=" UNIQUE";s+=" INDEX "+this.indexid+" ON "+this.table.toString();s+="("+this.columns.toString()+")";return s};yy.CreateIndex.prototype.execute=function(databaseid,params,cb){var db=alasql.databases[databaseid];var tableid=this.table.tableid;var table=db.tables[tableid];var indexid=this.indexid;if(this.unique){var rightfns=this.columns.map(function(colid){return"r['"+colid+"']"}).join("+'`'+");table.uniqdefs[indexid]={rightfns:rightfns};var ux=table.uniqs[indexid]={};if(table.data.length>0){for(var i=0,ilen=table.data.length;i<ilen;i++){var addr=rightfns(table.data[i]);
-if(!ux[addr]){ux[addr]={num:0}}ux[addr].num++}}}else{var rightfns=this.columns.map(function(colid){return"r['"+colid+"']"}).join("+'`'+");var hh=hash(rightfns);table.inddefs[indexid]={rightfns:rightfns,hh:hh};table.indices[hh]={};var ix=table.indices[hh]={};if(table.data.length>0){for(var i=0,ilen=table.data.length;i<ilen;i++){var addr=rightfns(table.data[i]);if(!ix[addr]){ix[addr]=[]}ix[addr].push(table.data[i])}}}var res=1;if(cb)res=cb(res);return res};yy.DropIndex=function(params){return yy.extend(this,params)};yy.DropIndex.prototype.toString=function(){return"INDEX TABLE"+this.indexid};yy.DropIndex.prototype.compile=function(db){var indexid=this.indexid;return function(){return 1}};yy.WithSelect=function(params){return yy.extend(this,params)};yy.WithSelect.prototype.toString=function(){var s=K("WITH")+" ";s+=this.withs.map(function(w){return L(w.name)+" "+K("AS")+" ("+w.select.toString()+")"}).join(",")+" ";s+=this.select.toString();return s};yy.WithSelect.prototype.execute=function(databaseid,params,cb){var self=this;var savedTables=[];self.withs.forEach(function(w){savedTables.push(alasql.databases[databaseid].tables[w.name]);var tb=alasql.databases[databaseid].tables[w.name]=new Table({tableid:w.name});tb.data=w.select.execute(databaseid,params)});var res=1;res=this.select.execute(databaseid,params,function(data){self.withs.forEach(function(w,idx){if(savedTables[idx])alasql.databases[databaseid].tables[w.name]=savedTables[idx];else delete alasql.databases[databaseid].tables[w.name]});if(cb)data=cb(data);return data});return res};yy.If=function(params){return yy.extend(this,params)};yy.If.prototype.toString=function(){var s=K("IF")+" ";s+=this.expression.toString();s+=" "+this.thenstat.toString();if(this.elsestat)s+=" "+K("ELSE")+NL()+ID()+this.thenstat.toString();return s};yy.If.prototype.execute=function(databaseid,params,cb){var res;var fn=new Function("params,alasql,p","var y;return "+this.expression.toJavaScript("({})","",null)).bind(this);if(fn(params,alasql))res=this.thenstat.execute(databaseid,params,cb);else{if(this.elsestat)res=this.elsestat.execute(databaseid,params,cb);else{if(cb)res=cb(res)}}return res};yy.While=function(params){return yy.extend(this,params)};yy.While.prototype.toString=function(){var s="WHILE ";s+=this.expression.toString();s+=" "+this.loopstat.toString();return s};yy.While.prototype.execute=function(databaseid,params,cb){var self=this;var res=[];var fn=new Function("params,alasql,p","var y;return "+this.expression.toJavaScript());if(cb){var first=false;loop();function loop(data){if(first){res.push(data)}else{first=true}setTimeout(function(){if(fn(params,alasql)){self.loopstat.execute(databaseid,params,loop)}else{res=cb(res)}},0)}}else{while(fn(params,alasql)){var res1=self.loopstat.execute(databaseid,params);res.push(res1)}}return res};yy.Break=function(params){return yy.extend(this,params)};yy.Break.prototype.toString=function(){var s=K("BREAK");return s};yy.Break.prototype.execute=function(databaseid,params,cb,scope){var res=1;if(cb)res=cb(res);return res};yy.Continue=function(params){return yy.extend(this,params)};yy.Continue.prototype.toString=function(){var s=K("CONTINUE");return s};yy.Continue.prototype.execute=function(databaseid,params,cb,scope){var res=1;if(cb)res=cb(res);return res};yy.BeginEnd=function(params){return yy.extend(this,params)};yy.BeginEnd.prototype.toString=function(){var s=K("BEGIN")+" "+this.statements.toString()+" "+K("END");return s};yy.BeginEnd.prototype.execute=function(databaseid,params,cb,scope){var self=this;var res=[];var idx=0;runone();function runone(){self.statements[idx].execute(databaseid,params,function(data){res.push(data);idx++;if(idx<self.statements.length)return runone();if(cb)res=cb(res)})}return res};yy.Insert=function(params){return yy.extend(this,params)};yy.Insert.prototype.toString=function(){var s="INSERT INTO "+this.into.toString();if(this.columns)s+="("+this.columns.toString()+")";if(this.values)s+=" VALUES "+this.values.toString();if(this.select)s+=" "+this.select.toString();return s};yy.Insert.prototype.toJavaScript=function(context,tableid,defcols){var s="this.queriesfn["+(this.queriesidx-1)+"](this.params,null,"+context+")";return s};yy.Insert.prototype.compile=function(databaseid){var self=this;databaseid=self.into.databaseid||databaseid;var db=alasql.databases[databaseid];var tableid=self.into.tableid;var table=db.tables[tableid];var s="";var sw="";var s3="var a,aa=[],x;";var s33;if(this.values){if(this.exists){this.existsfn=this.exists.map(function(ex){var nq=ex.compile(databaseid);nq.query.modifier="RECORDSET";return nq})}if(this.queries){this.queriesfn=this.queries.map(function(q){var nq=q.compile(databaseid);nq.query.modifier="RECORDSET";return nq})}self.values.forEach(function(values){var ss=[];if(self.columns){self.columns.forEach(function(col,idx){var q="'"+col.columnid+"':";if(table.xcolumns&&table.xcolumns[col.columnid]){if(["INT","FLOAT","NUMBER","MONEY"].indexOf(table.xcolumns[col.columnid].dbtypeid)>=0){q+="(x="+values[idx].toJavaScript()+",x==undefined?undefined:+x)"}else if(alasql.fn[table.xcolumns[col.columnid].dbtypeid]){q+="(new "+table.xcolumns[col.columnid].dbtypeid+"(";q+=values[idx].toJavaScript();q+="))"}else{q+=values[idx].toJavaScript()}}else{q+=values[idx].toJavaScript()}ss.push(q)})}else{if(values instanceof Array&&table.columns&&table.columns.length>0){table.columns.forEach(function(col,idx){var q="'"+col.columnid+"':";if(["INT","FLOAT","NUMBER","MONEY"].indexOf(col.dbtypeid)>=0){q+="+"+values[idx].toJavaScript()}else if(alasql.fn[col.dbtypeid]){q+="(new "+col.dbtypeid+"(";q+=values[idx].toJavaScript();q+="))"}else{q+=values[idx].toJavaScript()}ss.push(q)})}else{sw=JSONtoJavaScript(values)}}if(db.tables[tableid].defaultfns){ss.unshift(db.tables[tableid].defaultfns)}if(sw){s+="a="+sw+";"}else{s+="a={"+ss.join(",")+"};"}if(db.tables[tableid].isclass){s+="var db=alasql.databases['"+databaseid+"'];";s+='a.$class="'+tableid+'";';s+="a.$id=db.counter++;";s+="db.objects[a.$id]=a;"}if(db.tables[tableid].insert){s+="var db=alasql.databases['"+databaseid+"'];";s+="db.tables['"+tableid+"'].insert(a);"}else{s+="aa.push(a);"}});s33=s3+s;if(db.tables[tableid].insert){}else{s+="alasql.databases['"+databaseid+"'].tables['"+tableid+"'].data="+"alasql.databases['"+databaseid+"'].tables['"+tableid+"'].data.concat(aa);"}if(db.tables[tableid].insert){if(db.tables[tableid].isclass){s+="return a.$id;"}else{s+="return "+self.values.length}}else{s+="return "+self.values.length}var insertfn=new Function("db, params, alasql","var y;"+s3+s).bind(this)}else if(this.select){selectfn=this.select.compile(databaseid);if(db.engineid&&alasql.engines[db.engineid].intoTable){var statement=function(params,cb){var aa=selectfn(params);var res=alasql.engines[db.engineid].intoTable(db.databaseid,tableid,aa,null,cb);return res};return statement}else{var insertfn=function(db,params,alasql){var res=selectfn(params);if(db.tables[tableid].insert){for(var i=0,ilen=res.length;i<ilen;i++){db.tables[tableid].insert(res[i])}}else{db.tables[tableid].data=db.tables[tableid].data.concat(res)}if(alasql.options.nocount)return;else return res.length}}}else if(this.default){var insertfns="db.tables['"+tableid+"'].data.push({"+table.defaultfns+"});return 1;";var insertfn=new Function("db,params,alasql",insertfns)}else{throw new Error("Wrong INSERT parameters")}if(db.engineid&&alasql.engines[db.engineid].intoTable&&alasql.options.autocommit){var statement=function(params,cb){var aa=new Function("db,params","var y;"+s33+"return aa;")(db,params);var res=alasql.engines[db.engineid].intoTable(db.databaseid,tableid,aa,null,cb);return res}}else{var statement=function(params,cb){var db=alasql.databases[databaseid];if(alasql.options.autocommit&&db.engineid){alasql.engines[db.engineid].loadTableData(databaseid,tableid)}var res=insertfn(db,params,alasql);if(alasql.options.autocommit&&db.engineid){alasql.engines[db.engineid].saveTableData(databaseid,tableid)}if(alasql.options.nocount)res=undefined;if(cb)cb(res);return res}}return statement};yy.Insert.prototype.execute=function(databaseid,params,cb){return this.compile(databaseid)(params,cb)};yy.Delete=function(params){return yy.extend(this,params)};yy.Delete.prototype.toString=function(){var s="DELETE FROM "+this.table.toString();if(this.where)s+=" WHERE "+this.where.toString();return s};yy.Delete.prototype.compile=function(databaseid){databaseid=this.table.databaseid||databaseid;var tableid=this.table.tableid;var statement;var db=alasql.databases[databaseid];if(this.where){if(this.exists){this.existsfn=this.exists.map(function(ex){var nq=ex.compile(databaseid);nq.query.modifier="RECORDSET";return nq})}if(this.queries){this.queriesfn=this.queries.map(function(q){var nq=q.compile(databaseid);nq.query.modifier="RECORDSET";return nq})}wherefn=new Function("r,params,alasql","var y;return ("+this.where.toJavaScript("r","")+")").bind(this);statement=function(params,cb){if(db.engineid&&alasql.engines[db.engineid].deleteFromTable){return alasql.engines[db.engineid].deleteFromTable(databaseid,tableid,wherefn,params,cb)}if(alasql.options.autocommit&&db.engineid&&db.engineid=="LOCALSTORAGE"){alasql.engines[db.engineid].loadTableData(databaseid,tableid)}var table=db.tables[tableid];var orignum=table.data.length;var newtable=[];for(var i=0,ilen=table.data.length;i<ilen;i++){if(wherefn(table.data[i],params,alasql)){if(table.delete){table.delete(i,params,alasql)}else{}}else newtable.push(table.data[i])}table.data=newtable;var res=orignum-table.data.length;if(alasql.options.autocommit&&db.engineid&&db.engineid=="LOCALSTORAGE"){alasql.engines[db.engineid].saveTableData(databaseid,tableid)}if(cb)cb(res);return res}}else{statement=function(params,cb){if(alasql.options.autocommit&&db.engineid){alasql.engines[db.engineid].loadTableData(databaseid,tableid)}var table=db.tables[tableid];table.dirty=true;var orignum=db.tables[tableid].data.length;db.tables[tableid].data.length=0;if(alasql.options.autocommit&&db.engineid){alasql.engines[db.engineid].saveTableData(databaseid,tableid)}if(cb)cb(orignum);return orignum}}return statement};yy.Delete.prototype.execute=function(databaseid,params,cb){return this.compile(databaseid)(params,cb)};yy.Update=function(params){return yy.extend(this,params)};yy.Update.prototype.toString=function(){var s="UPDATE "+this.table.toString();if(this.columns)s+=" SET "+this.columns.toString();if(this.where)s+=" WHERE "+this.where.toString();return s};yy.SetColumn=function(params){return yy.extend(this,params)};yy.SetColumn.prototype.toString=function(){return this.column.toString()+"="+this.expression.toString()};yy.Update.prototype.compile=function(databaseid){databaseid=this.table.databaseid||databaseid;var tableid=this.table.tableid;if(this.where){if(this.exists){this.existsfn=this.exists.map(function(ex){var nq=ex.compile(databaseid);nq.query.modifier="RECORDSET";return nq})}if(this.queries){this.queriesfn=this.queries.map(function(q){var nq=q.compile(databaseid);nq.query.modifier="RECORDSET";return nq})}var wherefn=new Function("r,params,alasql","var y;return "+this.where.toJavaScript("r","")).bind(this)}var s="";this.columns.forEach(function(col){s+="r['"+col.column.columnid+"']="+col.expression.toJavaScript("r","")+";"});var assignfn=new Function("r,params,alasql","var y;"+s);var statement=function(params,cb){var db=alasql.databases[databaseid];if(db.engineid&&alasql.engines[db.engineid].updateTable){return alasql.engines[db.engineid].updateTable(databaseid,tableid,assignfn,wherefn,params,cb)}if(alasql.options.autocommit&&db.engineid){alasql.engines[db.engineid].loadTableData(databaseid,tableid)}var table=db.tables[tableid];if(!table){throw new Error("Table '"+tableid+"' not exists")}var numrows=0;for(var i=0,ilen=table.data.length;i<ilen;i++){if(!wherefn||wherefn(table.data[i],params,alasql)){if(table.update){table.update(assignfn,i,params)}else{assignfn(table.data[i],params,alasql)}numrows++}}if(alasql.options.autocommit&&db.engineid){alasql.engines[db.engineid].saveTableData(databaseid,tableid)}if(cb)cb(numrows);return numrows};return statement};yy.Update.prototype.execute=function(databaseid,params,cb){return this.compile(databaseid)(params,cb)};yy.Merge=function(params){return yy.extend(this,params)};yy.Merge.prototype.toString=function(){var s=K("MERGE")+" ";s+=L(this.into.tableid)+" ";if(this.into.as)s+=K("AS")+" "+L(this.into.as)+" ";s+=K("USING")+" "+L(this.using.tableid)+" ";if(this.using.as)s+=K("AS")+" "+L(this.using.as)+" ";s+=K("ON")+" "+this.on.toString()+" ";this.matches.forEach(function(m){s+=K("WHEN")+" ";if(!m.matched)s+=K("NOT")+" ";s+=K("MATCHED")+" ";if(m.bytarget)s+=K("BY")+" "+K("TARGET")+" ";if(m.bysource)s+=K("BY")+" "+K("SOURCE")+" ";if(m.expr)s+=K("AND")+" "+m.expr.toString()+" ";s+=K("THEN")+" ";if(m.action.delete)s+=K("DELETE")+" ";if(m.action.insert){s+=K("INSERT")+" ";if(m.action.columns)s+="("+m.action.columns.toString()+") ";if(m.action.values)s+=K("VALUES")+" ("+m.action.values.toString()+") ";if(m.action.defaultvalues)s+=K("DEFAULT")+" "+K("VALUES")+" "}if(m.action.update){s+=K("UPDATE")+" ";s+=m.action.update.map(function(u){return u.toString()}).join(",")+" "}});return s};yy.Merge.prototype.execute=function(databaseid,params,cb){var res=1;if(cb)res=cb(res);return res};yy.CreateDatabase=function(params){return yy.extend(this,params)};yy.CreateDatabase.prototype.toString=function(){var s=K("CREATE");if(this.engineid)s+=" "+L(this.engineid);s+=" "+K("DATABASE");if(this.ifnotexists)s+=" "+K("IF")+" "+K("NOT")+" "+K("EXISTS");s+=" "+L(this.databaseid);if(this.args&&this.args.length>0){s+="("+this.args.map(function(arg){return arg.toString()}).join(", ")+")"}if(this.as)s+=" "+K("AS")+" "+L(this.as);return s};yy.CreateDatabase.prototype.execute=function(databaseid,params,cb){var args;if(this.args&&this.args.length>0){args=this.args.map(function(arg){return new Function("params,alasql","var y;return "+arg.toJavaScript())(params,alasql)})}if(this.engineid){var res=alasql.engines[this.engineid].createDatabase(this.databaseid,this.args,this.ifnotexists,this.as,cb);return res}else{var dbid=this.databaseid;if(alasql.databases[dbid]){throw new Error("Database '"+dbid+"' already exists")}var a=new alasql.Database(dbid);var res=1;if(cb)return cb(res);return res}};yy.AttachDatabase=function(params){return yy.extend(this,params)};yy.AttachDatabase.prototype.toString=function(){var s=K("ATTACH");if(this.engineid)s+=" "+L(this.engineid);s+=" "+K("DATABASE")+" "+L(this.databaseid);if(args){s+="(";if(args.length>0){s+=args.map(function(arg){return arg.toString()}).join(", ")}s+=")"}if(this.as)s+=" "+K("AS")+" "+L(this.as);return s};yy.AttachDatabase.prototype.execute=function(databaseid,params,cb){if(!alasql.engines[this.engineid]){throw new Error('Engine "'+this.engineid+'" is not defined.')}var res=alasql.engines[this.engineid].attachDatabase(this.databaseid,this.as,this.args,params,cb);return res};yy.DetachDatabase=function(params){return yy.extend(this,params)};yy.DetachDatabase.prototype.toString=function(){var s=K("DETACH");s+=" "+K("DATABASE")+" "+L(this.databaseid);return s};yy.DetachDatabase.prototype.execute=function(databaseid,params,cb){if(!alasql.databases[this.databaseid].engineid){throw new Error('Cannot detach database "'+this.engineid+'", because it was not attached.')}var res;var dbid=this.databaseid;if(dbid==alasql.DEFAULTDATABASEID){throw new Error("Drop of default database is prohibited")}if(!alasql.databases[dbid]){if(!this.ifexists){throw new Error("Database '"+dbid+"' does not exist")}else{res=0}}else{delete alasql.databases[dbid];if(dbid==alasql.useid){alasql.use()}res=1}if(cb)cb(res);return res};yy.UseDatabase=function(params){return yy.extend(this,params)};yy.UseDatabase.prototype.toString=function(){return K("USE")+" "+K("DATABASE")+" "+L(this.databaseid)};yy.UseDatabase.prototype.execute=function(databaseid,params,cb){var dbid=this.databaseid;if(!alasql.databases[dbid]){throw new Error("Database '"+dbid+"' does not exist")}alasql.use(dbid);var res=1;if(cb)cb(res);return res};yy.DropDatabase=function(params){return yy.extend(this,params)};yy.DropDatabase.prototype.toString=function(){var s=K("DROP");if(this.ifexists)s+=" "+K("IF")+" "+K("EXISTS");s+=" "+K("DATABASE")+" "+L(this.databaseid);return s};yy.DropDatabase.prototype.execute=function(databaseid,params,cb){if(this.engineid){return alasql.engines[this.engineid].dropDatabase(this.databaseid,this.ifexists,cb)}var res;var dbid=this.databaseid;if(dbid==alasql.DEFAULTDATABASEID){throw new Error("Drop of default database is prohibited")}if(!alasql.databases[dbid]){if(!this.ifexists){throw new Error("Database '"+dbid+"' does not exist")}else{res=0}}else{if(alasql.databases[dbid].engineid){throw new Error("Cannot drop database '"+dbid+"', because it is attached. Detach it.")}delete alasql.databases[dbid];if(dbid==alasql.useid){alasql.use()}res=1}if(cb)cb(res);return res};yy.Declare=function(params){return yy.extend(this,params)};yy.Declare.prototype.toString=function(){var s=K("DECLARE")+" ";if(this.declares&&this.declares.length>0){s=this.declares.map(function(declare){var s="";s+="@"+L(declare.variable)+" ";s+=declare.dbtypeid;if(this.dbsize)s+="("+N(this.dbsize);if(this.dbprecision)s+=","+N(this.dbprecision);s+=")";if(declare.expression)s+=" = "+declare.expression.toString();return s}).join(",")}return s};yy.Declare.prototype.execute=function(databaseid,params,cb){var res=1;if(this.declares&&this.declares.length>0){this.declares.map(function(declare){var dbtypeid=declare.dbtypeid;if(!alasql.fn[dbtypeid])dbtypeid=dbtypeid.toUpperCase();alasql.declares[declare.variable]={dbtypeid:dbtypeid,dbsize:declare.dbsize,dbprecision:declare.dbprecision};if(declare.expression){alasql.vars[declare.variable]=new Function("params,alasql","return "+declare.expression.toJavaScript("({})","",null))(params,alasql);if(alasql.declares[declare.variable]){alasql.vars[declare.variable]=alasql.stdfn.CONVERT(alasql.vars[declare.variable],alasql.declares[declare.variable])}}})}if(cb)res=cb(res);return res};yy.ShowDatabases=function(params){return yy.extend(this,params)};yy.ShowDatabases.prototype.toString=function(){var s=K("SHOW")+" "+K("DATABASES");if(this.like)s+="LIKE "+this.like.toString();return s};yy.ShowDatabases.prototype.execute=function(databaseid,params,cb){if(this.engineid){return alasql.engines[this.engineid].showDatabases(this.like,cb)}else{var self=this;var res=[];for(dbid in alasql.databases){res.push({databaseid:dbid})}if(self.like&&res&&res.length>0){res=res.filter(function(d){return d.databaseid.match(new RegExp(self.like.value.replace(/\%/g,".*"),"g"))})}if(cb)cb(res);return res}};yy.ShowTables=function(params){return yy.extend(this,params)};yy.ShowTables.prototype.toString=function(){var s=K("SHOW")+" "+K("TABLES");if(this.databaseid)s+=" FROM "+this.databaseid;if(this.like)s+=" "+K("LIKE")+" "+this.like.toString();return s};yy.ShowTables.prototype.execute=function(databaseid,params,cb){var db=alasql.databases[this.databaseid||databaseid];var self=this;var res=[];for(tableid in db.tables){res.push({tableid:tableid})}if(self.like&&res&&res.length>0){res=res.filter(function(d){return d.tableid.match(new RegExp(self.like.value.replace(/\%/g,".*"),"g"))})}if(cb)cb(res);return res};yy.ShowColumns=function(params){return yy.extend(this,params)};yy.ShowColumns.prototype.toString=function(){var s=K("SHOW")+" "+K("COLUMNS");if(this.table.tableid)s+=" "+K("FROM")+" "+this.table.tableid;if(this.databaseid)s+=" "+K("FROM")+" "+this.databaseid;return s};yy.ShowColumns.prototype.execute=function(databaseid){var db=alasql.databases[this.databaseid||databaseid];var table=db.tables[this.table.tableid];var self=this;if(table&&table.columns){var res=table.columns.map(function(col){return{columnid:col.columnid,dbtypeid:col.dbtypeid,dbsize:col.dbsize}});return res}else{return[]}};yy.ShowIndex=function(params){return yy.extend(this,params)};yy.ShowIndex.prototype.toString=function(){var s=K("SHOW")+" "+K("INDEX");if(this.table.tableid)s+=" "+K("FROM")+" "+this.table.tableid;if(this.databaseid)s+=" "+K("FROM")+" "+this.databaseid;return s};yy.ShowIndex.prototype.execute=function(databaseid){var db=alasql.databases[this.databaseid||databaseid];var table=db.tables[this.table.tableid];var self=this;var res=[];if(table&&table.indices){for(var ind in table.indices){res.push({hh:ind,len:Object.keys(table.indices[ind]).length})}}return res};yy.ShowCreateTable=function(params){return yy.extend(this,params)};yy.ShowCreateTable.prototype.toString=function(){var s=K("SHOW")+" "+K("CREATE")+" "+K("TABLE")+" "+L(this.table.tableid);if(this.databaseid)s+=" "+K("FROM")+" "+L(this.databaseid);return s};yy.ShowCreateTable.prototype.execute=function(databaseid){var db=alasql.databases[this.databaseid||databaseid];var table=db.tables[this.table.tableid];var self=this;if(table){var s=K("CREATE")+" "+K("TABLE")+" "+L(this.table.tableid)+" (";var ss=[];if(table.columns){table.columns.forEach(function(col){var a=L(col.columnid)+" "+K(col.dbtypeid);if(col.dbsize)a+="("+N(col.dbsize)+")";if(col.primarykey)a+=" "+K("PRIMARY")+" "+K("KEY");ss.push(a)});s+=ss.join(", ")}s+=")";return s}else{throw new Error('There is no such table "'+this.table.tableid+'"')}};yy.SetVariable=function(params){return yy.extend(this,params)};yy.SetVariable.prototype.toString=function(){var s=K("SET")+" ";if(typeof this.value!="undefined")s+=K(this.variable.toUpperCase())+" "+(this.value?"ON":"OFF");if(this.expression)s+=this.method+L(this.variable)+" = "+this.expression.toString();return s};yy.SetVariable.prototype.execute=function(databaseid,params,cb){if(typeof this.value!="undefined"){var val=this.value;if(val=="ON")val=true;else if(val=="OFF")val=false;alasql.options[this.variable]=val}else if(this.expression){if(this.exists){this.existsfn=this.exists.map(function(ex){var nq=ex.compile(databaseid);if(nq.query&&!nq.query.modifier)nq.query.modifier="RECORDSET";return nq})}if(this.queries){this.queriesfn=this.queries.map(function(q){var nq=q.compile(databaseid);if(nq.query&&!nq.query.modifier)nq.query.modifier="RECORDSET";return nq})}var res=new Function("params,alasql","return "+this.expression.toJavaScript("({})","",null)).bind(this)(params,alasql);if(alasql.declares[this.variable]){res=alasql.stdfn.CONVERT(res,alasql.declares[this.variable])}if(this.props&&this.props.length>0){if(this.method=="@"){var fs="alasql.vars['"+this.variable+"']"}else{var fs="params['"+this.variable+"']"}fs+=this.props.map(function(prop){if(typeof prop=="string"){return"['"+prop+"']"}else if(typeof prop=="number"){return"["+prop+"]"}else{return"["+prop.toJavaScript()+"]"}}).join();new Function("value,params,alasql","var y;"+fs+"=value")(res,params,alasql)}else{if(this.method=="@"){alasql.vars[this.variable]=res}else{params[this.variable]=res}}}var res=1;if(cb)res=cb(res);return res};alasql.test=function(name,times,fn){if(arguments.length==0){alasql.log(alasql.con.results);return}else if(arguments.length==1){var tm=Date.now();fn();alasql.con.log(Date.now()-tm);return}if(arguments.length==2){fn=times;times=1}var tm=Date.now();for(var i=0;i<times;i++)fn();alasql.con.results[name]=Date.now()-tm};alasql.log=function(sql,params){var olduseid=alasql.useid;var target=alasql.options.logtarget;if(typeof exports=="object"){target="console"}var res;if(typeof sql=="string"){res=alasql(sql,params)}else{res=sql}if(target=="console"||typeof exports=="object"){if(typeof sql=="string"&&alasql.options.logprompt)console.log(olduseid+">",sql);if(res instanceof Array){if(console.table){console.table(res)}else{console.log(JSONtoString(res))}}else{console.log(JSONtoString(res))}}else{var el;if(target=="output"){el=document.getElementsByTagName("output")[0]}else{if(typeof target=="string"){el=document.getElementById(target)}else{el=target}}var s="";if(typeof sql=="string"&&alasql.options.logprompt){s+="<pre><code>"+alasql.pretty(sql)+"</code></pre>"}if(res instanceof Array){if(res.length==0){s+="<p>[ ]</p>"}else if(typeof res[0]!="object"||res[0]instanceof Array){for(var i=0,ilen=res.length;i<ilen;i++){s+="<p>"+loghtml(res[i])+"</p>"}}else{s+=loghtml(res)}}else{s+=loghtml(res)}el.innerHTML+=s}};alasql.clear=function(){var target=alasql.options.logtarget;if(typeof exports=="object"){target="console"}if(target=="console"||typeof exports=="object"){if(console.clear){console.clear()}else{}}else{var el;if(target=="output"){el=document.getElementsByTagName("output")[0]}else{if(typeof target=="string"){el=document.getElementById(target)}else{el=target}}el.innerHTML=""}};alasql.write=function(s){var target=alasql.options.logtarget;if(typeof exports=="object"){target="console"}if(target=="console"||typeof exports=="object"){if(console.log){console.log(s)}else{}}else{var el;if(target=="output"){el=document.getElementsByTagName("output")[0]}else{if(typeof target=="string"){el=document.getElementById(target)}else{el=target}}el.innerHTML+=s}};function loghtml(res){var s="";if(typeof res=="undefined"){s+="undefined"}else if(res instanceof Array){s+="<style>";s+="table {border:1px black solid; border-collapse: collapse; border-spacing: 0px;}";s+="td,th {border:1px black solid; padding-left:5px; padding-right:5px}";s+="th {background-color: #EEE}";s+="</style>";s+="<table>";var cols=[];for(colid in res[0]){cols.push(colid)}s+="<tr><th>#";cols.forEach(function(colid){s+="<th>"+colid});for(var i=0,ilen=res.length;i<ilen;i++){s+="<tr><th>"+(i+1);cols.forEach(function(colid){s+="<td> ";if(+res[i][colid]==+res[i][colid]){s+='<div style="text-align:right">';if(typeof res[i][colid]=="undefined")s+="NULL";else s+=res[i][colid];s+="</div>"}else{if(typeof res[i][colid]=="undefined"){s+="NULL"}else if(typeof res[i][colid]=="string"){s+=res[i][colid]}else s+=JSONtoString(res[i][colid])}})}s+="</table>"}else{s+="<p>"+JSONtoString(res)+"</p>"}return s}function scrollTo(element,to,duration){if(duration<=0)return;var difference=to-element.scrollTop;var perTick=difference/duration*10;setTimeout(function(){if(element.scrollTop==to)return;element.scrollTop=element.scrollTop+perTick;scrollTo(element,to,duration-10)},10)}alasql.prompt=function(el,useidel,firstsql){if(typeof exports=="object"){throw new Error("The functionality of prompt is not realized for Node.js")}var prompti=0;if(typeof el=="string")el=document.getElementById(el);if(typeof useidel=="string")useidel=document.getElementById(useidel);useidel.textContent=alasql.useid;if(firstsql){alasql.prompthistory.push(firstsql);prompti=alasql.prompthistory.length;try{var tm=Date.now();alasql.log(firstsql);alasql.write('<p style="color:blue">'+(Date.now()-tm)+" ms</p>")}catch(err){alasql.write("<p>"+olduseid+"&gt;&nbsp;<b>"+sql+"</b></p>");alasql.write('<p style="color:red">'+err+"<p>")}}var y=el.getBoundingClientRect().top+document.getElementsByTagName("body")[0].scrollTop;scrollTo(document.getElementsByTagName("body")[0],y,500);el.onkeydown=function(event){if(event.which==13){var sql=el.value;var olduseid=alasql.useid;el.value="";alasql.prompthistory.push(sql);prompti=alasql.prompthistory.length;try{var tm=Date.now();alasql.log(sql);alasql.write('<p style="color:blue">'+(Date.now()-tm)+" ms</p>")}catch(err){alasql.write("<p>"+olduseid+"&gt;&nbsp;"+alasql.pretty(sql,false)+"</p>");alasql.write('<p style="color:red">'+err+"<p>")}el.focus();useidel.textContent=alasql.useid;var y=el.getBoundingClientRect().top+document.getElementsByTagName("body")[0].scrollTop;scrollTo(document.getElementsByTagName("body")[0],y,500)}else if(event.which==38){prompti--;if(prompti<0)prompti=0;if(alasql.prompthistory[prompti]){el.value=alasql.prompthistory[prompti];event.preventDefault()}}else if(event.which==40){prompti++;if(prompti>=alasql.prompthistory.length){prompti=alasql.prompthistory.length;el.value=""}else if(alasql.prompthistory[prompti]){el.value=alasql.prompthistory[prompti];event.preventDefault()}}}};yy.BeginTransaction=function(params){return yy.extend(this,params)};yy.BeginTransaction.prototype.toString=function(){return K("BEGIN")+" "+K("TRANSACTION")};yy.BeginTransaction.prototype.execute=function(databaseid,params,cb){var res=1;if(alasql.databases[databaseid].engineid){return alasql.engines[alasql.databases[alasql.useid].engineid].begin(databaseid,cb)}else{}if(cb)cb(res);return res};yy.CommitTransaction=function(params){return yy.extend(this,params)};yy.CommitTransaction.prototype.toString=function(){return K("COMMIT")+" "+K("TRANSACTION")};yy.CommitTransaction.prototype.execute=function(databaseid,params,cb){var res=1;if(alasql.databases[databaseid].engineid){return alasql.engines[alasql.databases[alasql.useid].engineid].commit(databaseid,cb)}else{}if(cb)cb(res);return res};yy.RollbackTransaction=function(params){return yy.extend(this,params)};yy.RollbackTransaction.prototype.toString=function(){return K("ROLLBACK")+" "+K("TRANSACTION")};yy.RollbackTransaction.prototype.execute=function(databaseid,params,cb){var res=1;if(alasql.databases[databaseid].engineid){return alasql.engines[alasql.databases[databaseid].engineid].rollback(databaseid,cb)}else{}if(cb)cb(res);return res};if(alasql.options.tsql){alasql.stdfn.OBJECT_ID=function(name,type){if(typeof type=="undefined")type="T";type=type.toUpperCase();var sname=name.split(".");var dbid=alasql.useid;var objname=sname[0];if(sname.length==2){dbid=sname[0];objname=sname[1]}var tables=alasql.databases[dbid].tables;dbid=alasql.databases[dbid].databaseid;for(var tableid in tables){if(tableid==objname){if(tables[tableid].view&&type=="V")return dbid+"."+tableid;if(!tables[tableid].view&&type=="T")return dbid+"."+tableid;return undefined}}return undefined}}if(alasql.options.mysql){}if(alasql.options.mysql||alasql.options.sqlite){alasql.from.INFORMATION_SCHEMA=function(filename,opts,cb,idx,query){if(filename=="VIEWS"||filename=="TABLES"){var res=[];for(var databaseid in alasql.databases){var tables=alasql.databases[databaseid].tables;for(var tableid in tables){if(tables[tableid].view&&filename=="VIEWS"||!tables[tableid].view&&filename=="TABLES"){res.push({TABLE_CATALOG:databaseid,TABLE_NAME:tableid})}}}if(cb)res=cb(res,idx,query);return res}throw new Error("Unknown INFORMATION_SCHEMA table")}}if(alasql.options.postgres){}if(alasql.options.oracle){}if(alasql.options.sqlite){}alasql.into.SQL=function(filename,opts,data,columns,cb){var res;if(typeof filename=="object"){opts=filename;filename=undefined}var opt={};alasql.utils.extend(opt,opts);if(typeof opt.tableid=="undefined"){throw new Error("Table for INSERT TO is not defined.")}var s="";if(columns.length==0){if(typeof data[0]=="object"){columns=Object.keys(data[0]).map(function(columnid){return{columnid:columnid}})}else{}}for(var i=0,ilen=data.length;i<ilen;i++){s+="INSERT INTO "+opts.tableid+"(";s+=columns.map(function(col){return col.columnid}).join(",");s+=") VALUES (";s+=columns.map(function(col){var val=data[i][col.columnid];if(col.typeid){if(col.typeid=="STRING"||col.typeid=="VARCHAR"||col.typeid=="NVARCHAR"||col.typeid=="CHAR"||col.typeid=="NCHAR"){val="'"+escapeqq(val)+"'"}}else{if(typeof val=="string"){val="'"+escapeqq(val)+"'"}}return val});s+=");\n"}res=alasql.utils.saveFile(filename,s);if(cb)res=cb(res);return res};alasql.into.HTML=function(selector,opts,data,columns,cb){var res=1;if(typeof exports!="object"){var opt={};alasql.utils.extend(opt,opts);var sel=document.querySelector(selector);if(!sel){throw new Error("Selected HTML element is not found")}if(columns.length==0){if(typeof data[0]=="object"){columns=Object.keys(data[0]).map(function(columnid){return{columnid:columnid}})}else{}}var tbe=document.createElement("table");var thead=document.createElement("thead");tbe.appendChild(thead);if(opt.headers){var tre=document.createElement("tr");for(var i=0;i<columns.length;i++){var the=document.createElement("th");the.textContent=columns[i].columnid;tre.appendChild(the)}thead.appendChild(tre)}var tbody=document.createElement("tbody");tbe.appendChild(tbody);for(var j=0;j<data.length;j++){var tre=document.createElement("tr");for(var i=0;i<columns.length;i++){var the=document.createElement("td");
-the.textContent=data[j][columns[i].columnid];tre.appendChild(the)}tbody.appendChild(tre)}alasql.utils.domEmptyChildren(sel);sel.appendChild(tbe)}if(cb)res=cb(res);return res};alasql.into.JSON=function(filename,opts,data,columns,cb){var res=1;if(typeof filename=="object"){opts=filename;filename=undefined}var opt={};var s=JSON.stringify(data);res=alasql.utils.saveFile(filename,s);if(cb)res=cb(res);return res};alasql.into.TXT=function(filename,opts,data,columns,cb){if(columns.length==0&&data.length>0){columns=Object.keys(data[0]).map(function(columnid){return{columnid:columnid}})}if(typeof filename=="object"){opts=filename;filename=undefined}var res=data.length;var s="";if(data.length>0){var key=columns[0].columnid;s+=data.map(function(d){return d[key]}).join("\n")}res=alasql.utils.saveFile(filename,s);if(cb)res=cb(res);return res};alasql.into.TAB=alasql.into.TSV=function(filename,opts,data,columns,cb){var opt={};alasql.utils.extend(opt,opts);opt.separator="	";return alasql.into.CSV(filename,opt,data,columns,cb)};alasql.into.CSV=function(filename,opts,data,columns,cb){if(columns.length==0&&data.length>0){columns=Object.keys(data[0]).map(function(columnid){return{columnid:columnid}})}if(typeof filename=="object"){opts=filename;filename=undefined}var opt={};opt.separator=",";opt.quote='"';alasql.utils.extend(opt,opts);var res=data.length;var s="";if(opt.headers){s+=columns.map(function(col){return col.columnid}).join(opt.separator)+"\n"}data.forEach(function(d,idx){s+=columns.map(function(col){var s=d[col.columnid];s=(s+"").replace(new RegExp("\\"+opt.quote,"g"),'""');if((s+"").indexOf(opt.separator)>-1||(s+"").indexOf(opt.quote)>-1)s=opt.quote+s+opt.quote;return s}).join(opt.separator)+"\n"});res=alasql.utils.saveFile(filename,s);if(cb)res=cb(res);return res};alasql.into.XLS=function(filename,opts,data,columns,cb){if(typeof filename=="object"){opts=filename;filename=undefined}var sheets={};if(opts&&opts.sheets){sheets=opts.sheets}var sheet={};if(typeof sheets["Sheet1"]!="undefined"){sheet=sheets[0]}else{if(typeof opts!="undefined"){sheet=opts}}if(typeof sheet.sheetid=="undefined"){sheet.sheetid="Sheet1"}var s=toHTML();var res=alasql.utils.saveFile(filename,s);if(cb)res=cb(res);return res;function toHTML(){var s='<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" 		xmlns="http://www.w3.org/TR/REC-html40"><head> 		<meta charset="utf-8" /> 		<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets> ';s+=" <x:ExcelWorksheet><x:Name>"+sheet.sheetid+"</x:Name><x:WorksheetOptions><x:DisplayGridlines/>     </x:WorksheetOptions> 		</x:ExcelWorksheet>";s+="</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head>";s+="<body";if(typeof sheet.style!="undefined"){s+=' style="';if(typeof sheet.style=="function"){s+=sheet.style(sheet)}else{s+=sheet.style}s+='"'}s+=">";s+="<table>";if(typeof sheet.caption!="undefined"){var caption=sheet.caption;if(typeof caption=="string"){caption={title:caption}}s+="<caption";if(typeof caption.style!="undefined"){s+=' style="';if(typeof caption.style=="function"){s+=caption.style(sheet,caption)}else{s+=caption.style}s+='" '}s+=">";s+=caption.title;s+="</caption>"}if(typeof sheet.columns!="undefined"){columns=sheet.columns}else{if(columns.length==0&&data.length>0){if(typeof data[0]=="object"){if(data[0]instanceof Array){columns=data[0].map(function(d,columnidx){return{columnid:columnidx}})}else{columns=Object.keys(data[0]).map(function(columnid){return{columnid:columnid}})}}}}columns.forEach(function(column,columnidx){if(typeof sheet.column!="undefined"){extend(column,sheet.column)}if(typeof column.width=="undefined"){if(sheet.column&&sheet.column.width!="undefined"){column.width=sheet.column.width}else{column.width="120px"}}if(typeof column.width=="number")column.width=column.width+"px";if(typeof column.columnid=="undefined")column.columnid=columnidx;if(typeof column.title=="undefined")column.title=""+column.columnid;if(sheet.headers&&sheet.headers instanceof Array)column.title=sheet.headers[idx]});s+="<colgroups>";columns.forEach(function(column){s+='<col style="width: '+column.width+'"></col>'});s+="</colgroups>";if(sheet.headers){s+="<thead>";s+="<tr>";columns.forEach(function(column,columnidx){s+="<th ";if(typeof column.style!="undefined"){s+=' style="';if(typeof column.style=="function"){s+=column.style(sheet,column,columnidx)}else{s+=column.style}s+='" '}s+=">";if(typeof column.title!="undefined"){if(typeof column.title=="function"){s+=column.title(sheet,column,columnidx)}else{s+=column.title}}s+="</th>"});s+="</tr>";s+="</thead>"}s+="<tbody>";if(data&&data.length>0){data.forEach(function(row,rowidx){if(rowidx>sheet.limit)return;s+="<tr";var srow={};extend(srow,sheet.row);if(sheet.rows&&sheet.rows[rowidx]){extend(srow,sheet.rows[rowidx])}if(typeof srow!="undefined"){if(typeof srow.style!="undefined"){s+=' style="';if(typeof srow.style=="function"){s+=srow.style(sheet,row,rowidx)}else{s+=srow.style}s+='" '}}s+=">";columns.forEach(function(column,columnidx){var cell={};extend(cell,sheet.cell);extend(cell,srow.cell);if(typeof sheet.column!="undefined"){extend(cell,sheet.column.cell)}extend(cell,column.cell);if(sheet.cells&&sheet.cells[rowidx]&&sheet.cells[rowidx][columnidx]){extend(cell,sheet.cells[rowidx][columnidx])}var value=row[column.columnid];if(typeof cell.value=="function"){value=cell.value(value,sheet,row,column,cell,rowidx,columnidx)}var typeid=cell.typeid;if(typeof typeid=="function"){typeid=typeid(value,sheet,row,column,cell,rowidx,columnidx)}if(typeof typeid=="undefined"){if(typeof value=="number")typeid="number";else if(typeof value=="string")typeid="string";else if(typeof value=="boolean")typeid="boolean";else if(typeof value=="object"){if(value instanceof Date)typeid="date"}}var typestyle="";if(typeid=="money"){typestyle='mso-number-format:"\\#\\,\\#\\#0\\\\ _р_\\.";white-space:normal;'}else if(typeid=="number"){typestyle=" "}else if(typeid=="date"){typestyle='mso-number-format:"Short Date";'}else{if(opts.types&&opts.types[typeid]&&opts.types[typeid].typestyle){typestyle=opts.types[typeid].typestyle}}typestyle=typestyle||'mso-number-format:"\\@";';s+="<td style='"+typestyle+"' ";if(typeof cell.style!="undefined"){s+=' style="';if(typeof cell.style=="function"){s+=cell.style(value,sheet,row,column,rowidx,columnidx)}else{s+=cell.style}s+='" '}s+=">";var format=cell.format;if(typeof value=="undefined"){s+=""}else if(typeof format!="undefined"){if(typeof format=="function"){s+=format(value)}else if(typeof format=="string"){s+=value}else{throw new Error("Unknown format type. Should be function or string")}}else{if(typeid=="number"||typeid=="date"){s+=value.toString()}else if(typeid=="money"){s+=(+value).toFixed(2)}else{s+=value}}s+="</td>"});s+="</tr>"})}s+="</tbody>";s+="</table>";s+="</body>";s+="</html>";return s}function style(a){var s=' style="';if(a&&typeof a.style!="undefined"){s+=a.style+";"}s+='" ';return s}};alasql.into.XLSXML=function(filename,opts,data,columns,cb){if(typeof filename=="object"){opts=filename;filename=undefined}var sheets={};if(opts&&opts.sheets){sheets=opts.sheets}else{sheets.Sheet1=opts}var res=alasql.utils.saveFile(filename,toXML());if(cb)res=cb(res);return res;function toXML(){var s1='<?xml version="1.0"?> 		<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" 		 xmlns:o="urn:schemas-microsoft-com:office:office" 		 xmlns:x="urn:schemas-microsoft-com:office:excel" 		 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" 		 xmlns:html="http://www.w3.org/TR/REC-html40"> 		 <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"> 		 </DocumentProperties> 		 <OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office"> 		  <AllowPNG/> 		 </OfficeDocumentSettings> 		 <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"> 		  <ActiveSheet>0</ActiveSheet> 		 </ExcelWorkbook> 		 <Styles> 		  <Style ss:ID="Default" ss:Name="Normal"> 		   <Alignment ss:Vertical="Bottom"/> 		   <Borders/> 		   <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/> 		   <Interior/> 		   <NumberFormat/> 		   <Protection/> 		  </Style>';var s2="";var s3=" </Styles>";var styles={};var stylesn=62;function hstyle(st){var s="";for(var key in st){s+="<"+key;for(var attr in st[key]){s+=" ";if(attr.substr(0,2)=="x:"){s+=attr}else{s+="ss:"}s+=attr+'="'+st[key][attr]+'"'}s+="/>"}var hh=hash(s);if(styles[hh]){}else{styles[hh]={styleid:stylesn};s2+='<Style ss:ID="s'+stylesn+'">';s2+=s;s2+="</Style>";stylesn++}return"s"+styles[hh].styleid}for(var sheetid in sheets){var sheet=sheets[sheetid];if(typeof sheet.columns!="undefined"){columns=sheet.columns}else{if(columns.length==0&&data.length>0){if(typeof data[0]=="object"){if(data[0]instanceof Array){columns=data[0].map(function(d,columnidx){return{columnid:columnidx}})}else{columns=Object.keys(data[0]).map(function(columnid){return{columnid:columnid}})}}}}columns.forEach(function(column,columnidx){if(typeof sheet.column!="undefined"){extend(column,sheet.column)}if(typeof column.width=="undefined"){if(sheet.column&&typeof sheet.column.width!="undefined"){column.width=sheet.column.width}else{column.width=120}}if(typeof column.width=="number")column.width=column.width;if(typeof column.columnid=="undefined")column.columnid=columnidx;if(typeof column.title=="undefined")column.title=""+column.columnid;if(sheet.headers&&sheet.headers instanceof Array)column.title=sheet.headers[idx]});s3+='<Worksheet ss:Name="'+sheetid+'"> 	  			<Table ss:ExpandedColumnCount="'+columns.length+'" ss:ExpandedRowCount="'+((sheet.headers?1:0)+Math.min(data.length,sheet.limit||data.length))+'" x:FullColumns="1" 	   			x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="15">';columns.forEach(function(column,columnidx){s3+='<Column ss:Index="'+(columnidx+1)+'" ss:AutoFitWidth="0" ss:Width="'+column.width+'"/>'});if(sheet.headers){s3+='<Row ss:AutoFitHeight="0">';columns.forEach(function(column,columnidx){s3+="<Cell ";if(typeof column.style!="undefined"){var st={};if(typeof column.style=="function"){extend(st,column.style(sheet,column,columnidx))}else{extend(st,column.style)}s3+='ss:StyleID="'+hstyle(st)+'"'}s3+='><Data ss:Type="String">';if(typeof column.title!="undefined"){if(typeof column.title=="function"){s3+=column.title(sheet,column,columnidx)}else{s3+=column.title}}s3+="</Data></Cell>"});s3+="</Row>"}if(data&&data.length>0){data.forEach(function(row,rowidx){if(rowidx>sheet.limit)return;var srow={};extend(srow,sheet.row);if(sheet.rows&&sheet.rows[rowidx]){extend(srow,sheet.rows[rowidx])}s3+="<Row ";if(typeof srow!="undefined"){var st={};if(typeof srow.style!="undefined"){if(typeof srow.style=="function"){extend(st,srow.style(sheet,row,rowidx))}else{extend(st,srow.style)}s3+='ss:StyleID="'+hstyle(st)+'"'}}s3+=">";columns.forEach(function(column,columnidx){var cell={};extend(cell,sheet.cell);extend(cell,srow.cell);if(typeof sheet.column!="undefined"){extend(cell,sheet.column.cell)}extend(cell,column.cell);if(sheet.cells&&sheet.cells[rowidx]&&sheet.cells[rowidx][columnidx]){extend(cell,sheet.cells[rowidx][columnidx])}var value=row[column.columnid];if(typeof cell.value=="function"){value=cell.value(value,sheet,row,column,cell,rowidx,columnidx)}var typeid=cell.typeid;if(typeof typeid=="function"){typeid=typeid(value,sheet,row,column,cell,rowidx,columnidx)}if(typeof typeid=="undefined"){if(typeof value=="number")typeid="number";else if(typeof value=="string")typeid="string";else if(typeof value=="boolean")typeid="boolean";else if(typeof value=="object"){if(value instanceof Date)typeid="date"}}var Type="String";if(typeid=="number")Type="Number";else if(typeid=="date")Type="Date";var typestyle="";if(typeid=="money"){typestyle='mso-number-format:"\\#\\,\\#\\#0\\\\ _р_\\.";white-space:normal;'}else if(typeid=="number"){typestyle=" "}else if(typeid=="date"){typestyle='mso-number-format:"Short Date";'}else{if(opts.types&&opts.types[typeid]&&opts.types[typeid].typestyle){typestyle=opts.types[typeid].typestyle}}typestyle=typestyle||'mso-number-format:"\\@";';s3+="<Cell ";if(false){s+="<td style='"+typestyle+"' "}var st={};if(typeof cell.style!="undefined"){if(typeof cell.style=="function"){extend(st,cell.style(value,sheet,row,column,rowidx,columnidx))}else{extend(st,cell.style)}s3+='ss:StyleID="'+hstyle(st)+'"'}s3+=">";s3+='<Data ss:Type="'+Type+'">';var format=cell.format;if(typeof value=="undefined"){s3+=""}else if(typeof format!="undefined"){if(typeof format=="function"){s3+=format(value)}else if(typeof format=="string"){s3+=value}else{throw new Error("Unknown format type. Should be function or string")}}else{if(typeid=="number"||typeid=="date"){s3+=value.toString()}else if(typeid=="money"){s3+=(+value).toFixed(2)}else{s3+=value}}s3+="</Data></Cell>"});s3+="</Row>"})}s3+="</Table></Worksheet>"}s3+="</Workbook>";return s1+s2+s3}};alasql.into.XLSX=function(filename,opts,data,columns,cb){var res=1;if(deepEqual(columns,[{columnid:"_"}])){data=data.map(function(dat){return dat._});columns=undefined}else{}if(typeof exports=="object"){var XLSX=require("xlsx")}else{var XLSX=window.XLSX}if(typeof filename=="object"){opts=filename;filename=undefined}var wb={SheetNames:[],Sheets:{}};if(opts.sourcefilename){alasql.utils.loadBinaryFile(opts.sourcefilename,!!cb,function(data){wb=XLSX.read(data,{type:"binary"});doExport()})}else{doExport()}if(cb)res=cb(res);return res;function doExport(){if(typeof opts=="object"&&opts instanceof Array){if(data&&data.length>0){data.forEach(function(dat,idx){prepareSheet(opts[idx],dat,undefined,idx+1)})}}else{prepareSheet(opts,data,columns,{},1)}saveWorkbook(cb)}function prepareSheet(opts,data,columns,idx){var opt={sheetid:"Sheet"+idx,headers:true};alasql.utils.extend(opt,opts);if((!columns||columns.length==0)&&data.length>0){columns=Object.keys(data[0]).map(function(columnid){return{columnid:columnid}})}var cells={};if(wb.SheetNames.indexOf(opt.sheetid)>-1){cells=wb.Sheets[opt.sheetid]}else{wb.SheetNames.push(opt.sheetid);wb.Sheets[opt.sheetid]={};cells=wb.Sheets[opt.sheetid]}var range="A1";if(opt.range)range=opt.range;var col0=alasql.utils.xlscn(range.match(/[A-Z]+/)[0]);var row0=+range.match(/[0-9]+/)[0]-1;if(wb.Sheets[opt.sheetid]["!ref"]){var rangem=wb.Sheets[opt.sheetid]["!ref"];var colm=alasql.utils.xlscn(rangem.match(/[A-Z]+/)[0]);var rowm=+rangem.match(/[0-9]+/)[0]-1}else{var colm=1,rowm=1}var colmax=Math.max(col0+columns.length,colm);var rowmax=Math.max(row0+data.length+2,rowm);var i=row0+1;wb.Sheets[opt.sheetid]["!ref"]="A1:"+alasql.utils.xlsnc(colmax)+rowmax;if(opt.headers){columns.forEach(function(col,idx){cells[alasql.utils.xlsnc(col0+idx)+""+i]={v:col.columnid}});i++}for(var j=0;j<data.length;j++){columns.forEach(function(col,idx){var cell={v:data[j][col.columnid]};if(typeof data[j][col.columnid]=="number"){cell.t="n"}else if(typeof data[j][col.columnid]=="string"){cell.t="s"}else if(typeof data[j][col.columnid]=="boolean"){cell.t="b"}else if(typeof data[j][col.columnid]=="object"){if(data[j][col.columnid]instanceof Date){cell.t="d"}}cells[alasql.utils.xlsnc(col0+idx)+""+i]=cell});i++}}function saveWorkbook(cb){if(typeof filename=="undefined"){res=wb}else{if(typeof exports=="object"){XLSX.writeFile(wb,filename)}else{var wopts={bookType:"xlsx",bookSST:false,type:"binary"};var wbout=XLSX.write(wb,wopts);function s2ab(s){var buf=new ArrayBuffer(s.length);var view=new Uint8Array(buf);for(var i=0;i!=s.length;++i)view[i]=s.charCodeAt(i)&255;return buf}if(isIE()==9){throw new Error("Cannot save XLSX files in IE9. Please use XLS() export function")}else{saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}),filename)}}}}};alasql.ZipWriter=function(){var entries=[];function calculateCrc32(buf){var CRC_TABLE=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];var crc=~0;for(var ix=0;ix<buf.length;ix++){var offset=(crc^buf[ix])&255;crc=crc>>>8^CRC_TABLE[offset]}return crc^-1}function pack(items){var data=arguments,idx=0,buffer,bufferSize=0;items=items.split("");items.forEach(function(type){if(type=="v"){bufferSize+=2}else if(type=="V"||type=="l"){bufferSize+=4}});buffer=new Buffer(bufferSize);items.forEach(function(type,index){if(type=="v"){buffer.writeUInt16LE(data[index+1],idx);idx+=2}else if(type=="V"){buffer.writeUInt32LE(data[index+1],idx);idx+=4}else if(type=="l"){buffer.writeInt32LE(data[index+1],idx);idx+=4}});return buffer}function normalizeZipPath(zipPath){return zipPath.replace(/\\/g,"/").replace(/^\/|\/$/g,"")}this.addEntry=function(entry){entry.path=normalizeZipPath(entry.path);entries.push(entry)};this.addDir=function(zipPath,localPath){this.addEntry({path:zipPath,file:localPath,isFile:false})};this.addData=function(zipPath,data){if(!Buffer.isBuffer(data)){data=new Buffer(data)}this.addEntry({path:zipPath,data:data})};function buildZipBuffer(){var buffers=[];var length=0;function append(buf){if(typeof buf=="string"){buf=new Buffer(buf)}buffers.push(buf);length+=buf.length}entries.forEach(function(entry){var mtime,mdate;entry.extra=entry.extra||"";entry.comment=entry.comment||"";entry.cmethod=0;mtime=entry.mtime;mdate=entry.mdate;if(entry.mtime instanceof Date){mtime=entry.mtime.getHours();mtime=mtime<<6;mtime=mtime|entry.mtime.getMinutes();mtime=mtime<<5;mtime=mtime|entry.mtime.getSeconds()/2;mdate=entry.mtime.getFullYear()-1980;mdate=mdate<<4;mdate=mdate|entry.mtime.getMonth()+1;mdate=mdate<<5;mdate=mdate|entry.mtime.getDate()}var fileName=entry.path;if(!entry.isFile&&fileName.charAt(fileName.length-1)!="/"){entry.path=fileName=fileName+"/"}entry.offset=length;append(pack("VvvvvvlVVvv",67324752,20,2,entry.cmethod,mtime,mdate,entry.crc32,entry.csize,entry.size,fileName.length,entry.extra.length));append(fileName);append(entry.extra);append(entry.data);entry.mtime=mtime;entry.mdate=mdate;entry.eattr=entry.isFile?32:48});var startOffset=length;var centralDirSize=0;entries.forEach(function(entry){append(pack("VvvvvvvlVVvvvvvVV",33639248,20,20,2,entry.cmethod,entry.mtime,entry.mdate,entry.crc32,entry.csize,entry.size,entry.path.length,entry.extra.length,entry.comment.length,0,0,entry.eattr,entry.offset));append(entry.path);append(entry.extra);append(entry.comment);centralDirSize+=46+entry.path.length+entry.extra.length+entry.comment.length});var comment="";append(pack("VvvvvVVv",101010256,0,0,entries.length,entries.length,centralDirSize,startOffset,comment.length));append(comment);var buffer=Buffer.concat(buffers);return buffer}this.toBuffer=function(callback){var index=-1;function deflateNext(){var data;var entry=entries[++index];if(entry){entry.csize=entry.size=entry.crc32=0;data=entry.data=entry.data?entry.data:new Buffer(0);entry.mtime=new Date;if(entry.isFile!==false){entry.crc32=calculateCrc32(data);entry.isFile=true;entry.size=data.length;entry.csize=entry.size;deflateNext()}else{deflateNext()}}else{callback(buildZipBuffer())}}deflateNext()}};alasql.from.METEOR=function(filename,opts,cb,idx,query){var res=filename.find(opts).fetch();if(cb)res=cb(res,idx,query);return res};alasql.from.TABLETOP=function(key,opts,cb,idx,query){var res=[];var opt={headers:true,simpleSheet:true,key:key};alasql.utils.extend(opt,opts);opt.callback=function(data){res=data;if(cb)res=cb(res,idx,query)};Tabletop.init(opt);return res};alasql.from.HTML=function(selector,opts,cb,idx,query){var opt={};alasql.utils.extend(opt,opts);var sel=document.querySelector(selector);if(!sel&&sel.tagName!="TABLE"){throw new Error("Selected HTML element is not a TABLE")}var res=[];var headers=opt.headers;if(headers&&!(headers instanceof Array)){headers=[];var ths=sel.querySelector("thead tr").children;for(var i=0;i<ths.length;i++){if(!(ths.item(i).style&&ths.item(i).style.display=="none"&&opt.skipdisplaynone)){headers.push(ths.item(i).textContent)}else{headers.push(undefined)}}}var trs=sel.querySelectorAll("tbody tr");for(var j=0;j<trs.length;j++){var tds=trs.item(j).children;var r={};for(var i=0;i<tds.length;i++){if(!(tds.item(i).style&&tds.item(i).style.display=="none"&&opt.skipdisplaynone)){if(headers){r[headers[i]]=tds.item(i).textContent}else{r[i]=tds.item(i).textContent}}}res.push(r)}if(cb)res=cb(res,idx,query);return res};alasql.from.RANGE=function(start,finish,cb,idx,query){var res=[];for(i=start;i<=finish;i++)res.push(i);if(cb)res=cb(res,idx,query);return res};alasql.from.FILE=function(filename,opts,cb,idx,query){if(typeof filename=="string"){fname=filename}else if(filename instanceof Event){fname=filename.target.files[0].name}else{throw new Error("Wrong usage of FILE() function")}var parts=fname.split(".");var ext=parts[parts.length-1].toUpperCase();if(alasql.from[ext]){return alasql.from[ext](filename,opts,cb,idx,query)}else{throw new Error("Cannot recognize file type for loading")}};alasql.from.JSON=function(filename,opts,cb,idx,query){var res;alasql.utils.loadFile(filename,!!cb,function(data){res=JSON.parse(data);if(cb)res=cb(res,idx,query)});return res};alasql.from.TXT=function(filename,opts,cb,idx,query){var res;alasql.utils.loadFile(filename,!!cb,function(data){res=data.split(/\r?\n/);for(var i=0,ilen=res.length;i<ilen;i++){if(res[i]==+res[i])res[i]=+res[i];res[i]=[res[i]]}if(cb)res=cb(res,idx,query)});return res};alasql.from.TAB=alasql.from.TSV=function(filename,opts,cb,idx,query){if(!opts)opts={};opts.separator="	";return alasql.from.CSV(filename,opts,cb,idx,query)};alasql.from.CSV=function(filename,opts,cb,idx,query){var opt={separator:",",quote:'"'};alasql.utils.extend(opt,opts);var res;alasql.utils.loadFile(filename,!!cb,function(text){var delimiterCode=opt.separator.charCodeAt(0);var quoteCode=opt.quote.charCodeAt(0);var EOL={},EOF={},rows=[],N=text.length,I=0,n=0,t,eol;function token(){if(I>=N)return EOF;if(eol)return eol=false,EOL;var j=I;if(text.charCodeAt(j)===quoteCode){var i=j;while(i++<N){if(text.charCodeAt(i)===quoteCode){if(text.charCodeAt(i+1)!==quoteCode)break;++i}}I=i+2;var c=text.charCodeAt(i+1);if(c===13){eol=true;if(text.charCodeAt(i+2)===10)++I}else if(c===10){eol=true}return text.substring(j+1,i).replace(/""/g,'"')}while(I<N){var c=text.charCodeAt(I++),k=1;if(c===10)eol=true;else if(c===13){eol=true;if(text.charCodeAt(I)===10)++I,++k}else if(c!==delimiterCode)continue;return text.substring(j,I-k)}return text.substring(j)}while((t=token())!==EOF){var a=[];while(t!==EOL&&t!==EOF){a.push(t);t=token()}if(opt.headers){if(n==0){if(typeof opt.headers=="boolean"){hs=a}else if(opt.headers instanceof Array){hs=opt.headers;var r={};hs.forEach(function(h,idx){r[h]=a[idx];if(typeof r[h]!="undefined"&&r[h].trim()==+r[h])r[h]=+r[h]});rows.push(r)}}else{var r={};hs.forEach(function(h,idx){r[h]=a[idx];if(typeof r[h]!="undefined"&&r[h].trim()==+r[h])r[h]=+r[h]});rows.push(r)}n++}else{rows.push(a)}}res=rows;if(opt.headers){if(query&&query.sources&&query.sources[idx]){var columns=query.sources[idx].columns=[];hs.forEach(function(h){columns.push({columnid:h})})}}if(cb)res=cb(res,idx,query)});return res};alasql.from.XLS=function(filename,opts,cb,idx,query){if(typeof exports==="object"){var X=require("xlsjs")}else{var X=window.XLS;if(!X){throw new Error("XLS library is not attached")}}return XLSXLSX(X,filename,opts,cb,idx,query)};alasql.from.XLSX=function(filename,opts,cb,idx,query){if(typeof exports==="object"){var X=require("xlsx")}else{var X=window.XLSX;if(!X){throw new Error("XLSX library is not attached")}}return XLSXLSX(X,filename,opts,cb,idx,query)};function XLSXLSX(X,filename,opts,cb,idx,query){var opt={};if(!opts)opts={};alasql.utils.extend(opt,opts);var res;alasql.utils.loadBinaryFile(filename,!!cb,function(data){var workbook=X.read(data,{type:"binary"});var sheetid;if(typeof opt.sheetid=="undefined"){sheetid=workbook.SheetNames[0]}else{sheetid=opt.sheetid}var range;if(typeof opt.range=="undefined"){range=workbook.Sheets[sheetid]["!ref"]}else{range=opt.range;if(workbook.Sheets[sheetid][range])range=workbook.Sheets[sheetid][range]}var rg=range.split(":");var col0=rg[0].match(/[A-Z]+/)[0];var row0=+rg[0].match(/[0-9]+/)[0];var col1=rg[1].match(/[A-Z]+/)[0];var row1=+rg[1].match(/[0-9]+/)[0];var hh={};for(var j=alasql.utils.xlscn(col0);j<=alasql.utils.xlscn(col1);j++){var col=alasql.utils.xlsnc(j);if(opt.headers){if(workbook.Sheets[sheetid][col+""+row0]){hh[col]=workbook.Sheets[sheetid][col+""+row0].v}else{hh[col]=col}}else{hh[col]=col}}var res=[];if(opt.headers)row0++;for(var i=row0;i<=row1;i++){var row={};for(var j=alasql.utils.xlscn(col0);j<=alasql.utils.xlscn(col1);j++){var col=alasql.utils.xlsnc(j);if(workbook.Sheets[sheetid][col+""+i]){row[hh[col]]=workbook.Sheets[sheetid][col+""+i].v}}res.push(row)}if(cb)res=cb(res,idx,query)},function(err){throw err});return res}alasql.from.XML=function(filename,opts,cb,idx,query){var res;alasql.utils.loadFile(filename,!!cb,function(data){res=xmlparse(data).root;if(cb)res=cb(res,idx,query)});return res};function xmlparse(xml){xml=xml.trim();xml=xml.replace(/<!--[\s\S]*?-->/g,"");return document();function document(){return{declaration:declaration(),root:tag()}}function declaration(){var m=match(/^<\?xml\s*/);if(!m)return;var node={attributes:{}};while(!(eos()||is("?>"))){var attr=attribute();if(!attr)return node;node.attributes[attr.name]=attr.value}match(/\?>\s*/);return node}function tag(){var m=match(/^<([\w-:.]+)\s*/);if(!m)return;var node={name:m[1],attributes:{},children:[]};while(!(eos()||is(">")||is("?>")||is("/>"))){var attr=attribute();if(!attr)return node;node.attributes[attr.name]=attr.value}if(match(/^\s*\/>\s*/)){return node}match(/\??>\s*/);node.content=content();var child;while(child=tag()){node.children.push(child)}match(/^<\/[\w-:.]+>\s*/);return node}function content(){var m=match(/^([^<]*)/);if(m)return m[1];return""}function attribute(){var m=match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/);if(!m)return;return{name:m[1],value:strip(m[2])}}function strip(val){return val.replace(/^['"]|['"]$/g,"")}function match(re){var m=xml.match(re);if(!m)return;xml=xml.slice(m[0].length);return m}function eos(){return 0==xml.length}function is(prefix){return 0==xml.indexOf(prefix)}}alasql.from.GEXF=function(filename,opts,cb,idx,query){var res;alasql("SEARCH FROM XML("+filename+")",[],function(data){res=data;console.log(res);if(cb)res=cb(res)});return res};yy.Help=function(params){return yy.extend(this,params)};yy.Help.prototype.toString=function(){var s=K("HELP");if(this.subject)s+=" "+L(this.subject);return s};helpdocs=[{command:"ALTER TABLE table RENAME TO table"},{command:"ALTER TABLE table ADD COLUMN column coldef"},{command:"ALTER TABLE table MODIFY COLUMN column coldef"},{command:"ALTER TABLE table RENAME COLUMN column TO column"},{command:"ALTER TABLE table DROP column"},{command:"ATTACH engine DATABASE database"},{command:"ASSERT value"},{command:"BEGIN [TRANSACTION]"},{command:"COMMIT [TRANSACTION]"},{command:"CREATE [engine] DATABASE [IF NOT EXISTS] database"},{command:"CREATE TABLE [IF NOT EXISTS] table (column definitions)"},{command:"DELETE FROM table [WHERE expression]"},{command:"DETACH DATABASE database"},{command:"DROP [engine] DATABASE [IF EXISTS] database"},{command:"DROP TABLE [IF EXISTS] table"},{command:"INSERT INTO table VALUES value,..."},{command:"INSERT INTO table DEFAULT VALUES"},{command:"INSERT INTO table SELECT select"},{command:"HELP [subject]"},{command:"ROLLBACK [TRANSACTION]"},{command:"SELECT [modificator] columns [INTO table] [FROM table,...] [[mode] JOIN [ON] [USING]] [WHERE ] [GROUP BY] [HAVING] [ORDER BY] "},{command:"SET option value"},{command:"SHOW [engine] DATABASES"},{command:"SHOW TABLES"},{command:"SHOW CREATE TABLE table"},{command:"UPDATE table SET column1 = expression1, ... [WHERE expression]"},{command:"USE [DATABASE] database"},{command:"expression"},{command:'See also <a href="http://github/agershun/alasq">http://github/agershun/alasq</a> for more information'}];yy.Help.prototype.execute=function(databaseid,params,cb){var ss=[];if(!this.subject){ss=helpdocs}else{ss.push('See also <a href="http://github/agershun/alasq">http://github/agershun/alasq</a> for more information')}if(cb)ss=cb(ss);return ss};yy.Print=function(params){return yy.extend(this,params)};yy.Print.prototype.toString=function(){var s=K("PRINT");if(this.statement)s+=" "+this.statement.toString();return s};yy.Print.prototype.execute=function(databaseid,params,cb){var self=this;var res=1;alasql.precompile(this,databaseid,params);if(this.exprs&&this.exprs.length>0){var rs=this.exprs.map(function(expr){var exprfn=new Function("params,alasql,p","var y;return "+expr.toJavaScript("({})","",null)).bind(self);var r=exprfn(params,alasql);return JSONtoString(r)});console.log.apply(console,rs)}else if(this.select){var r=this.select.execute(databaseid,params);console.log(JSONtoString(r))}else{console.log()}if(cb)res=cb(res);return res};yy.Source=function(params){return yy.extend(this,params)};yy.Source.prototype.toString=function(){var s=K("SOURCE");if(this.url)s+=" "+S("'"+this.url+"'");return s};yy.Source.prototype.execute=function(databaseid,params,cb){var res;loadFile(this.url,!!cb,function(data){res=alasql(data);if(cb)res=cb(res);return res},function(err){throw err});return res};yy.Require=function(params){return yy.extend(this,params)};yy.Require.prototype.toString=function(){var s=K("REQUIRE");if(this.paths&&this.paths.length>0){s+=this.paths.map(function(path){return path.toString()}).join(",")}if(this.plugins&&this.plugins.length>0){s+=this.plugins.map(function(plugin){return plugin.toUpperCase()
-}).join(",")}return s};yy.Require.prototype.execute=function(databaseid,params,cb){var self=this;var res=0;var ss="";if(this.paths&&this.paths.length>0){this.paths.forEach(function(path){loadFile(path.value,!!cb,function(data){res++;ss+=data;if(res<self.paths.length)return;new Function("params,alasql",ss)(params,alasql);if(cb)res=cb(res)})})}else if(this.plugins&&this.plugins.length>0){this.plugins.forEach(function(plugin){if(!alasql.plugins[plugin]){loadFile(alasql.path+"/alasql-"+plugin.toLowerCase()+".js",!!cb,function(data){res++;ss+=data;if(res<self.plugins.length)return;new Function("params,alasql",ss)(params,alasql);alasql.plugins[plugin]=true;if(cb)res=cb(res)})}})}else{if(cb)res=cb(res)}return res};yy.Assert=function(params){return yy.extend(this,params)};yy.Source.prototype.toString=function(){var s=K("ASSERT");if(this.value)s+=" "+JSON.stringify(this.value);return s};yy.Assert.prototype.execute=function(databaseid){if(!deepEqual(alasql.res,this.value)){throw new Error((this.message||"Assert wrong")+": "+JSON.stringify(alasql.res)+" == "+JSON.stringify(this.value))}return 1};var WEBSQL=alasql.engines.WEBSQL=function(){};WEBSQL.createDatabase=function(wdbid,args,dbid,cb){var res=1;var wdb=openDatabase(wdbid,args[0],args[1],args[2]);if(this.dbid){var db=alasql.createDatabase(this.dbid);db.engineid="WEBSQL";db.wdbid=wdbid;sb.wdb=db}if(!wdb){throw new Error('Cannot create WebSQL database "'+databaseid+'"')}if(cb)cb(res);return res};WEBSQL.dropDatabase=function(databaseid){throw new Error("This is impossible to drop WebSQL database.")};WEBSQL.attachDatabase=function(databaseid,dbid,args,params,cb){var res=1;if(alasql.databases[dbid]){throw new Error('Unable to attach database as "'+dbid+'" because it already exists')}alasqlopenDatabase(databaseid,args[0],args[1],args[2]);return res};if(typeof window!="undefined"&&typeof window.indexedDB!="undefined"){var IDB=alasql.engines.INDEXEDDB=function(){};if(typeof window.indexedDB.webkitGetDatabaseNames=="function"){IDB.getDatabaseNames=window.indexedDB.webkitGetDatabaseNames.bind(window.indexedDB)}else{IDB.getDatabaseNames=function(){var request={};var result={contains:function(name){return true},notsupported:true};setTimeout(function(){var event={target:{result:result}};request.onsuccess(event)},0);return request};IDB.getDatabaseNamesNotSupported=true}IDB.showDatabases=function(like,cb){var request=IDB.getDatabaseNames();request.onsuccess=function(event){var dblist=event.target.result;if(IDB.getDatabaseNamesNotSupported){throw new Error("SHOW DATABASE is not supported in this browser")}var res=[];if(like){var relike=new RegExp(like.value.replace(/\%/g,".*"),"g")}for(var i=0;i<dblist.length;i++){if(!like||dblist[i].match(relike)){res.push({databaseid:dblist[i]})}}cb(res)}};IDB.createDatabase=function(ixdbid,args,ifnotexists,dbid,cb){console.log(arguments);if(ifnotexists){var request2=window.indexedDB.open(ixdbid,1);request2.onsuccess=function(event){event.target.result.close();cb(1)}}else{var request1=window.indexedDB.open(ixdbid,1);request1.onupgradeneeded=function(e){console.log("abort");e.target.transaction.abort()};request1.onsuccess=function(e){console.log("success");if(ifnotexists){cb(0)}else{throw new Error('IndexedDB: Cannot create new database "'+ixdbid+'" because it already exists')}}}};IDB.createDatabase=function(ixdbid,args,ifnotexists,dbid,cb){if(IDB.getDatabaseNamesNotSupported){if(ifnotexists){var dbExists=true;var request2=window.indexedDB.open(ixdbid);request2.onupgradeneeded=function(e){dbExists=false};request2.onsuccess=function(event){event.target.result.close();if(dbExists){cb(0)}else{cb(1)}}}else{var request1=window.indexedDB.open(ixdbid);request1.onupgradeneeded=function(e){e.target.transaction.abort()};request1.onabort=function(event){cb(1)};request1.onsuccess=function(event){event.target.result.close();throw new Error('IndexedDB: Cannot create new database "'+ixdbid+'" because it already exists')}}}else{var request1=IDB.getDatabaseNames();request1.onsuccess=function(event){var dblist=event.target.result;if(dblist.contains(ixdbid)){if(ifnotexists){cb(0);return}else{throw new Error('IndexedDB: Cannot create new database "'+ixdbid+'" because it already exists')}}var request2=window.indexedDB.open(ixdbid,1);request2.onsuccess=function(event){event.target.result.close();cb(1)}}}};IDB.dropDatabase=function(ixdbid,ifexists,cb){var request1=IDB.getDatabaseNames();request1.onsuccess=function(event){var dblist=event.target.result;if(!dblist.contains(ixdbid)){if(ifexists){cb(0);return}else{throw new Error('IndexedDB: Cannot drop new database "'+ixdbid+'" because it does not exist')}}var request2=window.indexedDB.deleteDatabase(ixdbid);request2.onsuccess=function(event){if(cb)cb(1)}}};IDB.attachDatabase=function(ixdbid,dbid,args,params,cb){var request1=IDB.getDatabaseNames();request1.onsuccess=function(event){var dblist=event.target.result;if(!dblist.contains(ixdbid)){throw new Error('IndexedDB: Cannot attach database "'+ixdbid+'" because it does not exist')}var request2=window.indexedDB.open(ixdbid);request2.onsuccess=function(event){var ixdb=event.target.result;var db=new alasql.Database(dbid||ixdbid);db.engineid="INDEXEDDB";db.ixdbid=ixdbid;db.tables=[];var tblist=ixdb.objectStoreNames;for(var i=0;i<tblist.length;i++){db.tables[tblist[i]]={}}event.target.result.close();cb(1)}}};IDB.createTable=function(databaseid,tableid,ifnotexists,cb){var ixdbid=alasql.databases[databaseid].ixdbid;var request1=IDB.getDatabaseNames();request1.onsuccess=function(event){var dblist=event.target.result;if(!dblist.contains(ixdbid)){throw new Error('IndexedDB: Cannot create table in database "'+ixdbid+'" because it does not exist')}var request2=window.indexedDB.open(ixdbid);request2.onversionchange=function(event){event.target.result.close()};request2.onsuccess=function(event){var version=event.target.result.version;event.target.result.close();var request3=window.indexedDB.open(ixdbid,version+1);request3.onupgradeneeded=function(event){var ixdb=event.target.result;var store=ixdb.createObjectStore(tableid,{autoIncrement:true})};request3.onsuccess=function(event){event.target.result.close();cb(1)};request3.onerror=function(event){throw event};request3.onblocked=function(event){throw new Error('Cannot create table "'+tableid+'" because database "'+databaseid+'"  is blocked')}}}};IDB.dropTable=function(databaseid,tableid,ifexists,cb){var ixdbid=alasql.databases[databaseid].ixdbid;var request1=IDB.getDatabaseNames();request1.onsuccess=function(event){var dblist=event.target.result;if(!dblist.contains(ixdbid)){throw new Error('IndexedDB: Cannot drop table in database "'+ixdbid+'" because it does not exist')}var request2=window.indexedDB.open(ixdbid);request2.onversionchange=function(event){event.target.result.close()};request2.onsuccess=function(event){var version=event.target.result.version;event.target.result.close();var request3=window.indexedDB.open(ixdbid,version+1);request3.onupgradeneeded=function(event){var ixdb=event.target.result;if(ixdb.objectStoreNames.contains(tableid)){ixdb.deleteObjectStore(tableid);delete alasql.databases[databaseid].tables[tableid]}else{if(!ifexists){throw new Error('IndexedDB: Cannot drop table "'+tableid+'" because it is not exist')}}};request3.onsuccess=function(event){event.target.result.close();cb(1)};request3.onerror=function(event){throw event};request3.onblocked=function(event){throw new Error('Cannot drop table "'+tableid+'" because database "'+databaseid+'" is blocked')}}}};IDB.intoTable=function(databaseid,tableid,value,columns,cb){var ixdbid=alasql.databases[databaseid].ixdbid;var request1=window.indexedDB.open(ixdbid);request1.onsuccess=function(event){var ixdb=event.target.result;var tx=ixdb.transaction([tableid],"readwrite");var tb=tx.objectStore(tableid);for(var i=0,ilen=value.length;i<ilen;i++){tb.add(value[i])}tx.oncomplete=function(){ixdb.close();cb(ilen)}}};IDB.fromTable=function(databaseid,tableid,cb,idx,query){var ixdbid=alasql.databases[databaseid].ixdbid;var request=window.indexedDB.open(ixdbid);request.onsuccess=function(event){var res=[];var ixdb=event.target.result;var tx=ixdb.transaction([tableid]);var store=tx.objectStore(tableid);var cur=store.openCursor();cur.onblocked=function(event){};cur.onerror=function(event){};cur.onsuccess=function(event){var cursor=event.target.result;if(cursor){res.push(cursor.value);cursor.continue()}else{ixdb.close();cb(res,idx,query)}}}};IDB.deleteFromTable=function(databaseid,tableid,wherefn,params,cb){var ixdbid=alasql.databases[databaseid].ixdbid;var request=window.indexedDB.open(ixdbid);request.onsuccess=function(event){var res=[];var ixdb=event.target.result;var tx=ixdb.transaction([tableid],"readwrite");var store=tx.objectStore(tableid);var cur=store.openCursor();var num=0;cur.onblocked=function(event){};cur.onerror=function(event){};cur.onsuccess=function(event){var cursor=event.target.result;if(cursor){if(!wherefn||wherefn(cursor.value,params)){cursor.delete();num++}cursor.continue()}else{ixdb.close();cb(num)}}}};IDB.updateTable=function(databaseid,tableid,assignfn,wherefn,params,cb){var ixdbid=alasql.databases[databaseid].ixdbid;var request=window.indexedDB.open(ixdbid);request.onsuccess=function(event){var res=[];var ixdb=event.target.result;var tx=ixdb.transaction([tableid],"readwrite");var store=tx.objectStore(tableid);var cur=store.openCursor();var num=0;cur.onblocked=function(event){};cur.onerror=function(event){};cur.onsuccess=function(event){var cursor=event.target.result;if(cursor){if(!wherefn||wherefn(cursor.value,params)){var r=cursor.value;assignfn(r,params);cursor.update(r);num++}cursor.continue()}else{ixdb.close();cb(num)}}}}}var LS=alasql.engines.LOCALSTORAGE=function(){};LS.get=function(key){var s=localStorage.getItem(key);if(typeof s=="undefined")return;var v=undefined;try{v=JSON.parse(s)}catch(err){throw new Error("Cannot parse JSON "+s)}return v};LS.set=function(key,value){if(typeof value=="undefined")localStorage.removeItem(key);else localStorage.setItem(key,JSON.stringify(value))};LS.createDatabase=function(lsdbid,args,ifnotexists,dbid,cb){var res=1;var ls=LS.get("alasql");if(!(ifnotexists&&ls&&ls.databases&&ls.databases[lsdbid])){if(!ls)ls={databases:{}};if(ls.databases&&ls.databases[lsdbid]){throw new Error('localStorage: Cannot create new database "'+lsdbid+'" because it already exists')}ls.databases[lsdbid]=true;LS.set("alasql",ls);LS.set(lsdbid,{databaseid:lsdbid,tables:{}})}else{res=0}if(cb)cb(res);return res};LS.dropDatabase=function(lsdbid,ifexists,cb){var res=1;var ls=LS.get("alasql");if(!(ifexists&&ls&&ls.databases&&!ls.databases[lsdbid])){if(!ls){if(!ifexists){throw new Error("There are no alasql databases in localStorage")}else{return 0}}if(ls.databases&&!ls.databases[lsdbid]){throw new Error('localStorage: Cannot drop database "'+lsdbid+'" because there is no such database')}delete ls.databases[lsdbid];LS.set("alasql",ls);var db=LS.get(lsdbid);for(var tableid in db.tables){localStorage.removeItem(lsdbid+"."+tableid)}localStorage.removeItem(lsdbid)}else{res=0}if(cb)cb(res);return res};LS.attachDatabase=function(lsdbid,dbid,args,params,cb){var res=1;if(alasql.databases[dbid]){throw new Error('Unable to attach database as "'+dbid+'" because it already exists')}var db=new alasql.Database(dbid||lsdbid);db.engineid="LOCALSTORAGE";db.lsdbid=lsdbid;db.tables=LS.get(lsdbid).tables;if(!alasql.options.autocommit){if(db.tables){for(var tbid in db.tables){db.tables[tbid].data=LS.get(db.lsdbid+"."+tbid)}}}if(cb)res=cb(res);return res};LS.showDatabases=function(like,cb){var res=[];var ls=LS.get("alasql");if(like){var relike=new RegExp(like.value.replace(/\%/g,".*"),"g")}if(ls&&ls.databases){for(dbid in ls.databases){res.push({databaseid:dbid})}if(like&&res&&res.length>0){res=res.filter(function(d){return d.databaseid.match(relike)})}}if(cb)cb(res);return res};LS.createTable=function(databaseid,tableid,ifnotexists,cb){var lsdbid=alasql.databases[databaseid].lsdbid;var tb=LS.get(lsdbid+"."+tableid);var res=1;if(tb&&!ifnotexists){throw new Error('Table "'+tableid+'" alsready exists in localStorage database "'+lsdbid+'"')}var lsdb=LS.get(lsdbid);var table=alasql.databases[databaseid].tables[tableid];lsdb.tables[tableid]={columns:table.columns};LS.set(lsdbid,lsdb);LS.set(lsdbid+"."+tableid,[]);if(cb)cb(res);return res};LS.dropTable=function(databaseid,tableid,ifexists,cb){var res=1;var lsdbid=alasql.databases[databaseid].lsdbid;if(alasql.options.autocommit){var lsdb=LS.get(lsdbid)}else{var lsdb=alasql.databases[databaseid]}if(!ifexists&&!lsdb.tables[tableid]){throw new Error('Cannot drop table "'+tableid+'" in localStorage, because it does not exist')}delete lsdb.tables[tableid];LS.set(lsdbid,lsdb);localStorage.removeItem(lsdbid+"."+tableid);if(cb)cb(res);return res};LS.fromTable=function(databaseid,tableid,cb,idx,query){var lsdbid=alasql.databases[databaseid].lsdbid;var res=LS.get(lsdbid+"."+tableid);if(cb)res=cb(res,idx,query);return res};LS.intoTable=function(databaseid,tableid,value,columns,cb){var lsdbid=alasql.databases[databaseid].lsdbid;var res=value.length;var tb=LS.get(lsdbid+"."+tableid);if(!tb)tb=[];tb=tb.concat(value);LS.set(lsdbid+"."+tableid,tb);if(cb)cb(res);return res};LS.loadTableData=function(databaseid,tableid){var db=alasql.databases[databaseid];var lsdbid=alasql.databases[databaseid].lsdbid;db.tables[tableid].data=LS.get(lsdbid+"."+tableid)};LS.saveTableData=function(databaseid,tableid){var db=alasql.databases[databaseid];var lsdbid=alasql.databases[databaseid].lsdbid;LS.set(lsdbid+"."+tableid,db.tables[tableid].data);db.tables[tableid].data=null};LS.commit=function(databaseid,cb){var db=alasql.databases[databaseid];var lsdbid=alasql.databases[databaseid].lsdbid;var lsdb={databaseid:lsdbid,tables:{}};if(db.tables){for(var tbid in db.tables){lsdb.tables[tbid]={columns:db.tables[tbid].columns};LS.set(lsdbid+"."+tbid,db.tables[tbid].data)}}LS.set(lsdbid,lsdb);return 1};LS.begin=LS.commit;LS.rollback=function(databaseid,cb){var db=alasql.databases[databaseid];db.dbversion++;var lsdbid=alasql.databases[databaseid].lsdbid;var lsdb=LS.get(lsdbid);if(lsdb.tables){for(var tbid in lsdb.tables){var tb=new alasql.Table({columns:db.tables[tbid].columns});extend(tb,lsdb.tables[tbid]);lsdb.tables[tbid]=tb;if(!alasql.options.autocommit){lsdb.tables[tbid].data=LS.get(db.lsdbid+"."+tbid)}lsdb.tables[tbid].indexColumns()}}delete alasql.databases[databaseid];alasql.databases[databaseid]=new alasql.Database(databaseid);extend(alasql.databases[databaseid],lsdb);alasql.databases[databaseid].databaseid=databaseid;alasql.databases[databaseid].engineid="LOCALSTORAGE"};var SQLITE=alasql.engines.SQLITE=function(){};SQLITE.createDatabase=function(wdbid,args,ifnotexists,dbid,cb){throw new Error("Connot create SQLITE database in memory. Attach it.")};SQLITE.dropDatabase=function(databaseid){throw new Error("This is impossible to drop SQLite database. Detach it.")};SQLITE.attachDatabase=function(sqldbid,dbid,args,params,cb){var res=1;if(alasql.databases[dbid]){throw new Error('Unable to attach database as "'+dbid+'" because it already exists')}if(args[0]&&args[0]instanceof yy.StringValue||args[0]instanceof yy.ParamValue){if(args[0]instanceof yy.StringValue){var value=args[0].value}else if(args[0]instanceof yy.ParamValue){var value=params[args[0].param]}alasql.utils.loadBinaryFile(value,true,function(data){var db=new alasql.Database(dbid||sqldbid);db.engineid="SQLITE";db.sqldbid=sqldbid;var sqldb=db.sqldb=new SQL.Database(data);db.tables=[];var tables=sqldb.exec("SELECT * FROM sqlite_master WHERE type='table'")[0].values;tables.forEach(function(tbl){db.tables[tbl[1]]={};var columns=db.tables[tbl[1]].columns=[];var ast=alasql.parse(tbl[4]);var coldefs=ast.statements[0].columns;if(coldefs&&coldefs.length>0){coldefs.forEach(function(cd){columns.push(cd)})}});cb(1)},function(err){throw new Error('Cannot open SQLite database file "'+args[0].value+'"')});return res}else{throw new Error("Cannot attach SQLite database without a file")}return res};SQLITE.fromTable=function(databaseid,tableid,cb,idx,query){var data=alasql.databases[databaseid].sqldb.exec("SELECT * FROM "+tableid);var columns=query.sources[idx].columns=[];if(data[0].columns.length>0){data[0].columns.forEach(function(columnid){columns.push({columnid:columnid})})}var res=[];if(data[0].values.length>0){data[0].values.forEach(function(d){var r={};columns.forEach(function(col,idx){r[col.columnid]=d[idx]});res.push(r)})}if(cb)cb(res,idx,query)};SQLITE.intoTable=function(databaseid,tableid,value,columns,cb){var sqldb=alasql.databases[databaseid].sqldb;for(var i=0,ilen=value.length;i<ilen;i++){var s="INSERT INTO "+tableid+" (";var d=value[i];var keys=Object.keys(d);s+=keys.join(",");s+=") VALUES (";s+=keys.map(function(k){v=d[k];if(typeof v=="string")v="'"+v+"'";return v}).join(",");s+=")";sqldb.exec(s)}var res=ilen;if(cb)cb(res);return res};var FS=alasql.engines.FILESTORAGE=alasql.engines.FILE=function(){};FS.createDatabase=function(fsdbid,args,ifnotexists,dbid,cb){var res=1;var filename=args[0].value;alasql.utils.fileExists(filename,function(fex){if(fex){if(ifnotexists){res=0;if(cb)res=cb(res);return res}else{throw new Error("Cannot create new database file, because it alreagy exists")}}else{var data={tables:{}};alasql.utils.saveFile(filename,JSON.stringify(data),function(data){if(cb)res=cb(res)})}});return res};FS.dropDatabase=function(fsdbid,ifexists,cb){var res;var filename=fsdbid.value;alasql.utils.fileExists(filename,function(fex){if(fex){res=1;alasql.utils.deleteFile(filename,function(){res=1;if(cb)res=cb(res)})}else{if(!ifexists){throw new Error("Cannot drop database file, because it does not exist")}res=0;if(cb)res=cb(res)}});return res};FS.attachDatabase=function(fsdbid,dbid,args,params,cb){var res=1;if(alasql.databases[dbid]){throw new Error('Unable to attach database as "'+dbid+'" because it already exists')}var db=new alasql.Database(dbid||fsdbid);db.engineid="FILESTORAGE";db.filename=args[0].value;loadFile(db.filename,!!cb,function(s){try{db.data=JSON.parse(s)}catch(err){throw new Error("Data in FileStorage database are corrupted")}db.tables=db.data.tables;if(!alasql.options.autocommit){if(db.tables){for(var tbid in db.tables){db.tables[tbid].data=db.data[tbid]}}}if(cb)res=cb(res)});return res};FS.createTable=function(databaseid,tableid,ifnotexists,cb){var db=alasql.databases[databaseid];var tb=db.data[tableid];var res=1;if(tb&&!ifnotexists){throw new Error('Table "'+tableid+'" alsready exists in the database "'+fsdbid+'"')}var table=alasql.databases[databaseid].tables[tableid];db.data.tables[tableid]={columns:table.columns};db.data[tableid]=[];FS.updateFile(databaseid);if(cb)cb(res);return res};FS.updateFile=function(databaseid){var db=alasql.databases[databaseid];if(db.issaving){db.postsave=true;return}db.issaving=true;db.postsave=false;alasql.utils.saveFile(db.filename,JSON.stringify(db.data),function(){db.issaving=false;if(db.postsave){setTimeout(function(){FS.updateFile(databaseid)},50)}})};FS.dropTable=function(databaseid,tableid,ifexists,cb){var res=1;var db=alasql.databases[databaseid];if(!ifexists&&!db.tables[tableid]){throw new Error('Cannot drop table "'+tableid+'" in fileStorage, because it does not exist')}delete db.tables[tableid];delete db.data.tables[tableid];delete db.data[tableid];FS.updateFile(databaseid);if(cb)cb(res);return res};FS.fromTable=function(databaseid,tableid,cb,idx,query){var db=alasql.databases[databaseid];var res=db.data[tableid];if(cb)res=cb(res,idx,query);return res};FS.intoTable=function(databaseid,tableid,value,columns,cb){var db=alasql.databases[databaseid];var res=value.length;var tb=db.data[tableid];if(!tb)tb=[];db.data[tableid]=tb.concat(value);FS.updateFile(databaseid);if(cb)cb(res);return res};FS.loadTableData=function(databaseid,tableid){var db=alasql.databases[databaseid];db.tables[tableid].data=db.data[tableid]};FS.saveTableData=function(databaseid,tableid){var db=alasql.databases[databaseid];db.data[tableid]=db.tables[tableid].data;db.tables[tableid].data=null;FS.updateFile(databaseid)};FS.commit=function(databaseid,cb){var db=alasql.databases[databaseid];var fsdb={tables:{}};if(db.tables){for(var tbid in db.tables){db.data.tables[tbid]={columns:db.tables[tbid].columns};db.data[tbid]=db.tables[tbid].data}}FS.updateFile(databaseid);return 1};FS.begin=FS.commit;FS.rollback=function(databaseid,cb){var res=1;var db=alasql.databases[databaseid];db.dbversion++;wait();function wait(){setTimeout(function(){if(db.issaving){return wait()}else{alasql.loadFile(db.filename,!!cb,function(data){db.data=data;db.tables={};for(var tbid in db.data.tables){var tb=new alasql.Table({columns:db.data.tables[tbid].columns});extend(tb,db.data.tables[tbid]);db.tables[tbid]=tb;if(!alasql.options.autocommit){db.tables[tbid].data=db.data[tbid]}db.tables[tbid].indexColumns()}delete alasql.databases[databaseid];alasql.databases[databaseid]=new alasql.Database(databaseid);extend(alasql.databases[databaseid],db);alasql.databases[databaseid].engineid="FILESTORAGE";alasql.databases[databaseid].filename=db.filename;if(cb)res=cb(res)})}},100)}};if(typeof exports!="object"&&typeof importScripts!="function"&&typeof document=="object"){var saveAs=saveAs||typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob&&navigator.msSaveOrOpenBlob.bind(navigator)||function(view){"use strict";if(typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var doc=view.document,get_URL=function(){return view.URL||view.webkitURL||view},save_link=doc.createElementNS("http://www.w3.org/1999/xhtml","a"),can_use_save_link="download"in save_link,click=function(node){var event=doc.createEvent("MouseEvents");event.initMouseEvent("click",true,false,view,0,0,0,0,0,false,false,false,false,0,null);node.dispatchEvent(event)},webkit_req_fs=view.webkitRequestFileSystem,req_fs=view.requestFileSystem||webkit_req_fs||view.mozRequestFileSystem,throw_outside=function(ex){(view.setImmediate||view.setTimeout)(function(){throw ex},0)},force_saveable_type="application/octet-stream",fs_min_size=0,arbitrary_revoke_timeout=500,revoke=function(file){var revoker=function(){if(typeof file==="string"){get_URL().revokeObjectURL(file)}else{file.remove()}};if(view.chrome){revoker()}else{setTimeout(revoker,arbitrary_revoke_timeout)}},dispatch=function(filesaver,event_types,event){event_types=[].concat(event_types);var i=event_types.length;while(i--){var listener=filesaver["on"+event_types[i]];if(typeof listener==="function"){try{listener.call(filesaver,event||filesaver)}catch(ex){throw_outside(ex)}}}},FileSaver=function(blob,name){var filesaver=this,type=blob.type,blob_changed=false,object_url,target_view,dispatch_all=function(){dispatch(filesaver,"writestart progress write writeend".split(" "))},fs_error=function(){if(blob_changed||!object_url){object_url=get_URL().createObjectURL(blob)}if(target_view){target_view.location.href=object_url}else{var new_tab=view.open(object_url,"_blank");if(new_tab==undefined&&typeof safari!=="undefined"){view.location.href=object_url}}filesaver.readyState=filesaver.DONE;dispatch_all();revoke(object_url)},abortable=function(func){return function(){if(filesaver.readyState!==filesaver.DONE){return func.apply(this,arguments)}}},create_if_not_found={create:true,exclusive:false},slice;filesaver.readyState=filesaver.INIT;if(!name){name="download"}if(can_use_save_link){object_url=get_URL().createObjectURL(blob);save_link.href=object_url;save_link.download=name;click(save_link);filesaver.readyState=filesaver.DONE;dispatch_all();revoke(object_url);return}if(/^\s*(?:text\/(?:plain|xml)|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)){blob=new Blob(["",blob],{type:blob.type})}if(view.chrome&&type&&type!==force_saveable_type){slice=blob.slice||blob.webkitSlice;blob=slice.call(blob,0,blob.size,force_saveable_type);blob_changed=true}if(webkit_req_fs&&name!=="download"){name+=".download"}if(type===force_saveable_type||webkit_req_fs){target_view=view}if(!req_fs){fs_error();return}fs_min_size+=blob.size;req_fs(view.TEMPORARY,fs_min_size,abortable(function(fs){fs.root.getDirectory("saved",create_if_not_found,abortable(function(dir){var save=function(){dir.getFile(name,create_if_not_found,abortable(function(file){file.createWriter(abortable(function(writer){writer.onwriteend=function(event){target_view.location.href=file.toURL();filesaver.readyState=filesaver.DONE;dispatch(filesaver,"writeend",event);revoke(file)};writer.onerror=function(){var error=writer.error;if(error.code!==error.ABORT_ERR){fs_error()}};"writestart progress write abort".split(" ").forEach(function(event){writer["on"+event]=filesaver["on"+event]});writer.write(blob);filesaver.abort=function(){writer.abort();filesaver.readyState=filesaver.DONE};filesaver.readyState=filesaver.WRITING}),fs_error)}),fs_error)};dir.getFile(name,{create:false},abortable(function(file){file.remove();save()}),abortable(function(ex){if(ex.code===ex.NOT_FOUND_ERR){save()}else{fs_error()}}))}),fs_error)}),fs_error)},FS_proto=FileSaver.prototype,saveAs=function(blob,name){return new FileSaver(blob,name)};FS_proto.abort=function(){var filesaver=this;filesaver.readyState=filesaver.DONE;dispatch(filesaver,"abort")};FS_proto.readyState=FS_proto.INIT=0;FS_proto.WRITING=1;FS_proto.DONE=2;FS_proto.error=FS_proto.onwritestart=FS_proto.onprogress=FS_proto.onwrite=FS_proto.onabort=FS_proto.onerror=FS_proto.onwriteend=null;return saveAs}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!=null){define([],function(){return saveAs})}alasql.utils.saveAs=saveAs}new Database("alasql");alasql.use("alasql");return alasql});if(typeof importScripts==="function"){}else if(typeof exports!="object"){alasql.worker=function(path,paths,cb){if(path===true)path=undefined;if(typeof path=="undefined"){var sc=document.getElementsByTagName("script");for(var i=0;i<sc.length;i++){if(sc[i].src.substr(-16).toLowerCase()=="alasql-worker.js"){path=sc[i].src.substr(0,sc[i].src.length-16)+"alasql.js";break}else if(sc[i].src.substr(-20).toLowerCase()=="alasql-worker.min.js"){path=sc[i].src.substr(0,sc[i].src.length-20)+"alasql.min.js";break}else if(sc[i].src.substr(-9).toLowerCase()=="alasql.js"){path=sc[i].src;break}else if(sc[i].src.substr(-13).toLowerCase()=="alasql.min.js"){path=sc[i].src.substr(0,sc[i].src.length-13)+"alasql.min.js";break}}}if(typeof path=="undefined"){throw new Error("Path to alasql.js is not specified")}else if(path!==false){var js="importScripts('";js+=path;js+="');self.onmessage = function(event) {"+"alasql(event.data.sql,event.data.params, function(data){"+"postMessage({id:event.data.id, data:data});});}";var blob=new Blob([js],{type:"text/plain"});alasql.webworker=new Worker(URL.createObjectURL(blob));alasql.webworker.onmessage=function(event){var id=event.data.id;alasql.buffer[id](event.data.data);delete alasql.buffer[id]};alasql.webworker.onerror=function(e){throw e};if(arguments.length>1){var sql="REQUIRE "+paths.map(function(p){return'"'+p+'"'}).join(",");alasql(sql,[],cb)}}else if(path===false){delete alasql.webworker;return}}}
\ No newline at end of file
diff --git a/datalets/base-ajax-json-alasql-datalet/static/js/jsonsql-0.1.js b/datalets/base-ajax-json-alasql-datalet/static/js/jsonsql-0.1.js
deleted file mode 100755
index 036ed23..0000000
--- a/datalets/base-ajax-json-alasql-datalet/static/js/jsonsql-0.1.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * JsonSQL
- * By: Trent Richardson [http://trentrichardson.com]
- * Version 0.1
- * Last Modified: 1/1/2008
- * 
- * Copyright 2008 Trent Richardson
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var jsonsql = {
-
-    query: function(sql,json){
-
-        var returnfields = sql.match(/^(select)\s+([a-z0-9_\,\.\s\*]+)\s+from\s+([a-z0-9_\.]+)(?: where\s+\((.+)\))?\s*(?:order\sby\s+([a-z0-9_\,]+))?\s*(asc|desc|ascnum|descnum)?\s*(?:limit\s+([0-9_\,]+))?/i);
-
-        var ops = {
-            fields: returnfields[2].replace(' ','').split(','),
-            from: returnfields[3].replace(' ',''),
-            where: (returnfields[4] == undefined)? "true":returnfields[4],
-            orderby: (returnfields[5] == undefined)? []:returnfields[5].replace(' ','').split(','),
-            order: (returnfields[6] == undefined)? "asc":returnfields[6],
-            limit: (returnfields[7] == undefined)? []:returnfields[7].replace(' ','').split(',')
-        };
-
-        return this.parse(json, ops);
-    },
-
-    parse: function(json,ops){
-        var o = { fields:["*"], from:"json", where:"", orderby:[], order: "asc", limit:[] };
-        for(i in ops) o[i] = ops[i];
-
-        var result = [];
-        result = this.returnFilter(json,o);
-        result = this.returnOrderBy(result,o.orderby,o.order);
-        result = this.returnLimit(result,o.limit);
-
-        return result;
-    },
-
-    returnFilter: function(json,jsonsql_o){
-
-        var jsonsql_scope = eval(jsonsql_o.from);
-        var jsonsql_result = [];
-        var jsonsql_rc = 0;
-
-        if(jsonsql_o.where == "")
-            jsonsql_o.where = "true";
-
-        for(var jsonsql_i in jsonsql_scope){
-            with(jsonsql_scope[jsonsql_i]){
-                if(eval(jsonsql_o.where)){
-                    jsonsql_result[jsonsql_rc++] = this.returnFields(jsonsql_scope[jsonsql_i],jsonsql_o.fields);
-                }
-            }
-        }
-
-        return jsonsql_result;
-    },
-
-    returnFields: function(scope,fields){
-        if(fields.length == 0)
-            fields = ["*"];
-
-        if(fields[0] == "*")
-            return scope;
-
-        var returnobj = {};
-        for(var i in fields)
-            returnobj[fields[i]] = scope[fields[i]];
-
-        return returnobj;
-    },
-
-    returnOrderBy: function(result,orderby,order){
-        if(orderby.length == 0)
-            return result;
-
-        result.sort(function(a,b){
-            switch(order.toLowerCase()){
-                case "desc": return (eval('a.'+ orderby[0] +' < b.'+ orderby[0]))? 1:-1;
-                case "asc":  return (eval('a.'+ orderby[0] +' > b.'+ orderby[0]))? 1:-1;
-                case "descnum": return (eval('a.'+ orderby[0] +' - b.'+ orderby[0]));
-                case "ascnum":  return (eval('b.'+ orderby[0] +' - a.'+ orderby[0]));
-            }
-        });
-
-        return result;
-    },
-
-    returnLimit: function(result,limit){
-        switch(limit.length){
-            case 0: return result;
-            case 1: return result.splice(0,limit[0]);
-            case 2: return result.splice(limit[0]-1,limit[1]);
-        }
-    }
-
-};
\ No newline at end of file
diff --git a/datalets/base-datalet/base-datalet.html b/datalets/base-datalet/base-datalet.html
index 4bc2c2e..8455306 100755
--- a/datalets/base-datalet/base-datalet.html
+++ b/datalets/base-datalet/base-datalet.html
@@ -339,7 +339,7 @@ Example :
 
                 //xhr.open('POST', 'http://172.16.15.77/DEEalerProvider/DEEP/export/export-datalet-as-img', true);
                 //xhr.open('POST', 'http://stage.routetopa.eu/deep/DEEP/export/export-datalet-as-img', true);
-                xhr.open('POST', 'http://deep.routetopa.eu/deep_1_5/DEEP/export/export-datalet-as-img', true);
+                xhr.open('POST', 'http://deep.routetopa.eu/deep_1_6/DEEP/export/export-datalet-as-img', true);
                 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
 
                 var post = 'svg_data=' + window.encodeURIComponent(svg);
@@ -348,16 +348,75 @@ Example :
             },
 
             exportRTF: function () {
+
+                var t = this;
                 var parent = this.parentElement;
                 while(!parent.hasAttribute("data-url"))
                     parent = parent.parentElement;
 
+                var post_id = parent.parentElement.id.match(/\d+/); post_id = post_id ? post_id[0] : "";
+                var datalet_id = $(parent).parents().eq(3).attr("datalet-id") ? $(parent).parents().eq(3).attr("datalet-id") : "";
+                var isPublicRoom = (typeof SPODPUBLICROOM != 'undefined') ? true : false;
 
                 var svg = $(parent).find(".highcharts-container");
                 svg = $(svg[0]).html();
 
                 var xhr = new XMLHttpRequest();
 
+                $.ajax({
+                    url: ODE.get_datalet_info,
+                    data: {"post_id" : post_id, "datalet_id" : datalet_id, "is_public_room" : isPublicRoom},
+                    dataType: "json",
+                    success: function(e){
+                        //xhr.open('POST', 'http://172.16.15.77/DEEalerProvider/DEEP/export/export-datalet-as-rtf', true);
+                        //xhr.open('POST', 'http://stage.routetopa.eu/deep/DEEP/export/export-datalet-as-rtf', true);
+                        xhr.open('POST', 'http://deep.routetopa.eu/deep_1_6/DEEP/export/export-datalet-as-rtf', true);
+                        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+
+                        var post = 'svg_data=' + window.encodeURIComponent(svg) + '&datalet=' + parent.is + '&dataset=' + t.$.base_datalet_link.getAttribute("href");
+                        post += '&user=' + e.user + '&timestamp=' + e.timestamp + '&comment=' + (e.comment ? e.comment : "");
+
+                        xhr.responseType = 'blob';
+
+                        //OpenDataSoft check
+                        if(t.dataUrl.indexOf("/records/") > -1)
+                        {
+                            var urlSource = t.dataUrl.split("/")[0] + "//" + t.dataUrl.split("/")[2];
+
+                            var i;
+                            if(t.dataUrl.indexOf("&") > -1)
+                                i = t.dataUrl.indexOf("&");
+                            else
+                                i = t.dataUrl.length;
+
+                            $.ajax({
+                                url: urlSource + "/api/dataset/1.0/" + t.dataUrl.substring(t.dataUrl.indexOf("=")+1, i),
+                                dataType: "json",
+                                success: function(e){
+                                    post += '&name=' + e.metas.title + '&description=' + e.metas.description + '&created=' + e.metas.data_processed  + '&lastModified=' + e.metas.modified;
+                                    xhr.send(post);
+                                }
+                            });
+                        }
+                        else if(t.dataUrl.indexOf("datastore_search?resource_id") > -1 )
+                        {
+                            $.ajax({
+                                url: t.dataUrl.replace("datastore_search?resource_id", "resource_show?id"),
+                                dataType: "json",
+                                success: function(e){
+                                    post += '&name=' + e.result.name + '&description=' + e.result.description + '&created=' + e.result.created + '&format=' + e.result.format + '&lastModified=' + e.result.last_modified;
+                                    xhr.send(post);
+                                }
+                            });
+                        }
+                        else
+                        {
+                            xhr.send(post);
+                        }
+
+                    }
+                });
+
                 xhr.onreadystatechange = function(){
                     if (this.readyState == 4 && this.status == 200){
                         var blob = new Blob([this.response], {type:'octet/stream'});
@@ -370,51 +429,6 @@ Example :
                         //window.URL.revokeObjectURL(downloadUrl);
                     }
                 };
-
-                //xhr.open('POST', 'http://172.16.15.77/DEEalerProvider/DEEP/export/export-datalet-as-rtf', true);
-                //xhr.open('POST', 'http://stage.routetopa.eu/deep/DEEP/export/export-datalet-as-rtf', true);
-                xhr.open('POST', 'http://deep.routetopa.eu/deep_1_5/DEEP/export/export-datalet-as-rtf', true);
-                xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
-
-                var post = 'svg_data=' + window.encodeURIComponent(svg) + '&datalet=' + parent.is + '&dataset=' + this.$.base_datalet_link.getAttribute("href");
-                xhr.responseType = 'blob';
-
-                //OpenDataSoft check
-                if(this.dataUrl.indexOf("/records/") > -1 )
-                {
-                    var urlSource = this.dataUrl.split("/")[0] + "//" + this.dataUrl.split("/")[2];
-
-                    var i;
-                    if(this.dataUrl.indexOf("&") > -1)
-                        i = this.dataUrl.indexOf("&");
-                    else
-                        i = this.dataUrl.length;
-
-                    $.ajax({
-                        url: urlSource + "/api/dataset/1.0/" + this.dataUrl.substring(this.dataUrl.indexOf("=")+1, i),
-                        dataType: "json",
-                        success: function(e){
-                            post += '&name=' + e.metas.title + '&description=' + e.metas.description + '&created=' + e.metas.data_processed  + '&lastModified=' + e.metas.modified;
-                            xhr.send(post);
-                        }
-                    });
-                }
-                else if(this.dataUrl.indexOf("datastore_search?resource_id") > -1 )
-                {
-                    $.ajax({
-                        url: this.dataUrl.replace("datastore_search?resource_id", "resource_show?id"),
-                        dataType: "json",
-                        success: function(e){
-                            post += '&name=' + e.result.name + '&description=' + e.result.description + '&created=' + e.result.created + '&format=' + e.result.format + '&lastModified=' + e.result.last_modified;
-                            xhr.send(post);
-                        }
-                    });
-                }
-                else
-                {
-                    xhr.send(post);
-                }
-
             },
 
             importToMySpace: function() {
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/datasetexplorer-datalet/datasetexplorer-datalet.html b/datalets/datasetexplorer-datalet/datasetexplorer-datalet.html
index f96a7cb..3e309ad 100755
--- a/datalets/datasetexplorer-datalet/datasetexplorer-datalet.html
+++ b/datalets/datasetexplorer-datalet/datasetexplorer-datalet.html
@@ -143,6 +143,8 @@ Example:
 
             transformData: function()
             {
+                this._filterData();
+
                 var treemapData = [];
                 DatasetexplorerBehavior.map.name = this._component.title;
 
@@ -212,7 +214,7 @@ Example:
                 this._component.fire("datasetexplorer-datalet_data-url", { url: url });
             },
 
-            filterData : function() {
+            _filterData : function() {
 
                 this.meta = this.data.result.providers;
                 this._component.fields = JSON.parse(this._component.fields);
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 :
     </template>
 
     <script src="static/js/highcharts.js"></script>
-    <!--<script src="static/js/exporting.js"></script>-->
     <script src="static/js/highcharts-more.js"></script>
     <script src="static/themes/themes.js"></script>
 
@@ -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 + '<br/><br/><span style="fill:#7cb5ec" x="8" dy="15">●</span> ' + this.series.name + ': <b>' + this.y + ' ' + params['suffix'] + '</b>';
-//                }
-//
-//                //theme default
-//                Highcharts.setOptions(Highcharts[params['theme']]);
-//
-//                this._component.stack = params['stack'];
-//            }
+            }
 
         };
 
diff --git a/locales/controllet_ln.js b/locales/controllet_ln.js
index e66ad5e..669fadb 100755
--- a/locales/controllet_ln.js
+++ b/locales/controllet_ln.js
@@ -47,6 +47,13 @@ ln["warning_en"] = "WARNING";
 ln["filterField_en"] = "Field";
 ln["filterOperation_en"] = "Operation";
 ln["filterValue_en"] = "Value";
+
+ln["=_en"] = "is equal to";
+ln["!=_en"] = "is different from";
+ln[">_en"] = "is greater than";
+ln[">=_en"] = "is greater than or equal to";
+ln["<_en"] = "is less than";
+ln["<=_en"] = "is less than or equal to";
 ln["contains_en"] = "contains";
 ln["notContains_en"] = "not contains";
 ln["start_en"] = "start with";
@@ -173,6 +180,13 @@ ln["warning_it"] = "ATTENZIONE";
 ln["filterField_it"] = "Campo";
 ln["filterOperation_it"] = "Operazione";
 ln["filterValue_it"] = "Valore";
+
+ln["=_it"] = "è uguale a";
+ln["!=_it"] = "è diverso da";
+ln[">_it"] = "è maggiore di";
+ln[">=_it"] = "è maggiore uguale di";
+ln["<_it"] = "è minore di";
+ln["<=_it"] = "è minore uguale di";
 ln["contains_it"] = "contiene";
 ln["notContains_it"] = "non contiene";
 ln["start_it"] = "inizia con";
@@ -299,6 +313,13 @@ ln["warning_fr"] = "WARNING";
 ln["filterField_fr"] = "Field";
 ln["filterOperation_fr"] = "Operation";
 ln["filterValue_fr"] = "Value";
+
+ln["=_fr"] = "is equal to";
+ln["!=_fr"] = "is different from";
+ln[">_fr"] = "is greater than";
+ln[">=_fr"] = "is greater than or equal to";
+ln["<_fr"] = "is less than";
+ln["<=_fr"] = "is less than or equal to";
 ln["contains_fr"] = "contains";
 ln["notContains_fr"] = "not contains";
 ln["start_fr"] = "start with";
@@ -425,6 +446,13 @@ ln["warning_nl"] = "WARNING";
 ln["filterField_nl"] = "Field";
 ln["filterOperation_nl"] = "Operation";
 ln["filterValue_nl"] = "Value";
+
+ln["=_nl"] = "is equal to";
+ln["!=_nl"] = "is different from";
+ln[">_nl"] = "is greater than";
+ln[">=_nl"] = "is greater than or equal to";
+ln["<_nl"] = "is less than";
+ln["<=_nl"] = "is less than or equal to";
 ln["contains_nl"] = "contains";
 ln["notContains_nl"] = "not contains";
 ln["start_nl"] = "start with";