| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033 | 
							- /**
 
-  * Copyright (c) 2011-2013 Fabien Cazenave, Mozilla.
 
-  *
 
-  * 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.
 
-  */
 
- /*
 
-   Additional modifications for PDF.js project:
 
-     - Disables language initialization on page loading;
 
-     - Removes consoleWarn and consoleLog and use console.log/warn directly.
 
-     - Removes window._ assignment.
 
-     - Remove compatibility code for OldIE.
 
- */
 
- /*jshint browser: true, devel: true, es5: true, globalstrict: true */
 
- 'use strict';
 
- document.webL10n = (function(window, document, undefined) {
 
-   var gL10nData = {};
 
-   var gTextData = '';
 
-   var gTextProp = 'textContent';
 
-   var gLanguage = '';
 
-   var gMacros = {};
 
-   var gReadyState = 'loading';
 
-   /**
 
-    * Synchronously loading l10n resources significantly minimizes flickering
 
-    * from displaying the app with non-localized strings and then updating the
 
-    * strings. Although this will block all script execution on this page, we
 
-    * expect that the l10n resources are available locally on flash-storage.
 
-    *
 
-    * As synchronous XHR is generally considered as a bad idea, we're still
 
-    * loading l10n resources asynchronously -- but we keep this in a setting,
 
-    * just in case... and applications using this library should hide their
 
-    * content until the `localized' event happens.
 
-    */
 
-   var gAsyncResourceLoading = true; // read-only
 
-   /**
 
-    * DOM helpers for the so-called "HTML API".
 
-    *
 
-    * These functions are written for modern browsers. For old versions of IE,
 
-    * they're overridden in the 'startup' section at the end of this file.
 
-    */
 
-   function getL10nResourceLinks() {
 
-     return document.querySelectorAll('link[type="application/l10n"]');
 
-   }
 
-   function getL10nDictionary() {
 
-     var script = document.querySelector('script[type="application/l10n"]');
 
-     // TODO: support multiple and external JSON dictionaries
 
-     return script ? JSON.parse(script.innerHTML) : null;
 
-   }
 
-   function getTranslatableChildren(element) {
 
-     return element ? element.querySelectorAll('*[data-l10n-id]') : [];
 
-   }
 
-   function getL10nAttributes(element) {
 
-     if (!element)
 
-       return {};
 
-     var l10nId = element.getAttribute('data-l10n-id');
 
-     var l10nArgs = element.getAttribute('data-l10n-args');
 
-     var args = {};
 
-     if (l10nArgs) {
 
-       try {
 
-         args = JSON.parse(l10nArgs);
 
-       } catch (e) {
 
-         console.warn('could not parse arguments for #' + l10nId);
 
-       }
 
-     }
 
-     return { id: l10nId, args: args };
 
-   }
 
-   function fireL10nReadyEvent(lang) {
 
-     var evtObject = document.createEvent('Event');
 
-     evtObject.initEvent('localized', true, false);
 
-     evtObject.language = lang;
 
-     document.dispatchEvent(evtObject);
 
-   }
 
-   function xhrLoadText(url, onSuccess, onFailure) {
 
-     onSuccess = onSuccess || function _onSuccess(data) {};
 
-     onFailure = onFailure || function _onFailure() {
 
-       console.warn(url + ' not found.');
 
-     };
 
-     var xhr = new XMLHttpRequest();
 
-     xhr.open('GET', url, gAsyncResourceLoading);
 
-     if (xhr.overrideMimeType) {
 
-       xhr.overrideMimeType('text/plain; charset=utf-8');
 
-     }
 
-     xhr.onreadystatechange = function() {
 
-       if (xhr.readyState == 4) {
 
-         if (xhr.status == 200 || xhr.status === 0) {
 
-           onSuccess(xhr.responseText);
 
-         } else {
 
-           onFailure();
 
-         }
 
-       }
 
-     };
 
-     xhr.onerror = onFailure;
 
-     xhr.ontimeout = onFailure;
 
-     // in Firefox OS with the app:// protocol, trying to XHR a non-existing
 
-     // URL will raise an exception here -- hence this ugly try...catch.
 
-     try {
 
-       xhr.send(null);
 
-     } catch (e) {
 
-       onFailure();
 
-     }
 
-   }
 
-   /**
 
-    * l10n resource parser:
 
-    *  - reads (async XHR) the l10n resource matching `lang';
 
-    *  - imports linked resources (synchronously) when specified;
 
-    *  - parses the text data (fills `gL10nData' and `gTextData');
 
-    *  - triggers success/failure callbacks when done.
 
-    *
 
-    * @param {string} href
 
-    *    URL of the l10n resource to parse.
 
-    *
 
-    * @param {string} lang
 
-    *    locale (language) to parse. Must be a lowercase string.
 
-    *
 
-    * @param {Function} successCallback
 
-    *    triggered when the l10n resource has been successully parsed.
 
-    *
 
-    * @param {Function} failureCallback
 
-    *    triggered when the an error has occured.
 
-    *
 
-    * @return {void}
 
-    *    uses the following global variables: gL10nData, gTextData, gTextProp.
 
-    */
 
-   function parseResource(href, lang, successCallback, failureCallback) {
 
-     var baseURL = href.replace(/[^\/]*$/, '') || './';
 
-     // handle escaped characters (backslashes) in a string
 
-     function evalString(text) {
 
-       if (text.lastIndexOf('\\') < 0)
 
-         return text;
 
-       return text.replace(/\\\\/g, '\\')
 
-                  .replace(/\\n/g, '\n')
 
-                  .replace(/\\r/g, '\r')
 
-                  .replace(/\\t/g, '\t')
 
-                  .replace(/\\b/g, '\b')
 
-                  .replace(/\\f/g, '\f')
 
-                  .replace(/\\{/g, '{')
 
-                  .replace(/\\}/g, '}')
 
-                  .replace(/\\"/g, '"')
 
-                  .replace(/\\'/g, "'");
 
-     }
 
-     // parse *.properties text data into an l10n dictionary
 
-     // If gAsyncResourceLoading is false, then the callback will be called
 
-     // synchronously. Otherwise it is called asynchronously.
 
-     function parseProperties(text, parsedPropertiesCallback) {
 
-       var dictionary = {};
 
-       // token expressions
 
-       var reBlank = /^\s*|\s*$/;
 
-       var reComment = /^\s*#|^\s*$/;
 
-       var reSection = /^\s*\[(.*)\]\s*$/;
 
-       var reImport = /^\s*@import\s+url\((.*)\)\s*$/i;
 
-       var reSplit = /^([^=\s]*)\s*=\s*(.+)$/; // TODO: escape EOLs with '\'
 
-       // parse the *.properties file into an associative array
 
-       function parseRawLines(rawText, extendedSyntax, parsedRawLinesCallback) {
 
-         var entries = rawText.replace(reBlank, '').split(/[\r\n]+/);
 
-         var currentLang = '*';
 
-         var genericLang = lang.split('-', 1)[0];
 
-         var skipLang = false;
 
-         var match = '';
 
-         function nextEntry() {
 
-           // Use infinite loop instead of recursion to avoid reaching the
 
-           // maximum recursion limit for content with many lines.
 
-           while (true) {
 
-             if (!entries.length) {
 
-               parsedRawLinesCallback();
 
-               return;
 
-             }
 
-             var line = entries.shift();
 
-             // comment or blank line?
 
-             if (reComment.test(line))
 
-               continue;
 
-             // the extended syntax supports [lang] sections and @import rules
 
-             if (extendedSyntax) {
 
-               match = reSection.exec(line);
 
-               if (match) { // section start?
 
-                 // RFC 4646, section 4.4, "All comparisons MUST be performed
 
-                 // in a case-insensitive manner."
 
-                 currentLang = match[1].toLowerCase();
 
-                 skipLang = (currentLang !== '*') &&
 
-                     (currentLang !== lang) && (currentLang !== genericLang);
 
-                 continue;
 
-               } else if (skipLang) {
 
-                 continue;
 
-               }
 
-               match = reImport.exec(line);
 
-               if (match) { // @import rule?
 
-                 loadImport(baseURL + match[1], nextEntry);
 
-                 return;
 
-               }
 
-             }
 
-             // key-value pair
 
-             var tmp = line.match(reSplit);
 
-             if (tmp && tmp.length == 3) {
 
-               dictionary[tmp[1]] = evalString(tmp[2]);
 
-             }
 
-           }
 
-         }
 
-         nextEntry();
 
-       }
 
-       // import another *.properties file
 
-       function loadImport(url, callback) {
 
-         xhrLoadText(url, function(content) {
 
-           parseRawLines(content, false, callback); // don't allow recursive imports
 
-         }, null);
 
-       }
 
-       // fill the dictionary
 
-       parseRawLines(text, true, function() {
 
-         parsedPropertiesCallback(dictionary);
 
-       });
 
-     }
 
-     // load and parse l10n data (warning: global variables are used here)
 
-     xhrLoadText(href, function(response) {
 
-       gTextData += response; // mostly for debug
 
-       // parse *.properties text data into an l10n dictionary
 
-       parseProperties(response, function(data) {
 
-         // find attribute descriptions, if any
 
-         for (var key in data) {
 
-           var id, prop, index = key.lastIndexOf('.');
 
-           if (index > 0) { // an attribute has been specified
 
-             id = key.substring(0, index);
 
-             prop = key.substr(index + 1);
 
-           } else { // no attribute: assuming text content by default
 
-             id = key;
 
-             prop = gTextProp;
 
-           }
 
-           if (!gL10nData[id]) {
 
-             gL10nData[id] = {};
 
-           }
 
-           gL10nData[id][prop] = data[key];
 
-         }
 
-         // trigger callback
 
-         if (successCallback) {
 
-           successCallback();
 
-         }
 
-       });
 
-     }, failureCallback);
 
-   }
 
-   // load and parse all resources for the specified locale
 
-   function loadLocale(lang, callback) {
 
-     // RFC 4646, section 2.1 states that language tags have to be treated as
 
-     // case-insensitive. Convert to lowercase for case-insensitive comparisons.
 
-     if (lang) {
 
-       lang = lang.toLowerCase();
 
-     }
 
-     callback = callback || function _callback() {};
 
-     clear();
 
-     gLanguage = lang;
 
-     // check all <link type="application/l10n" href="..." /> nodes
 
-     // and load the resource files
 
-     var langLinks = getL10nResourceLinks();
 
-     var langCount = langLinks.length;
 
-     if (langCount === 0) {
 
-       // we might have a pre-compiled dictionary instead
 
-       var dict = getL10nDictionary();
 
-       if (dict && dict.locales && dict.default_locale) {
 
-         console.log('using the embedded JSON directory, early way out');
 
-         gL10nData = dict.locales[lang];
 
-         if (!gL10nData) {
 
-           var defaultLocale = dict.default_locale.toLowerCase();
 
-           for (var anyCaseLang in dict.locales) {
 
-             anyCaseLang = anyCaseLang.toLowerCase();
 
-             if (anyCaseLang === lang) {
 
-               gL10nData = dict.locales[lang];
 
-               break;
 
-             } else if (anyCaseLang === defaultLocale) {
 
-               gL10nData = dict.locales[defaultLocale];
 
-             }
 
-           }
 
-         }
 
-         callback();
 
-       } else {
 
-         console.log('no resource to load, early way out');
 
-       }
 
-       // early way out
 
-       fireL10nReadyEvent(lang);
 
-       gReadyState = 'complete';
 
-       return;
 
-     }
 
-     // start the callback when all resources are loaded
 
-     var onResourceLoaded = null;
 
-     var gResourceCount = 0;
 
-     onResourceLoaded = function() {
 
-       gResourceCount++;
 
-       if (gResourceCount >= langCount) {
 
-         callback();
 
-         fireL10nReadyEvent(lang);
 
-         gReadyState = 'complete';
 
-       }
 
-     };
 
-     // load all resource files
 
-     function L10nResourceLink(link) {
 
-       var href = link.href;
 
-       // Note: If |gAsyncResourceLoading| is false, then the following callbacks
 
-       // are synchronously called.
 
-       this.load = function(lang, callback) {
 
-         parseResource(href, lang, callback, function() {
 
-           console.warn(href + ' not found.');
 
-           // lang not found, used default resource instead
 
-           console.warn('"' + lang + '" resource not found');
 
-           gLanguage = '';
 
-           // Resource not loaded, but we still need to call the callback.
 
-           callback();
 
-         });
 
-       };
 
-     }
 
-     for (var i = 0; i < langCount; i++) {
 
-       var resource = new L10nResourceLink(langLinks[i]);
 
-       resource.load(lang, onResourceLoaded);
 
-     }
 
-   }
 
-   // clear all l10n data
 
-   function clear() {
 
-     gL10nData = {};
 
-     gTextData = '';
 
-     gLanguage = '';
 
-     // TODO: clear all non predefined macros.
 
-     // There's no such macro /yet/ but we're planning to have some...
 
-   }
 
-   /**
 
-    * Get rules for plural forms (shared with JetPack), see:
 
-    * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
 
-    * https://github.com/mozilla/addon-sdk/blob/master/python-lib/plural-rules-generator.p
 
-    *
 
-    * @param {string} lang
 
-    *    locale (language) used.
 
-    *
 
-    * @return {Function}
 
-    *    returns a function that gives the plural form name for a given integer:
 
-    *       var fun = getPluralRules('en');
 
-    *       fun(1)    -> 'one'
 
-    *       fun(0)    -> 'other'
 
-    *       fun(1000) -> 'other'.
 
-    */
 
-   function getPluralRules(lang) {
 
-     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
 
-     };
 
-     // utility functions for plural rules methods
 
-     function isIn(n, list) {
 
-       return list.indexOf(n) !== -1;
 
-     }
 
-     function isBetween(n, start, end) {
 
-       return start <= n && n <= end;
 
-     }
 
-     // list of all plural rules methods:
 
-     // map an integer to the plural form name to use
 
-     var pluralRules = {
 
-       '0': function(n) {
 
-         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';
 
-       }
 
-     };
 
-     // return a function that gives the plural form name for a given integer
 
-     var index = locales2rules[lang.replace(/-.*$/, '')];
 
-     if (!(index in pluralRules)) {
 
-       console.warn('plural form unknown for [' + lang + ']');
 
-       return function() { return 'other'; };
 
-     }
 
-     return pluralRules[index];
 
-   }
 
-   // pre-defined 'plural' macro
 
-   gMacros.plural = function(str, param, key, prop) {
 
-     var n = parseFloat(param);
 
-     if (isNaN(n))
 
-       return str;
 
-     // TODO: support other properties (l20n still doesn't...)
 
-     if (prop != gTextProp)
 
-       return str;
 
-     // initialize _pluralRules
 
-     if (!gMacros._pluralRules) {
 
-       gMacros._pluralRules = getPluralRules(gLanguage);
 
-     }
 
-     var index = '[' + gMacros._pluralRules(n) + ']';
 
-     // try to find a [zero|one|two] key if it's defined
 
-     if (n === 0 && (key + '[zero]') in gL10nData) {
 
-       str = gL10nData[key + '[zero]'][prop];
 
-     } else if (n == 1 && (key + '[one]') in gL10nData) {
 
-       str = gL10nData[key + '[one]'][prop];
 
-     } else if (n == 2 && (key + '[two]') in gL10nData) {
 
-       str = gL10nData[key + '[two]'][prop];
 
-     } else if ((key + index) in gL10nData) {
 
-       str = gL10nData[key + index][prop];
 
-     } else if ((key + '[other]') in gL10nData) {
 
-       str = gL10nData[key + '[other]'][prop];
 
-     }
 
-     return str;
 
-   };
 
-   /**
 
-    * l10n dictionary functions
 
-    */
 
-   // fetch an l10n object, warn if not found, apply `args' if possible
 
-   function getL10nData(key, args, fallback) {
 
-     var data = gL10nData[key];
 
-     if (!data) {
 
-       console.warn('#' + key + ' is undefined.');
 
-       if (!fallback) {
 
-         return null;
 
-       }
 
-       data = fallback;
 
-     }
 
-     /** This is where l10n expressions should be processed.
 
-       * The plan is to support C-style expressions from the l20n project;
 
-       * until then, only two kinds of simple expressions are supported:
 
-       *   {[ index ]} and {{ arguments }}.
 
-       */
 
-     var rv = {};
 
-     for (var prop in data) {
 
-       var str = data[prop];
 
-       str = substIndexes(str, args, key, prop);
 
-       str = substArguments(str, args, key);
 
-       rv[prop] = str;
 
-     }
 
-     return rv;
 
-   }
 
-   // replace {[macros]} with their values
 
-   function substIndexes(str, args, key, prop) {
 
-     var reIndex = /\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)\s*\]\}/;
 
-     var reMatch = reIndex.exec(str);
 
-     if (!reMatch || !reMatch.length)
 
-       return str;
 
-     // an index/macro has been found
 
-     // Note: at the moment, only one parameter is supported
 
-     var macroName = reMatch[1];
 
-     var paramName = reMatch[2];
 
-     var param;
 
-     if (args && paramName in args) {
 
-       param = args[paramName];
 
-     } else if (paramName in gL10nData) {
 
-       param = gL10nData[paramName];
 
-     }
 
-     // there's no macro parser yet: it has to be defined in gMacros
 
-     if (macroName in gMacros) {
 
-       var macro = gMacros[macroName];
 
-       str = macro(str, param, key, prop);
 
-     }
 
-     return str;
 
-   }
 
