CLDRPluralRuleParser: Move from src/ to lib/ without local patch
authorTimo Tijhof <krinklemail@gmail.com>
Fri, 4 May 2018 02:28:10 +0000 (03:28 +0100)
committerTimo Tijhof <krinklemail@gmail.com>
Fri, 4 May 2018 02:28:10 +0000 (03:28 +0100)
Follows-up 648667ac9f, which didn't move this module because
it had a local patch for exposing via module.exports and mw.lib
(instead of its default 'pluralRuleParser' global).

Restore the file back to a clean copy from upstream, and perform
the export via a separately concatenated file instead, using the
same pattern we already use for 'oojs' and 'moment'.

Change-Id: I27ee80dc34e0ad5206cf9c1ce68be3ec8811ecf8

resources/Resources.php
resources/lib/CLDRPluralRuleParser/CLDRPluralRuleParser.js [new file with mode: 0644]
resources/src/mediawiki.libs.pluralruleparser/export.js [new file with mode: 0644]
resources/src/mediawiki.libs/CLDRPluralRuleParser.js [deleted file]

index 095ca75..91e5750 100644 (file)
@@ -1627,7 +1627,10 @@ return [
        ],
 
        'mediawiki.libs.pluralruleparser' => [
-               'scripts' => 'resources/src/mediawiki.libs/CLDRPluralRuleParser.js',
+               'scripts' => [
+                       'resources/lib/CLDRPluralRuleParser/CLDRPluralRuleParser.js',
+                       'resources/src/mediawiki.libs.pluralruleparser/export.js',
+               ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
 
diff --git a/resources/lib/CLDRPluralRuleParser/CLDRPluralRuleParser.js b/resources/lib/CLDRPluralRuleParser/CLDRPluralRuleParser.js
new file mode 100644 (file)
index 0000000..1491e3d
--- /dev/null
@@ -0,0 +1,608 @@
+/**
+ * cldrpluralparser.js
+ * A parser engine for CLDR plural rules.
+ *
+ * Copyright 2012-2014 Santhosh Thottingal and other contributors
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ * @version 0.1.0
+ * @source https://github.com/santhoshtr/CLDRPluralRuleParser
+ * @author Santhosh Thottingal <santhosh.thottingal@gmail.com>
+ * @author Timo Tijhof
+ * @author Amir Aharoni
+ */
+
+/**
+ * Evaluates a plural rule in CLDR syntax for a number
+ * @param {string} rule
+ * @param {integer} number
+ * @return {boolean} true if evaluation passed, false if evaluation failed.
+ */
+
+// UMD returnExports https://github.com/umdjs/umd/blob/master/returnExports.js
+(function(root, factory) {
+       if (typeof define === 'function' && define.amd) {
+               // AMD. Register as an anonymous module.
+               define(factory);
+       } else if (typeof exports === 'object') {
+               // Node. Does not work with strict CommonJS, but
+               // only CommonJS-like environments that support module.exports,
+               // like Node.
+               module.exports = factory();
+       } else {
+               // Browser globals (root is window)
+               root.pluralRuleParser = factory();
+       }
+}(this, function() {
+
+function pluralRuleParser(rule, number) {
+       'use strict';
+
+       /*
+       Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules
+       -----------------------------------------------------------------
+       condition     = and_condition ('or' and_condition)*
+               ('@integer' samples)?
+               ('@decimal' samples)?
+       and_condition = relation ('and' relation)*
+       relation      = is_relation | in_relation | within_relation
+       is_relation   = expr 'is' ('not')? value
+       in_relation   = expr (('not')? 'in' | '=' | '!=') range_list
+       within_relation = expr ('not')? 'within' range_list
+       expr          = operand (('mod' | '%') value)?
+       operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
+       range_list    = (range | value) (',' range_list)*
+       value         = digit+
+       digit         = 0|1|2|3|4|5|6|7|8|9
+       range         = value'..'value
+       samples       = sampleRange (',' sampleRange)* (',' ('…'|'...'))?
+       sampleRange   = decimalValue '~' decimalValue
+       decimalValue  = value ('.' value)?
+       */
+
+       // We don't evaluate the samples section of the rule. Ignore it.
+       rule = rule.split('@')[0].replace(/^\s*/, '').replace(/\s*$/, '');
+
+       if (!rule.length) {
+               // Empty rule or 'other' rule.
+               return true;
+       }
+
+       // Indicates the current position in the rule as we parse through it.
+       // Shared among all parsing functions below.
+       var pos = 0,
+               operand,
+               expression,
+               relation,
+               result,
+               whitespace = makeRegexParser(/^\s+/),
+               value = makeRegexParser(/^\d+/),
+               _n_ = makeStringParser('n'),
+               _i_ = makeStringParser('i'),
+               _f_ = makeStringParser('f'),
+               _t_ = makeStringParser('t'),
+               _v_ = makeStringParser('v'),
+               _w_ = makeStringParser('w'),
+               _is_ = makeStringParser('is'),
+               _isnot_ = makeStringParser('is not'),
+               _isnot_sign_ = makeStringParser('!='),
+               _equal_ = makeStringParser('='),
+               _mod_ = makeStringParser('mod'),
+               _percent_ = makeStringParser('%'),
+               _not_ = makeStringParser('not'),
+               _in_ = makeStringParser('in'),
+               _within_ = makeStringParser('within'),
+               _range_ = makeStringParser('..'),
+               _comma_ = makeStringParser(','),
+               _or_ = makeStringParser('or'),
+               _and_ = makeStringParser('and');
+
+       function debug() {
+               // console.log.apply(console, arguments);
+       }
+
+       debug('pluralRuleParser', rule, number);
+
+       // Try parsers until one works, if none work return null
+       function choice(parserSyntax) {
+               return function() {
+                       var i, result;
+
+                       for (i = 0; i < parserSyntax.length; i++) {
+                               result = parserSyntax[i]();
+
+                               if (result !== null) {
+                                       return result;
+                               }
+                       }
+
+                       return null;
+               };
+       }
+
+       // Try several parserSyntax-es in a row.
+       // All must succeed; otherwise, return null.
+       // This is the only eager one.
+       function sequence(parserSyntax) {
+               var i, parserRes,
+                       originalPos = pos,
+                       result = [];
+
+               for (i = 0; i < parserSyntax.length; i++) {
+                       parserRes = parserSyntax[i]();
+
+                       if (parserRes === null) {
+                               pos = originalPos;
+
+                               return null;
+                       }
+
+                       result.push(parserRes);
+               }
+
+               return result;
+       }
+
+       // Run the same parser over and over until it fails.
+       // Must succeed a minimum of n times; otherwise, return null.
+       function nOrMore(n, p) {
+               return function() {
+                       var originalPos = pos,
+                               result = [],
+                               parsed = p();
+
+                       while (parsed !== null) {
+                               result.push(parsed);
+                               parsed = p();
+                       }
+
+                       if (result.length < n) {
+                               pos = originalPos;
+
+                               return null;
+                       }
+
+                       return result;
+               };
+       }
+
+       // Helpers - just make parserSyntax out of simpler JS builtin types
+       function makeStringParser(s) {
+               var len = s.length;
+
+               return function() {
+                       var result = null;
+
+                       if (rule.substr(pos, len) === s) {
+                               result = s;
+                               pos += len;
+                       }
+
+                       return result;
+               };
+       }
+
+       function makeRegexParser(regex) {
+               return function() {
+                       var matches = rule.substr(pos).match(regex);
+
+                       if (matches === null) {
+                               return null;
+                       }
+
+                       pos += matches[0].length;
+
+                       return matches[0];
+               };
+       }
+
+       /**
+        * Integer digits of n.
+        */
+       function i() {
+               var result = _i_();
+
+               if (result === null) {
+                       debug(' -- failed i', parseInt(number, 10));
+
+                       return result;
+               }
+
+               result = parseInt(number, 10);
+               debug(' -- passed i ', result);
+
+               return result;
+       }
+
+       /**
+        * Absolute value of the source number (integer and decimals).
+        */
+       function n() {
+               var result = _n_();
+
+               if (result === null) {
+                       debug(' -- failed n ', number);
+
+                       return result;
+               }
+
+               result = parseFloat(number, 10);
+               debug(' -- passed n ', result);
+
+               return result;
+       }
+
+       /**
+        * Visible fractional digits in n, with trailing zeros.
+        */
+       function f() {
+               var result = _f_();
+
+               if (result === null) {
+                       debug(' -- failed f ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1] || 0;
+               debug(' -- passed f ', result);
+
+               return result;
+       }
+
+       /**
+        * Visible fractional digits in n, without trailing zeros.
+        */
+       function t() {
+               var result = _t_();
+
+               if (result === null) {
+                       debug(' -- failed t ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1].replace(/0$/, '') || 0;
+               debug(' -- passed t ', result);
+
+               return result;
+       }
+
+       /**
+        * Number of visible fraction digits in n, with trailing zeros.
+        */
+       function v() {
+               var result = _v_();
+
+               if (result === null) {
+                       debug(' -- failed v ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1].length || 0;
+               debug(' -- passed v ', result);
+
+               return result;
+       }
+
+       /**
+        * Number of visible fraction digits in n, without trailing zeros.
+        */
+       function w() {
+               var result = _w_();
+
+               if (result === null) {
+                       debug(' -- failed w ', number);
+
+                       return result;
+               }
+
+               result = (number + '.').split('.')[1].replace(/0$/, '').length || 0;
+               debug(' -- passed w ', result);
+
+               return result;
+       }
+
+       // operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
+       operand = choice([n, i, f, t, v, w]);
+
+       // expr          = operand (('mod' | '%') value)?
+       expression = choice([mod, operand]);
+
+       function mod() {
+               var result = sequence(
+                       [operand, whitespace, choice([_mod_, _percent_]), whitespace, value]
+               );
+
+               if (result === null) {
+                       debug(' -- failed mod');
+
+                       return null;
+               }
+
+               debug(' -- passed ' + parseInt(result[0], 10) + ' ' + result[2] + ' ' + parseInt(result[4], 10));
+
+               return parseInt(result[0], 10) % parseInt(result[4], 10);
+       }
+
+       function not() {
+               var result = sequence([whitespace, _not_]);
+
+               if (result === null) {
+                       debug(' -- failed not');
+
+                       return null;
+               }
+
+               return result[1];
+       }
+
+       // is_relation   = expr 'is' ('not')? value
+       function is() {
+               var result = sequence([expression, whitespace, choice([_is_]), whitespace, value]);
+
+               if (result !== null) {
+                       debug(' -- passed is : ' + result[0] + ' == ' + parseInt(result[4], 10));
+
+                       return result[0] === parseInt(result[4], 10);
+               }
+
+               debug(' -- failed is');
+
+               return null;
+       }
+
+       // is_relation   = expr 'is' ('not')? value
+       function isnot() {
+               var result = sequence(
+                       [expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value]
+               );
+
+               if (result !== null) {
+                       debug(' -- passed isnot: ' + result[0] + ' != ' + parseInt(result[4], 10));
+
+                       return result[0] !== parseInt(result[4], 10);
+               }
+
+               debug(' -- failed isnot');
+
+               return null;
+       }
+
+       function not_in() {
+               var i, range_list,
+                       result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]);
+
+               if (result !== null) {
+                       debug(' -- passed not_in: ' + result[0] + ' != ' + result[4]);
+                       range_list = result[4];
+
+                       for (i = 0; i < range_list.length; i++) {
+                               if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) {
+                                       return false;
+                               }
+                       }
+
+                       return true;
+               }
+
+               debug(' -- failed not_in');
+
+               return null;
+       }
+
+       // range_list    = (range | value) (',' range_list)*
+       function rangeList() {
+               var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]),
+                       resultList = [];
+
+               if (result !== null) {
+                       resultList = resultList.concat(result[0]);
+
+                       if (result[1][0]) {
+                               resultList = resultList.concat(result[1][0]);
+                       }
+
+                       return resultList;
+               }
+
+               debug(' -- failed rangeList');
+
+               return null;
+       }
+
+       function rangeTail() {
+               // ',' range_list
+               var result = sequence([_comma_, rangeList]);
+
+               if (result !== null) {
+                       return result[1];
+               }
+
+               debug(' -- failed rangeTail');
+
+               return null;
+       }
+
+       // range         = value'..'value
+       function range() {
+               var i, array, left, right,
+                       result = sequence([value, _range_, value]);
+
+               if (result !== null) {
+                       debug(' -- passed range');
+
+                       array = [];
+                       left = parseInt(result[0], 10);
+                       right = parseInt(result[2], 10);
+
+                       for (i = left; i <= right; i++) {
+                               array.push(i);
+                       }
+
+                       return array;
+               }
+
+               debug(' -- failed range');
+
+               return null;
+       }
+
+       function _in() {
+               var result, range_list, i;
+
+               // in_relation   = expr ('not')? 'in' range_list
+               result = sequence(
+                       [expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList]
+               );
+
+               if (result !== null) {
+                       debug(' -- passed _in:' + result);
+
+                       range_list = result[5];
+
+                       for (i = 0; i < range_list.length; i++) {
+                               if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) {
+                                       return (result[1][0] !== 'not');
+                               }
+                       }
+
+                       return (result[1][0] === 'not');
+               }
+
+               debug(' -- failed _in ');
+
+               return null;
+       }
+
+       /**
+        * The difference between "in" and "within" is that
+        * "in" only includes integers in the specified range,
+        * while "within" includes all values.
+        */
+       function within() {
+               var range_list, result;
+
+               // within_relation = expr ('not')? 'within' range_list
+               result = sequence(
+                       [expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList]
+               );
+
+               if (result !== null) {
+                       debug(' -- passed within');
+
+                       range_list = result[5];
+
+                       if ((result[0] >= parseInt(range_list[0], 10)) &&
+                               (result[0] < parseInt(range_list[range_list.length - 1], 10))) {
+
+                               return (result[1][0] !== 'not');
+                       }
+
+                       return (result[1][0] === 'not');
+               }
+
+               debug(' -- failed within ');
+
+               return null;
+       }
+
+       // relation      = is_relation | in_relation | within_relation
+       relation = choice([is, not_in, isnot, _in, within]);
+
+       // and_condition = relation ('and' relation)*
+       function and() {
+               var i,
+                       result = sequence([relation, nOrMore(0, andTail)]);
+
+               if (result) {
+                       if (!result[0]) {
+                               return false;
+                       }
+
+                       for (i = 0; i < result[1].length; i++) {
+                               if (!result[1][i]) {
+                                       return false;
+                               }
+                       }
+
+                       return true;
+               }
+
+               debug(' -- failed and');
+
+               return null;
+       }
+
+       // ('and' relation)*
+       function andTail() {
+               var result = sequence([whitespace, _and_, whitespace, relation]);
+
+               if (result !== null) {
+                       debug(' -- passed andTail' + result);
+
+                       return result[3];
+               }
+
+               debug(' -- failed andTail');
+
+               return null;
+
+       }
+       //  ('or' and_condition)*
+       function orTail() {
+               var result = sequence([whitespace, _or_, whitespace, and]);
+
+               if (result !== null) {
+                       debug(' -- passed orTail: ' + result[3]);
+
+                       return result[3];
+               }
+
+               debug(' -- failed orTail');
+
+               return null;
+       }
+
+       // condition     = and_condition ('or' and_condition)*
+       function condition() {
+               var i,
+                       result = sequence([and, nOrMore(0, orTail)]);
+
+               if (result) {
+                       for (i = 0; i < result[1].length; i++) {
+                               if (result[1][i]) {
+                                       return true;
+                               }
+                       }
+
+                       return result[0];
+               }
+
+               return false;
+       }
+
+       result = condition();
+
+       /**
+        * For success, the pos must have gotten to the end of the rule
+        * and returned a non-null.
+        * n.b. This is part of language infrastructure,
+        * so we do not throw an internationalizable message.
+        */
+       if (result === null) {
+               throw new Error('Parse error at position ' + pos.toString() + ' for rule: ' + rule);
+       }
+
+       if (pos !== rule.length) {
+               debug('Warning: Rule not parsed completely. Parser stopped at ' + rule.substr(0, pos) + ' for rule: ' + rule);
+       }
+
+       return result;
+}
+
+return pluralRuleParser;
+
+}));
diff --git a/resources/src/mediawiki.libs.pluralruleparser/export.js b/resources/src/mediawiki.libs.pluralruleparser/export.js
new file mode 100644 (file)
index 0000000..28449d3
--- /dev/null
@@ -0,0 +1,5 @@
+// Expose via module.exports
+module.exports = window.pluralRuleParser;
+
+// Back-compat: Also expose via mw.lib
+mediaWiki.libs.pluralRuleParser = window.pluralRuleParser;
diff --git a/resources/src/mediawiki.libs/CLDRPluralRuleParser.js b/resources/src/mediawiki.libs/CLDRPluralRuleParser.js
deleted file mode 100644 (file)
index 549a9ab..0000000
+++ /dev/null
@@ -1,596 +0,0 @@
-/* This is CLDRPluralRuleParser v1.1.3, ported to MediaWiki ResourceLoader */
-
-/**
-* CLDRPluralRuleParser.js
-* A parser engine for CLDR plural rules.
-*
-* Copyright 2012-2014 Santhosh Thottingal and other contributors
-* Released under the MIT license
-* http://opensource.org/licenses/MIT
-*
-* @source https://github.com/santhoshtr/CLDRPluralRuleParser
-* @author Santhosh Thottingal <santhosh.thottingal@gmail.com>
-* @author Timo Tijhof
-* @author Amir Aharoni
-*/
-
-( function ( mw ) {
-/**
- * Evaluates a plural rule in CLDR syntax for a number
- * @param {string} rule
- * @param {integer} number
- * @return {boolean} true if evaluation passed, false if evaluation failed.
- */
-
-function pluralRuleParser(rule, number) {
-       'use strict';
-
-       /*
-       Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules
-       -----------------------------------------------------------------
-       condition     = and_condition ('or' and_condition)*
-               ('@integer' samples)?
-               ('@decimal' samples)?
-       and_condition = relation ('and' relation)*
-       relation      = is_relation | in_relation | within_relation
-       is_relation   = expr 'is' ('not')? value
-       in_relation   = expr (('not')? 'in' | '=' | '!=') range_list
-       within_relation = expr ('not')? 'within' range_list
-       expr          = operand (('mod' | '%') value)?
-       operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
-       range_list    = (range | value) (',' range_list)*
-       value         = digit+
-       digit         = 0|1|2|3|4|5|6|7|8|9
-       range         = value'..'value
-       samples       = sampleRange (',' sampleRange)* (',' ('…'|'...'))?
-       sampleRange   = decimalValue '~' decimalValue
-       decimalValue  = value ('.' value)?
-       */
-
-       // We don't evaluate the samples section of the rule. Ignore it.
-       rule = rule.split('@')[0].replace(/^\s*/, '').replace(/\s*$/, '');
-
-       if (!rule.length) {
-               // Empty rule or 'other' rule.
-               return true;
-       }
-
-       // Indicates the current position in the rule as we parse through it.
-       // Shared among all parsing functions below.
-       var pos = 0,
-               operand,
-               expression,
-               relation,
-               result,
-               whitespace = makeRegexParser(/^\s+/),
-               value = makeRegexParser(/^\d+/),
-               _n_ = makeStringParser('n'),
-               _i_ = makeStringParser('i'),
-               _f_ = makeStringParser('f'),
-               _t_ = makeStringParser('t'),
-               _v_ = makeStringParser('v'),
-               _w_ = makeStringParser('w'),
-               _is_ = makeStringParser('is'),
-               _isnot_ = makeStringParser('is not'),
-               _isnot_sign_ = makeStringParser('!='),
-               _equal_ = makeStringParser('='),
-               _mod_ = makeStringParser('mod'),
-               _percent_ = makeStringParser('%'),
-               _not_ = makeStringParser('not'),
-               _in_ = makeStringParser('in'),
-               _within_ = makeStringParser('within'),
-               _range_ = makeStringParser('..'),
-               _comma_ = makeStringParser(','),
-               _or_ = makeStringParser('or'),
-               _and_ = makeStringParser('and');
-
-       function debug() {
-               // console.log.apply(console, arguments);
-       }
-
-       debug('pluralRuleParser', rule, number);
-
-       // Try parsers until one works, if none work return null
-       function choice(parserSyntax) {
-               return function() {
-                       var i, result;
-
-                       for (i = 0; i < parserSyntax.length; i++) {
-                               result = parserSyntax[i]();
-
-                               if (result !== null) {
-                                       return result;
-                               }
-                       }
-
-                       return null;
-               };
-       }
-
-       // Try several parserSyntax-es in a row.
-       // All must succeed; otherwise, return null.
-       // This is the only eager one.
-       function sequence(parserSyntax) {
-               var i, parserRes,
-                       originalPos = pos,
-                       result = [];
-
-               for (i = 0; i < parserSyntax.length; i++) {
-                       parserRes = parserSyntax[i]();
-
-                       if (parserRes === null) {
-                               pos = originalPos;
-
-                               return null;
-                       }
-
-                       result.push(parserRes);
-               }
-
-               return result;
-       }
-
-       // Run the same parser over and over until it fails.
-       // Must succeed a minimum of n times; otherwise, return null.
-       function nOrMore(n, p) {
-               return function() {
-                       var originalPos = pos,
-                               result = [],
-                               parsed = p();
-
-                       while (parsed !== null) {
-                               result.push(parsed);
-                               parsed = p();
-                       }
-
-                       if (result.length < n) {
-                               pos = originalPos;
-
-                               return null;
-                       }
-
-                       return result;
-               };
-       }
-
-       // Helpers - just make parserSyntax out of simpler JS builtin types
-       function makeStringParser(s) {
-               var len = s.length;
-
-               return function() {
-                       var result = null;
-
-                       if (rule.substr(pos, len) === s) {
-                               result = s;
-                               pos += len;
-                       }
-
-                       return result;
-               };
-       }
-
-       function makeRegexParser(regex) {
-               return function() {
-                       var matches = rule.substr(pos).match(regex);
-
-                       if (matches === null) {
-                               return null;
-                       }
-
-                       pos += matches[0].length;
-
-                       return matches[0];
-               };
-       }
-
-       /**
-        * Integer digits of n.
-        */
-       function i() {
-               var result = _i_();
-
-               if (result === null) {
-                       debug(' -- failed i', parseInt(number, 10));
-
-                       return result;
-               }
-
-               result = parseInt(number, 10);
-               debug(' -- passed i ', result);
-
-               return result;
-       }
-
-       /**
-        * Absolute value of the source number (integer and decimals).
-        */
-       function n() {
-               var result = _n_();
-
-               if (result === null) {
-                       debug(' -- failed n ', number);
-
-                       return result;
-               }
-
-               result = parseFloat(number, 10);
-               debug(' -- passed n ', result);
-
-               return result;
-       }
-
-       /**
-        * Visible fractional digits in n, with trailing zeros.
-        */
-       function f() {
-               var result = _f_();
-
-               if (result === null) {
-                       debug(' -- failed f ', number);
-
-                       return result;
-               }
-
-               result = (number + '.').split('.')[1] || 0;
-               debug(' -- passed f ', result);
-
-               return result;
-       }
-
-       /**
-        * Visible fractional digits in n, without trailing zeros.
-        */
-       function t() {
-               var result = _t_();
-
-               if (result === null) {
-                       debug(' -- failed t ', number);
-
-                       return result;
-               }
-
-               result = (number + '.').split('.')[1].replace(/0$/, '') || 0;
-               debug(' -- passed t ', result);
-
-               return result;
-       }
-
-       /**
-        * Number of visible fraction digits in n, with trailing zeros.
-        */
-       function v() {
-               var result = _v_();
-
-               if (result === null) {
-                       debug(' -- failed v ', number);
-
-                       return result;
-               }
-
-               result = (number + '.').split('.')[1].length || 0;
-               debug(' -- passed v ', result);
-
-               return result;
-       }
-
-       /**
-        * Number of visible fraction digits in n, without trailing zeros.
-        */
-       function w() {
-               var result = _w_();
-
-               if (result === null) {
-                       debug(' -- failed w ', number);
-
-                       return result;
-               }
-
-               result = (number + '.').split('.')[1].replace(/0$/, '').length || 0;
-               debug(' -- passed w ', result);
-
-               return result;
-       }
-
-       // operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
-       operand = choice([n, i, f, t, v, w]);
-
-       // expr          = operand (('mod' | '%') value)?
-       expression = choice([mod, operand]);
-
-       function mod() {
-               var result = sequence(
-                       [operand, whitespace, choice([_mod_, _percent_]), whitespace, value]
-               );
-
-               if (result === null) {
-                       debug(' -- failed mod');
-
-                       return null;
-               }
-
-               debug(' -- passed ' + parseInt(result[0], 10) + ' ' + result[2] + ' ' + parseInt(result[4], 10));
-
-               return parseInt(result[0], 10) % parseInt(result[4], 10);
-       }
-
-       function not() {
-               var result = sequence([whitespace, _not_]);
-
-               if (result === null) {
-                       debug(' -- failed not');
-
-                       return null;
-               }
-
-               return result[1];
-       }
-
-       // is_relation   = expr 'is' ('not')? value
-       function is() {
-               var result = sequence([expression, whitespace, choice([_is_]), whitespace, value]);
-
-               if (result !== null) {
-                       debug(' -- passed is : ' + result[0] + ' == ' + parseInt(result[4], 10));
-
-                       return result[0] === parseInt(result[4], 10);
-               }
-
-               debug(' -- failed is');
-
-               return null;
-       }
-
-       // is_relation   = expr 'is' ('not')? value
-       function isnot() {
-               var result = sequence(
-                       [expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value]
-               );
-
-               if (result !== null) {
-                       debug(' -- passed isnot: ' + result[0] + ' != ' + parseInt(result[4], 10));
-
-                       return result[0] !== parseInt(result[4], 10);
-               }
-
-               debug(' -- failed isnot');
-
-               return null;
-       }
-
-       function not_in() {
-               var i, range_list,
-                       result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]);
-
-               if (result !== null) {
-                       debug(' -- passed not_in: ' + result[0] + ' != ' + result[4]);
-                       range_list = result[4];
-
-                       for (i = 0; i < range_list.length; i++) {
-                               if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) {
-                                       return false;
-                               }
-                       }
-
-                       return true;
-               }
-
-               debug(' -- failed not_in');
-
-               return null;
-       }
-
-       // range_list    = (range | value) (',' range_list)*
-       function rangeList() {
-               var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]),
-                       resultList = [];
-
-               if (result !== null) {
-                       resultList = resultList.concat(result[0]);
-
-                       if (result[1][0]) {
-                               resultList = resultList.concat(result[1][0]);
-                       }
-
-                       return resultList;
-               }
-
-               debug(' -- failed rangeList');
-
-               return null;
-       }
-
-       function rangeTail() {
-               // ',' range_list
-               var result = sequence([_comma_, rangeList]);
-
-               if (result !== null) {
-                       return result[1];
-               }
-
-               debug(' -- failed rangeTail');
-
-               return null;
-       }
-
-       // range         = value'..'value
-       function range() {
-               var i, array, left, right,
-                       result = sequence([value, _range_, value]);
-
-               if (result !== null) {
-                       debug(' -- passed range');
-
-                       array = [];
-                       left = parseInt(result[0], 10);
-                       right = parseInt(result[2], 10);
-
-                       for (i = left; i <= right; i++) {
-                               array.push(i);
-                       }
-
-                       return array;
-               }
-
-               debug(' -- failed range');
-
-               return null;
-       }
-
-       function _in() {
-               var result, range_list, i;
-
-               // in_relation   = expr ('not')? 'in' range_list
-               result = sequence(
-                       [expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList]
-               );
-
-               if (result !== null) {
-                       debug(' -- passed _in:' + result);
-
-                       range_list = result[5];
-
-                       for (i = 0; i < range_list.length; i++) {
-                               if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) {
-                                       return (result[1][0] !== 'not');
-                               }
-                       }
-
-                       return (result[1][0] === 'not');
-               }
-
-               debug(' -- failed _in ');
-
-               return null;
-       }
-
-       /**
-        * The difference between "in" and "within" is that
-        * "in" only includes integers in the specified range,
-        * while "within" includes all values.
-        */
-       function within() {
-               var range_list, result;
-
-               // within_relation = expr ('not')? 'within' range_list
-               result = sequence(
-                       [expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList]
-               );
-
-               if (result !== null) {
-                       debug(' -- passed within');
-
-                       range_list = result[5];
-
-                       if ((result[0] >= parseInt(range_list[0], 10)) &&
-                               (result[0] < parseInt(range_list[range_list.length - 1], 10))) {
-
-                               return (result[1][0] !== 'not');
-                       }
-
-                       return (result[1][0] === 'not');
-               }
-
-               debug(' -- failed within ');
-
-               return null;
-       }
-
-       // relation      = is_relation | in_relation | within_relation
-       relation = choice([is, not_in, isnot, _in, within]);
-
-       // and_condition = relation ('and' relation)*
-       function and() {
-               var i,
-                       result = sequence([relation, nOrMore(0, andTail)]);
-
-               if (result) {
-                       if (!result[0]) {
-                               return false;
-                       }
-
-                       for (i = 0; i < result[1].length; i++) {
-                               if (!result[1][i]) {
-                                       return false;
-                               }
-                       }
-
-                       return true;
-               }
-
-               debug(' -- failed and');
-
-               return null;
-       }
-
-       // ('and' relation)*
-       function andTail() {
-               var result = sequence([whitespace, _and_, whitespace, relation]);
-
-               if (result !== null) {
-                       debug(' -- passed andTail' + result);
-
-                       return result[3];
-               }
-
-               debug(' -- failed andTail');
-
-               return null;
-
-       }
-       //  ('or' and_condition)*
-       function orTail() {
-               var result = sequence([whitespace, _or_, whitespace, and]);
-
-               if (result !== null) {
-                       debug(' -- passed orTail: ' + result[3]);
-
-                       return result[3];
-               }
-
-               debug(' -- failed orTail');
-
-               return null;
-       }
-
-       // condition     = and_condition ('or' and_condition)*
-       function condition() {
-               var i,
-                       result = sequence([and, nOrMore(0, orTail)]);
-
-               if (result) {
-                       for (i = 0; i < result[1].length; i++) {
-                               if (result[1][i]) {
-                                       return true;
-                               }
-                       }
-
-                       return result[0];
-               }
-
-               return false;
-       }
-
-       result = condition();
-
-       /**
-        * For success, the pos must have gotten to the end of the rule
-        * and returned a non-null.
-        * n.b. This is part of language infrastructure,
-        * so we do not throw an internationalizable message.
-        */
-       if (result === null) {
-               throw new Error('Parse error at position ' + pos.toString() + ' for rule: ' + rule);
-       }
-
-       if (pos !== rule.length) {
-               debug('Warning: Rule not parsed completely. Parser stopped at ' + rule.substr(0, pos) + ' for rule: ' + rule);
-       }
-
-       return result;
-}
-
-/* pluralRuleParser ends here */
-mw.libs.pluralRuleParser = pluralRuleParser;
-module.exports = pluralRuleParser;
-
-} )( mediaWiki );