xregexp-rails 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,102 @@
1
+ /*
2
+ XRegExp Unicode plugin pack: Scripts 1.1
3
+ (c) 2010-2012 Steven Levithan
4
+ MIT License
5
+ <http://xregexp.com>
6
+
7
+ Uses the Unicode 6.1 character database:
8
+ <http://unicode.org/Public/6.1.0/ucd/Scripts.txt>
9
+
10
+ This package for the XRegExp Unicode plugin enables the 71 Unicode 6.1 scripts
11
+ in the Basic Multilingual Plane (U+0000-U+FFFF).
12
+
13
+ Example usage:
14
+
15
+ \p{Latin}
16
+ \p{Canadian Aboriginal}
17
+
18
+ Letter case, spaces, hyphens, and underscores are ignored in script names.
19
+ */
20
+
21
+ var XRegExp;
22
+
23
+ if (!(XRegExp && XRegExp.addUnicodePackage)) {
24
+ throw ReferenceError("XRegExp's Unicode plugin must be loaded before adding scripts");
25
+ }
26
+
27
+ XRegExp.addUnicodePackage({
28
+ Arabic: "0600-06040606-060B060D-061A061E0620-063F0641-064A0656-065E066A-066F0671-06DC06DE-06FF0750-077F08A008A2-08AC08E4-08FEFB50-FBC1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFCFE70-FE74FE76-FEFC",
29
+ Armenian: "0531-05560559-055F0561-0587058A058FFB13-FB17",
30
+ Balinese: "1B00-1B4B1B50-1B7C",
31
+ Bamum: "A6A0-A6F7",
32
+ Batak: "1BC0-1BF31BFC-1BFF",
33
+ Bengali: "0981-09830985-098C098F09900993-09A809AA-09B009B209B6-09B909BC-09C409C709C809CB-09CE09D709DC09DD09DF-09E309E6-09FB",
34
+ Bopomofo: "02EA02EB3105-312D31A0-31BA",
35
+ Braille: "2800-28FF",
36
+ Buginese: "1A00-1A1B1A1E1A1F",
37
+ Buhid: "1740-1753",
38
+ Canadian_Aboriginal: "1400-167F18B0-18F5",
39
+ Cham: "AA00-AA36AA40-AA4DAA50-AA59AA5C-AA5F",
40
+ Cherokee: "13A0-13F4",
41
+ Common: "0000-0040005B-0060007B-00A900AB-00B900BB-00BF00D700F702B9-02DF02E5-02E902EC-02FF0374037E038503870589060C061B061F06400660-066906DD096409650E3F0FD5-0FD810FB16EB-16ED173517361802180318051CD31CE11CE9-1CEC1CEE-1CF31CF51CF62000-200B200E-2064206A-20702074-207E2080-208E20A0-20B92100-21252127-2129212C-21312133-214D214F-215F21892190-23F32400-24262440-244A2460-26FF2701-27FF2900-2B4C2B50-2B592E00-2E3B2FF0-2FFB3000-300430063008-30203030-3037303C-303F309B309C30A030FB30FC3190-319F31C0-31E33220-325F327F-32CF3358-33FF4DC0-4DFFA700-A721A788-A78AA830-A839FD3EFD3FFDFDFE10-FE19FE30-FE52FE54-FE66FE68-FE6BFEFFFF01-FF20FF3B-FF40FF5B-FF65FF70FF9EFF9FFFE0-FFE6FFE8-FFEEFFF9-FFFD",
42
+ Coptic: "03E2-03EF2C80-2CF32CF9-2CFF",
43
+ Cyrillic: "0400-04840487-05271D2B1D782DE0-2DFFA640-A697A69F",
44
+ Devanagari: "0900-09500953-09630966-09770979-097FA8E0-A8FB",
45
+ Ethiopic: "1200-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A135D-137C1380-13992D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDEAB01-AB06AB09-AB0EAB11-AB16AB20-AB26AB28-AB2E",
46
+ Georgian: "10A0-10C510C710CD10D0-10FA10FC-10FF2D00-2D252D272D2D",
47
+ Glagolitic: "2C00-2C2E2C30-2C5E",
48
+ Greek: "0370-03730375-0377037A-037D038403860388-038A038C038E-03A103A3-03E103F0-03FF1D26-1D2A1D5D-1D611D66-1D6A1DBF1F00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FC41FC6-1FD31FD6-1FDB1FDD-1FEF1FF2-1FF41FF6-1FFE2126",
49
+ Gujarati: "0A81-0A830A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABC-0AC50AC7-0AC90ACB-0ACD0AD00AE0-0AE30AE6-0AF1",
50
+ Gurmukhi: "0A01-0A030A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A3C0A3E-0A420A470A480A4B-0A4D0A510A59-0A5C0A5E0A66-0A75",
51
+ Han: "2E80-2E992E9B-2EF32F00-2FD5300530073021-30293038-303B3400-4DB54E00-9FCCF900-FA6DFA70-FAD9",
52
+ Hangul: "1100-11FF302E302F3131-318E3200-321E3260-327EA960-A97CAC00-D7A3D7B0-D7C6D7CB-D7FBFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",
53
+ Hanunoo: "1720-1734",
54
+ Hebrew: "0591-05C705D0-05EA05F0-05F4FB1D-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FB4F",
55
+ Hiragana: "3041-3096309D-309F",
56
+ Inherited: "0300-036F04850486064B-0655065F0670095109521CD0-1CD21CD4-1CE01CE2-1CE81CED1CF41DC0-1DE61DFC-1DFF200C200D20D0-20F0302A-302D3099309AFE00-FE0FFE20-FE26",
57
+ Javanese: "A980-A9CDA9CF-A9D9A9DEA9DF",
58
+ Kannada: "0C820C830C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBC-0CC40CC6-0CC80CCA-0CCD0CD50CD60CDE0CE0-0CE30CE6-0CEF0CF10CF2",
59
+ Katakana: "30A1-30FA30FD-30FF31F0-31FF32D0-32FE3300-3357FF66-FF6FFF71-FF9D",
60
+ Kayah_Li: "A900-A92F",
61
+ Khmer: "1780-17DD17E0-17E917F0-17F919E0-19FF",
62
+ Lao: "0E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB90EBB-0EBD0EC0-0EC40EC60EC8-0ECD0ED0-0ED90EDC-0EDF",
63
+ Latin: "0041-005A0061-007A00AA00BA00C0-00D600D8-00F600F8-02B802E0-02E41D00-1D251D2C-1D5C1D62-1D651D6B-1D771D79-1DBE1E00-1EFF2071207F2090-209C212A212B2132214E2160-21882C60-2C7FA722-A787A78B-A78EA790-A793A7A0-A7AAA7F8-A7FFFB00-FB06FF21-FF3AFF41-FF5A",
64
+ Lepcha: "1C00-1C371C3B-1C491C4D-1C4F",
65
+ Limbu: "1900-191C1920-192B1930-193B19401944-194F",
66
+ Lisu: "A4D0-A4FF",
67
+ Malayalam: "0D020D030D05-0D0C0D0E-0D100D12-0D3A0D3D-0D440D46-0D480D4A-0D4E0D570D60-0D630D66-0D750D79-0D7F",
68
+ Mandaic: "0840-085B085E",
69
+ Meetei_Mayek: "AAE0-AAF6ABC0-ABEDABF0-ABF9",
70
+ Mongolian: "1800180118041806-180E1810-18191820-18771880-18AA",
71
+ Myanmar: "1000-109FAA60-AA7B",
72
+ New_Tai_Lue: "1980-19AB19B0-19C919D0-19DA19DE19DF",
73
+ NKo: "07C0-07FA",
74
+ Ogham: "1680-169C",
75
+ Ol_Chiki: "1C50-1C7F",
76
+ Oriya: "0B01-0B030B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3C-0B440B470B480B4B-0B4D0B560B570B5C0B5D0B5F-0B630B66-0B77",
77
+ Phags_Pa: "A840-A877",
78
+ Rejang: "A930-A953A95F",
79
+ Runic: "16A0-16EA16EE-16F0",
80
+ Samaritan: "0800-082D0830-083E",
81
+ Saurashtra: "A880-A8C4A8CE-A8D9",
82
+ Sinhala: "0D820D830D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60DCA0DCF-0DD40DD60DD8-0DDF0DF2-0DF4",
83
+ Sundanese: "1B80-1BBF1CC0-1CC7",
84
+ Syloti_Nagri: "A800-A82B",
85
+ Syriac: "0700-070D070F-074A074D-074F",
86
+ Tagalog: "1700-170C170E-1714",
87
+ Tagbanwa: "1760-176C176E-177017721773",
88
+ Tai_Le: "1950-196D1970-1974",
89
+ Tai_Tham: "1A20-1A5E1A60-1A7C1A7F-1A891A90-1A991AA0-1AAD",
90
+ Tai_Viet: "AA80-AAC2AADB-AADF",
91
+ Tamil: "0B820B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BBE-0BC20BC6-0BC80BCA-0BCD0BD00BD70BE6-0BFA",
92
+ Telugu: "0C01-0C030C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D-0C440C46-0C480C4A-0C4D0C550C560C580C590C60-0C630C66-0C6F0C78-0C7F",
93
+ Thaana: "0780-07B1",
94
+ Thai: "0E01-0E3A0E40-0E5B",
95
+ Tibetan: "0F00-0F470F49-0F6C0F71-0F970F99-0FBC0FBE-0FCC0FCE-0FD40FD90FDA",
96
+ Tifinagh: "2D30-2D672D6F2D702D7F",
97
+ Vai: "A500-A62B",
98
+ Yi: "A000-A48CA490-A4C6"
99
+ });
100
+
101
+ // Excludes 31 Unicode 6.1 scripts with no code points in the BMP.
102
+
@@ -0,0 +1,664 @@
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
+