-   // replace {{arguments}} with their values
 
-   function substArguments(str, args, key) {
 
-     var reArgs = /\{\{\s*(.+?)\s*\}\}/g;
 
-     return str.replace(reArgs, function(matched_text, arg) {
 
-       if (args && arg in args) {
 
-         return args[arg];
 
-       }
 
-       if (arg in gL10nData) {
 
-         return gL10nData[arg];
 
-       }
 
-       console.log('argument {{' + arg + '}} for #' + key + ' is undefined.');
 
-       return matched_text;
 
-     });
 
-   }
 
-   // translate an HTML element
 
-   function translateElement(element) {
 
-     var l10n = getL10nAttributes(element);
 
-     if (!l10n.id)
 
-       return;
 
-     // get the related l10n object
 
-     var data = getL10nData(l10n.id, l10n.args);
 
-     if (!data) {
 
-       console.warn('#' + l10n.id + ' is undefined.');
 
-       return;
 
-     }
 
-     // translate element (TODO: security checks?)
 
-     if (data[gTextProp]) { // XXX
 
-       if (getChildElementCount(element) === 0) {
 
-         element[gTextProp] = data[gTextProp];
 
-       } else {
 
-         // this element has element children: replace the content of the first
 
-         // (non-empty) child textNode and clear other child textNodes
 
-         var children = element.childNodes;
 
-         var found = false;
 
-         for (var i = 0, l = children.length; i < l; i++) {
 
-           if (children[i].nodeType === 3 && /\S/.test(children[i].nodeValue)) {
 
-             if (found) {
 
-               children[i].nodeValue = '';
 
-             } else {
 
-               children[i].nodeValue = data[gTextProp];
 
-               found = true;
 
-             }
 
-           }
 
-         }
 
-         // if no (non-empty) textNode is found, insert a textNode before the
 
-         // first element child.
 
-         if (!found) {
 
-           var textNode = document.createTextNode(data[gTextProp]);
 
-           element.insertBefore(textNode, element.firstChild);
 
-         }
 
-       }
 
-       delete data[gTextProp];
 
-     }
 
-     for (var k in data) {
 
-       element[k] = data[k];
 
-     }
 
-   }
 
