PageSourceSearch

https://www.smart-winners.com/blog/en/scripts/syntaxhighlighter/scripts/XRegExp.js

js smart-winners.com collected 2026-10-03 00:24:42 UTC 29,440 bytes, 664 lines download raw bytes

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
8var XRegExp;
9
10if (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 nonp
113articipating 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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.