gluon-web-remote

Web remote Administration for big size of gluon routers
git clone git://archive.git.mtrnord.blog/MTRNord/gluon-web-remote.git
Log | Files | Refs | README

XRegExp.js (29440B)


      1 // XRegExp 1.5.1
      2 // (c) 2007-2012 Steven Levithan
      3 // MIT License
      4 // <http://xregexp.com>
      5 // Provides an augmented, extensible, cross-browser implementation of regular expressions,
      6 // including support for additional syntax, flags, and methods
      7 
      8 var XRegExp;
      9 
     10 if (XRegExp) {
     11     // Avoid running twice, since that would break references to native globals
     12     throw Error("can't load XRegExp twice in the same frame");
     13 }
     14 
     15 // Run within an anonymous function to protect variables and avoid new globals
     16 (function (undefined) {
     17 
     18     //---------------------------------
     19     //  Constructor
     20     //---------------------------------
     21 
     22     // Accepts a pattern and flags; returns a new, extended `RegExp` object. Differs from a native
     23     // regular expression in that additional syntax and flags are supported and cross-browser
     24     // syntax inconsistencies are ameliorated. `XRegExp(/regex/)` clones an existing regex and
     25     // converts to type XRegExp
     26     XRegExp = function (pattern, flags) {
     27         var output = [],
     28             currScope = XRegExp.OUTSIDE_CLASS,
     29             pos = 0,
     30             context, tokenResult, match, chr, regex;
     31 
     32         if (XRegExp.isRegExp(pattern)) {
     33             if (flags !== undefined)
     34                 throw TypeError("can't supply flags when constructing one RegExp from another");
     35             return clone(pattern);
     36         }
     37         // Tokens become part of the regex construction process, so protect against infinite
     38         // recursion when an XRegExp is constructed within a token handler or trigger
     39         if (isInsideConstructor)
     40             throw Error("can't call the XRegExp constructor within token definition functions");
     41 
     42         flags = flags || "";
     43         context = { // `this` object for custom tokens
     44             hasNamedCapture: false,
     45             captureNames: [],
     46             hasFlag: function (flag) {return flags.indexOf(flag) > -1;},
     47             setFlag: function (flag) {flags += flag;}
     48         };
     49 
     50         while (pos < pattern.length) {
     51             // Check for custom tokens at the current position
     52             tokenResult = runTokens(pattern, pos, currScope, context);
     53 
     54             if (tokenResult) {
     55                 output.push(tokenResult.output);
     56                 pos += (tokenResult.match[0].length || 1);
     57             } else {
     58                 // Check for native multicharacter metasequences (excluding character classes) at
     59                 // the current position
     60                 if (match = nativ.exec.call(nativeTokens[currScope], pattern.slice(pos))) {
     61                     output.push(match[0]);
     62                     pos += match[0].length;
     63                 } else {
     64                     chr = pattern.charAt(pos);
     65                     if (chr === "[")
     66                         currScope = XRegExp.INSIDE_CLASS;
     67                     else if (chr === "]")
     68                         currScope = XRegExp.OUTSIDE_CLASS;
     69                     // Advance position one character
     70                     output.push(chr);
     71                     pos++;
     72                 }
     73             }
     74         }
     75 
     76         regex = RegExp(output.join(""), nativ.replace.call(flags, flagClip, ""));
     77         regex._xregexp = {
     78             source: pattern,
     79             captureNames: context.hasNamedCapture ? context.captureNames : null
     80         };
     81         return regex;
     82     };
     83 
     84 
     85     //---------------------------------
     86     //  Public properties
     87     //---------------------------------
     88 
     89     XRegExp.version = "1.5.1";
     90 
     91     // Token scope bitflags
     92     XRegExp.INSIDE_CLASS = 1;
     93     XRegExp.OUTSIDE_CLASS = 2;
     94 
     95 
     96     //---------------------------------
     97     //  Private variables
     98     //---------------------------------
     99 
    100     var replacementToken = /\$(?:(\d\d?|[$&`'])|{([$\w]+)})/g,
    101         flagClip = /[^gimy]+|([\s\S])(?=[\s\S]*\1)/g, // Nonnative and duplicate flags
    102         quantifier = /^(?:[?*+]|{\d+(?:,\d*)?})\??/,
    103         isInsideConstructor = false,
    104         tokens = [],
    105         // Copy native globals for reference ("native" is an ES3 reserved keyword)
    106         nativ = {
    107             exec: RegExp.prototype.exec,
    108             test: RegExp.prototype.test,
    109             match: String.prototype.match,
    110             replace: String.prototype.replace,
    111             split: String.prototype.split
    112         },
    113         compliantExecNpcg = nativ.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups
    114         compliantLastIndexIncrement = function () {
    115             var x = /^/g;
    116             nativ.test.call(x, "");
    117             return !x.lastIndex;
    118         }(),
    119         hasNativeY = RegExp.prototype.sticky !== undefined,
    120         nativeTokens = {};
    121 
    122     // `nativeTokens` match native multicharacter metasequences only (including deprecated octals,
    123     // excluding character classes)
    124     nativeTokens[XRegExp.INSIDE_CLASS] = /^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/;
    125     nativeTokens[XRegExp.OUTSIDE_CLASS] = /^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/;
    126 
    127 
    128     //---------------------------------
    129     //  Public methods
    130     //---------------------------------
    131 
    132     // Lets you extend or change XRegExp syntax and create custom flags. This is used internally by
    133     // the XRegExp library and can be used to create XRegExp plugins. This function is intended for
    134     // users with advanced knowledge of JavaScript's regular expression syntax and behavior. It can
    135     // be disabled by `XRegExp.freezeTokens`
    136     XRegExp.addToken = function (regex, handler, scope, trigger) {
    137         tokens.push({
    138             pattern: clone(regex, "g" + (hasNativeY ? "y" : "")),
    139             handler: handler,
    140             scope: scope || XRegExp.OUTSIDE_CLASS,
    141             trigger: trigger || null
    142         });
    143     };
    144 
    145     // Accepts a pattern and flags; returns an extended `RegExp` object. If the pattern and flag
    146     // combination has previously been cached, the cached copy is returned; otherwise the newly
    147     // created regex is cached
    148     XRegExp.cache = function (pattern, flags) {
    149         var key = pattern + "/" + (flags || "");
    150         return XRegExp.cache[key] || (XRegExp.cache[key] = XRegExp(pattern, flags));
    151     };
    152 
    153     // Accepts a `RegExp` instance; returns a copy with the `/g` flag set. The copy has a fresh
    154     // `lastIndex` (set to zero). If you want to copy a regex without forcing the `global`
    155     // property, use `XRegExp(regex)`. Do not use `RegExp(regex)` because it will not preserve
    156     // special properties required for named capture
    157     XRegExp.copyAsGlobal = function (regex) {
    158         return clone(regex, "g");
    159     };
    160 
    161     // Accepts a string; returns the string with regex metacharacters escaped. The returned string
    162     // can safely be used at any point within a regex to match the provided literal string. Escaped
    163     // characters are [ ] { } ( ) * + ? - . , \ ^ $ | # and whitespace
    164     XRegExp.escape = function (str) {
    165         return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
    166     };
    167 
    168     // Accepts a string to search, regex to search with, position to start the search within the
    169     // string (default: 0), and an optional Boolean indicating whether matches must start at-or-
    170     // after the position or at the specified position only. This function ignores the `lastIndex`
    171     // of the provided regex in its own handling, but updates the property for compatibility
    172     XRegExp.execAt = function (str, regex, pos, anchored) {
    173         var r2 = clone(regex, "g" + ((anchored && hasNativeY) ? "y" : "")),
    174             match;
    175         r2.lastIndex = pos = pos || 0;
    176         match = r2.exec(str); // Run the altered `exec` (required for `lastIndex` fix, etc.)
    177         if (anchored && match && match.index !== pos)
    178             match = null;
    179         if (regex.global)
    180             regex.lastIndex = match ? r2.lastIndex : 0;
    181         return match;
    182     };
    183 
    184     // Breaks the unrestorable link to XRegExp's private list of tokens, thereby preventing
    185     // syntax and flag changes. Should be run after XRegExp and any plugins are loaded
    186     XRegExp.freezeTokens = function () {
    187         XRegExp.addToken = function () {
    188             throw Error("can't run addToken after freezeTokens");
    189         };
    190     };
    191 
    192     // Accepts any value; returns a Boolean indicating whether the argument is a `RegExp` object.
    193     // Note that this is also `true` for regex literals and regexes created by the `XRegExp`
    194     // constructor. This works correctly for variables created in another frame, when `instanceof`
    195     // and `constructor` checks would fail to work as intended
    196     XRegExp.isRegExp = function (o) {
    197         return Object.prototype.toString.call(o) === "[object RegExp]";
    198     };
    199 
    200     // Executes `callback` once per match within `str`. Provides a simpler and cleaner way to
    201     // iterate over regex matches compared to the traditional approaches of subverting
    202     // `String.prototype.replace` or repeatedly calling `exec` within a `while` loop
    203     XRegExp.iterate = function (str, regex, callback, context) {
    204         var r2 = clone(regex, "g"),
    205             i = -1, match;
    206         while (match = r2.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.)
    207             if (regex.global)
    208                 regex.lastIndex = r2.lastIndex; // Doing this to follow expectations if `lastIndex` is checked within `callback`
    209             callback.call(context, match, ++i, str, regex);
    210             if (r2.lastIndex === match.index)
    211                 r2.lastIndex++;
    212         }
    213         if (regex.global)
    214             regex.lastIndex = 0;
    215     };
    216 
    217     // Accepts a string and an array of regexes; returns the result of using each successive regex
    218     // to search within the matches of the previous regex. The array of regexes can also contain
    219     // objects with `regex` and `backref` properties, in which case the named or numbered back-
    220     // references specified are passed forward to the next regex or returned. E.g.:
    221     // var xregexpImgFileNames = XRegExp.matchChain(html, [
    222     //     {regex: /<img\b([^>]+)>/i, backref: 1}, // <img> tag attributes
    223     //     {regex: XRegExp('(?ix) \\s src=" (?<src> [^"]+ )'), backref: "src"}, // src attribute values
    224     //     {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths
    225     //     /[^\/]+$/ // filenames (strip directory paths)
    226     // ]);
    227     XRegExp.matchChain = function (str, chain) {
    228         return function recurseChain (values, level) {
    229             var item = chain[level].regex ? chain[level] : {regex: chain[level]},
    230                 regex = clone(item.regex, "g"),
    231                 matches = [], i;
    232             for (i = 0; i < values.length; i++) {
    233                 XRegExp.iterate(values[i], regex, function (match) {
    234                     matches.push(item.backref ? (match[item.backref] || "") : match[0]);
    235                 });
    236             }
    237             return ((level === chain.length - 1) || !matches.length) ?
    238                 matches : recurseChain(matches, level + 1);
    239         }([str], 0);
    240     };
    241 
    242 
    243     //---------------------------------
    244     //  New RegExp prototype methods
    245     //---------------------------------
    246 
    247     // Accepts a context object and arguments array; returns the result of calling `exec` with the
    248     // first value in the arguments array. the context is ignored but is accepted for congruity
    249     // with `Function.prototype.apply`
    250     RegExp.prototype.apply = function (context, args) {
    251         return this.exec(args[0]);
    252     };
    253 
    254     // Accepts a context object and string; returns the result of calling `exec` with the provided
    255     // string. the context is ignored but is accepted for congruity with `Function.prototype.call`
    256     RegExp.prototype.call = function (context, str) {
    257         return this.exec(str);
    258     };
    259 
    260 
    261     //---------------------------------
    262     //  Overriden native methods
    263     //---------------------------------
    264 
    265     // Adds named capture support (with backreferences returned as `result.name`), and fixes two
    266     // cross-browser issues per ES3:
    267     // - Captured values for nonparticipating capturing groups should be returned as `undefined`,
    268     //   rather than the empty string.
    269     // - `lastIndex` should not be incremented after zero-length matches.
    270     RegExp.prototype.exec = function (str) {
    271         var match, name, r2, origLastIndex;
    272         if (!this.global)
    273             origLastIndex = this.lastIndex;
    274         match = nativ.exec.apply(this, arguments);
    275         if (match) {
    276             // Fix browsers whose `exec` methods don't consistently return `undefined` for
    277             // nonparticipating capturing groups
    278             if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) {
    279                 r2 = RegExp(this.source, nativ.replace.call(getNativeFlags(this), "g", ""));
    280                 // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed
    281                 // matching due to characters outside the match
    282                 nativ.replace.call((str + "").slice(match.index), r2, function () {
    283                     for (var i = 1; i < arguments.length - 2; i++) {
    284                         if (arguments[i] === undefined)
    285                             match[i] = undefined;
    286                     }
    287                 });
    288             }
    289             // Attach named capture properties
    290             if (this._xregexp && this._xregexp.captureNames) {
    291                 for (var i = 1; i < match.length; i++) {
    292                     name = this._xregexp.captureNames[i - 1];
    293                     if (name)
    294                        match[name] = match[i];
    295                 }
    296             }
    297             // Fix browsers that increment `lastIndex` after zero-length matches
    298             if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
    299                 this.lastIndex--;
    300         }
    301         if (!this.global)
    302             this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
    303         return match;
    304     };
    305 
    306     // Fix browser bugs in native method
    307     RegExp.prototype.test = function (str) {
    308         // Use the native `exec` to skip some processing overhead, even though the altered
    309         // `exec` would take care of the `lastIndex` fixes
    310         var match, origLastIndex;
    311         if (!this.global)
    312             origLastIndex = this.lastIndex;
    313         match = nativ.exec.call(this, str);
    314         // Fix browsers that increment `lastIndex` after zero-length matches
    315         if (match && !compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
    316             this.lastIndex--;
    317         if (!this.global)
    318             this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
    319         return !!match;
    320     };
    321 
    322     // Adds named capture support and fixes browser bugs in native method
    323     String.prototype.match = function (regex) {
    324         if (!XRegExp.isRegExp(regex))
    325             regex = RegExp(regex); // Native `RegExp`
    326         if (regex.global) {
    327             var result = nativ.match.apply(this, arguments);
    328             regex.lastIndex = 0; // Fix IE bug
    329             return result;
    330         }
    331         return regex.exec(this); // Run the altered `exec`
    332     };
    333 
    334     // Adds support for `${n}` tokens for named and numbered backreferences in replacement text,
    335     // and provides named backreferences to replacement functions as `arguments[0].name`. Also
    336     // fixes cross-browser differences in replacement text syntax when performing a replacement
    337     // using a nonregex search value, and the value of replacement regexes' `lastIndex` property
    338     // during replacement iterations. Note that this doesn't support SpiderMonkey's proprietary
    339     // third (`flags`) parameter
    340     String.prototype.replace = function (search, replacement) {
    341         var isRegex = XRegExp.isRegExp(search),
    342             captureNames, result, str, origLastIndex;
    343 
    344         // There are too many combinations of search/replacement types/values and browser bugs that
    345         // preclude passing to native `replace`, so don't try
    346         //if (...)
    347         //    return nativ.replace.apply(this, arguments);
    348 
    349         if (isRegex) {
    350             if (search._xregexp)
    351                 captureNames = search._xregexp.captureNames; // Array or `null`
    352             if (!search.global)
    353                 origLastIndex = search.lastIndex;
    354         } else {
    355             search = search + ""; // Type conversion
    356         }
    357 
    358         if (Object.prototype.toString.call(replacement) === "[object Function]") {
    359             result = nativ.replace.call(this + "", search, function () {
    360                 if (captureNames) {
    361                     // Change the `arguments[0]` string primitive to a String object which can store properties
    362                     arguments[0] = new String(arguments[0]);
    363                     // Store named backreferences on `arguments[0]`
    364                     for (var i = 0; i < captureNames.length; i++) {
    365                         if (captureNames[i])
    366                             arguments[0][captureNames[i]] = arguments[i + 1];
    367                     }
    368                 }
    369                 // Update `lastIndex` before calling `replacement` (fix browsers)
    370                 if (isRegex && search.global)
    371                     search.lastIndex = arguments[arguments.length - 2] + arguments[0].length;
    372                 return replacement.apply(null, arguments);
    373             });
    374         } else {
    375             str = this + ""; // Type conversion, so `args[args.length - 1]` will be a string (given nonstring `this`)
    376             result = nativ.replace.call(str, search, function () {
    377                 var args = arguments; // Keep this function's `arguments` available through closure
    378                 return nativ.replace.call(replacement + "", replacementToken, function ($0, $1, $2) {
    379                     // Numbered backreference (without delimiters) or special variable
    380                     if ($1) {
    381                         switch ($1) {
    382                             case "$": return "$";
    383                             case "&": return args[0];
    384                             case "`": return args[args.length - 1].slice(0, args[args.length - 2]);
    385                             case "'": return args[args.length - 1].slice(args[args.length - 2] + args[0].length);
    386                             // Numbered backreference
    387                             default:
    388                                 // What does "$10" mean?
    389                                 // - Backreference 10, if 10 or more capturing groups exist
    390                                 // - Backreference 1 followed by "0", if 1-9 capturing groups exist
    391                                 // - Otherwise, it's the string "$10"
    392                                 // Also note:
    393                                 // - Backreferences cannot be more than two digits (enforced by `replacementToken`)
    394                                 // - "$01" is equivalent to "$1" if a capturing group exists, otherwise it's the string "$01"
    395                                 // - There is no "$0" token ("$&" is the entire match)
    396                                 var literalNumbers = "";
    397                                 $1 = +$1; // Type conversion; drop leading zero
    398                                 if (!$1) // `$1` was "0" or "00"
    399                                     return $0;
    400                                 while ($1 > args.length - 3) {
    401                                     literalNumbers = String.prototype.slice.call($1, -1) + literalNumbers;
    402                                     $1 = Math.floor($1 / 10); // Drop the last digit
    403                                 }
    404                                 return ($1 ? args[$1] || "" : "$") + literalNumbers;
    405                         }
    406                     // Named backreference or delimited numbered backreference
    407                     } else {
    408                         // What does "${n}" mean?
    409                         // - Backreference to numbered capture n. Two differences from "$n":
    410                         //   - n can be more than two digits
    411                         //   - Backreference 0 is allowed, and is the entire match
    412                         // - Backreference to named capture n, if it exists and is not a number overridden by numbered capture
    413                         // - Otherwise, it's the string "${n}"
    414                         var n = +$2; // Type conversion; drop leading zeros
    415                         if (n <= args.length - 3)
    416                             return args[n];
    417                         n = captureNames ? indexOf(captureNames, $2) : -1;
    418                         return n > -1 ? args[n + 1] : $0;
    419                     }
    420                 });
    421             });
    422         }
    423 
    424         if (isRegex) {
    425             if (search.global)
    426                 search.lastIndex = 0; // Fix IE, Safari bug (last tested IE 9.0.5, Safari 5.1.2 on Windows)
    427             else
    428                 search.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
    429         }
    430 
    431         return result;
    432     };
    433 
    434     // A consistent cross-browser, ES3 compliant `split`
    435     String.prototype.split = function (s /* separator */, limit) {
    436         // If separator `s` is not a regex, use the native `split`
    437         if (!XRegExp.isRegExp(s))
    438             return nativ.split.apply(this, arguments);
    439 
    440         var str = this + "", // Type conversion
    441             output = [],
    442             lastLastIndex = 0,
    443             match, lastLength;
    444 
    445         // Behavior for `limit`: if it's...
    446         // - `undefined`: No limit
    447         // - `NaN` or zero: Return an empty array
    448         // - A positive number: Use `Math.floor(limit)`
    449         // - A negative number: No limit
    450         // - Other: Type-convert, then use the above rules
    451         if (limit === undefined || +limit < 0) {
    452             limit = Infinity;
    453         } else {
    454             limit = Math.floor(+limit);
    455             if (!limit)
    456                 return [];
    457         }
    458 
    459         // This is required if not `s.global`, and it avoids needing to set `s.lastIndex` to zero
    460         // and restore it to its original value when we're done using the regex
    461         s = XRegExp.copyAsGlobal(s);
    462 
    463         while (match = s.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.)
    464             if (s.lastIndex > lastLastIndex) {
    465                 output.push(str.slice(lastLastIndex, match.index));
    466 
    467                 if (match.length > 1 && match.index < str.length)
    468                     Array.prototype.push.apply(output, match.slice(1));
    469 
    470                 lastLength = match[0].length;
    471                 lastLastIndex = s.lastIndex;
    472 
    473                 if (output.length >= limit)
    474                     break;
    475             }
    476 
    477             if (s.lastIndex === match.index)
    478                 s.lastIndex++;
    479         }
    480 
    481         if (lastLastIndex === str.length) {
    482             if (!nativ.test.call(s, "") || lastLength)
    483                 output.push("");
    484         } else {
    485             output.push(str.slice(lastLastIndex));
    486         }
    487 
    488         return output.length > limit ? output.slice(0, limit) : output;
    489     };
    490 
    491 
    492     //---------------------------------
    493     //  Private helper functions
    494     //---------------------------------
    495 
    496     // Supporting function for `XRegExp`, `XRegExp.copyAsGlobal`, etc. Returns a copy of a `RegExp`
    497     // instance with a fresh `lastIndex` (set to zero), preserving properties required for named
    498     // capture. Also allows adding new flags in the process of copying the regex
    499     function clone (regex, additionalFlags) {
    500         if (!XRegExp.isRegExp(regex))
    501             throw TypeError("type RegExp expected");
    502         var x = regex._xregexp;
    503         regex = XRegExp(regex.source, getNativeFlags(regex) + (additionalFlags || ""));
    504         if (x) {
    505             regex._xregexp = {
    506                 source: x.source,
    507                 captureNames: x.captureNames ? x.captureNames.slice(0) : null
    508             };
    509         }
    510         return regex;
    511     }
    512 
    513     function getNativeFlags (regex) {
    514         return (regex.global     ? "g" : "") +
    515                (regex.ignoreCase ? "i" : "") +
    516                (regex.multiline  ? "m" : "") +
    517                (regex.extended   ? "x" : "") + // Proposed for ES4; included in AS3
    518                (regex.sticky     ? "y" : "");
    519     }
    520 
    521     function runTokens (pattern, index, scope, context) {
    522         var i = tokens.length,
    523             result, match, t;
    524         // Protect against constructing XRegExps within token handler and trigger functions
    525         isInsideConstructor = true;
    526         // Must reset `isInsideConstructor`, even if a `trigger` or `handler` throws
    527         try {
    528             while (i--) { // Run in reverse order
    529                 t = tokens[i];
    530                 if ((scope & t.scope) && (!t.trigger || t.trigger.call(context))) {
    531                     t.pattern.lastIndex = index;
    532                     match = t.pattern.exec(pattern); // Running the altered `exec` here allows use of named backreferences, etc.
    533                     if (match && match.index === index) {
    534                         result = {
    535                             output: t.handler.call(context, match, scope),
    536                             match: match
    537                         };
    538                         break;
    539                     }
    540                 }
    541             }
    542         } catch (err) {
    543             throw err;
    544         } finally {
    545             isInsideConstructor = false;
    546         }
    547         return result;
    548     }
    549 
    550     function indexOf (array, item, from) {
    551         if (Array.prototype.indexOf) // Use the native array method if available
    552             return array.indexOf(item, from);
    553         for (var i = from || 0; i < array.length; i++) {
    554             if (array[i] === item)
    555                 return i;
    556         }
    557         return -1;
    558     }
    559 
    560 
    561     //---------------------------------
    562     //  Built-in tokens
    563     //---------------------------------
    564 
    565     // Augment XRegExp's regular expression syntax and flags. Note that when adding tokens, the
    566     // third (`scope`) argument defaults to `XRegExp.OUTSIDE_CLASS`
    567 
    568     // Comment pattern: (?# )
    569     XRegExp.addToken(
    570         /\(\?#[^)]*\)/,
    571         function (match) {
    572             // Keep tokens separated unless the following token is a quantifier
    573             return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)";
    574         }
    575     );
    576 
    577     // Capturing group (match the opening parenthesis only).
    578     // Required for support of named capturing groups
    579     XRegExp.addToken(
    580         /\((?!\?)/,
    581         function () {
    582             this.captureNames.push(null);
    583             return "(";
    584         }
    585     );
    586 
    587     // Named capturing group (match the opening delimiter only): (?<name>
    588     XRegExp.addToken(
    589         /\(\?<([$\w]+)>/,
    590         function (match) {
    591             this.captureNames.push(match[1]);
    592             this.hasNamedCapture = true;
    593             return "(";
    594         }
    595     );
    596 
    597     // Named backreference: \k<name>
    598     XRegExp.addToken(
    599         /\\k<([\w$]+)>/,
    600         function (match) {
    601             var index = indexOf(this.captureNames, match[1]);
    602             // Keep backreferences separate from subsequent literal numbers. Preserve back-
    603             // references to named groups that are undefined at this point as literal strings
    604             return index > -1 ?
    605                 "\\" + (index + 1) + (isNaN(match.input.charAt(match.index + match[0].length)) ? "" : "(?:)") :
    606                 match[0];
    607         }
    608     );
    609 
    610     // Empty character class: [] or [^]
    611     XRegExp.addToken(
    612         /\[\^?]/,
    613         function (match) {
    614             // For cross-browser compatibility with ES3, convert [] to \b\B and [^] to [\s\S].
    615             // (?!) should work like \b\B, but is unreliable in Firefox
    616             return match[0] === "[]" ? "\\b\\B" : "[\\s\\S]";
    617         }
    618     );
    619 
    620     // Mode modifier at the start of the pattern only, with any combination of flags imsx: (?imsx)
    621     // Does not support x(?i), (?-i), (?i-m), (?i: ), (?i)(?m), etc.
    622     XRegExp.addToken(
    623         /^\(\?([imsx]+)\)/,
    624         function (match) {
    625             this.setFlag(match[1]);
    626             return "";
    627         }
    628     );
    629 
    630     // Whitespace and comments, in free-spacing (aka extended) mode only
    631     XRegExp.addToken(
    632         /(?:\s+|#.*)+/,
    633         function (match) {
    634             // Keep tokens separated unless the following token is a quantifier
    635             return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)";
    636         },
    637         XRegExp.OUTSIDE_CLASS,
    638         function () {return this.hasFlag("x");}
    639     );
    640 
    641     // Dot, in dotall (aka singleline) mode only
    642     XRegExp.addToken(
    643         /\./,
    644         function () {return "[\\s\\S]";},
    645         XRegExp.OUTSIDE_CLASS,
    646         function () {return this.hasFlag("s");}
    647     );
    648 
    649 
    650     //---------------------------------
    651     //  Backward compatibility
    652     //---------------------------------
    653 
    654     // Uncomment the following block for compatibility with XRegExp 1.0-1.2:
    655     /*
    656     XRegExp.matchWithinChain = XRegExp.matchChain;
    657     RegExp.prototype.addFlags = function (s) {return clone(this, s);};
    658     RegExp.prototype.execAll = function (s) {var r = []; XRegExp.iterate(s, this, function (m) {r.push(m);}); return r;};
    659     RegExp.prototype.forEachExec = function (s, f, c) {return XRegExp.iterate(s, this, f, c);};
    660     RegExp.prototype.validate = function (s) {var r = RegExp("^(?:" + this.source + ")$(?!\\s)", getNativeFlags(this)); if (this.global) this.lastIndex = 0; return s.search(r) === 0;};
    661     */
    662 
    663 })();
    664