-   // webkit browsers don't currently support 'children' on SVG elements...
 
-   function getChildElementCount(element) {
 
-     if (element.children) {
 
-       return element.children.length;
 
-     }
 
-     if (typeof element.childElementCount !== 'undefined') {
 
-       return element.childElementCount;
 
-     }
 
-     var count = 0;
 
-     for (var i = 0; i < element.childNodes.length; i++) {
 
-       count += element.nodeType === 1 ? 1 : 0;
 
-     }
 
-     return count;
 
-   }
 
-   // translate an HTML subtree
 
-   function translateFragment(element) {
 
-     element = element || document.documentElement;
 
-     // check all translatable children (= w/ a `data-l10n-id' attribute)
 
-     var children = getTranslatableChildren(element);
 
-     var elementCount = children.length;
 
-     for (var i = 0; i < elementCount; i++) {
 
-       translateElement(children[i]);
 
-     }
 
-     // translate element itself if necessary
 
-     translateElement(element);
 
-   }
 
-   return {
 
-     // get a localized string
 
-     get: function(key, args, fallbackString) {
 
-       var index = key.lastIndexOf('.');
 
-       var prop = gTextProp;
 
-       if (index > 0) { // An attribute has been specified
 
-         prop = key.substr(index + 1);
 
-         key = key.substring(0, index);
 
-       }
 
-       var fallback;
 
-       if (fallbackString) {
 
-         fallback = {};
 
-         fallback[prop] = fallbackString;
 
-       }
 
-       var data = getL10nData(key, args, fallback);
 
-       if (data && prop in data) {
 
-         return data[prop];
 
-       }
 
-       return '{{' + key + '}}';
 
-     },
 
-     // debug
 
-     getData: function() { return gL10nData; },
 
-     getText: function() { return gTextData; },
 
-     // get|set the document language
 
-     getLanguage: function() { return gLanguage; },
 
-     setLanguage: function(lang, callback) {
 
-       loadLocale(lang, function() {
 
-         if (callback)
 
-           callback();
 
-         translateFragment();
 
-       });
 
-     },
 
-     // get the direction (ltr|rtl) of the current language
 
-     getDirection: function() {
 
-       // http://www.w3.org/International/questions/qa-scripts
 
-       // Arabic, Hebrew, Farsi, Pashto, Urdu
 
-       var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
 
-       var shortCode = gLanguage.split('-', 1)[0];
 
-       return (rtlList.indexOf(shortCode) >= 0) ? 'rtl' : 'ltr';
 
-     },
 
-     // translate an element or document fragment
 
-     translate: translateFragment,
 
-     // this can be used to prevent race conditions
 
-     getReadyState: function() { return gReadyState; },
 
-     ready: function(callback) {
 
-       if (!callback) {
 
-         return;
 
-       } else if (gReadyState == 'complete' || gReadyState == 'interactive') {
 
-         window.setTimeout(function() {
 
-           callback();
 
-         });
 
-       } else if (document.addEventListener) {
 
-         document.addEventListener('localized', function once() {
 
-           document.removeEventListener('localized', once);
 
-           callback();
 
-         });
 
-       }
 
-     }
 
-   };
 
- }) (window, document);
 
 
  |