xregexp-rails 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/app/assets/javascripts/xregexp-matchrecursive.js +151 -0
- data/app/assets/javascripts/xregexp-unicode-base.js +73 -0
- data/app/assets/javascripts/xregexp-unicode-blocks.js +187 -0
- data/app/assets/javascripts/xregexp-unicode-categories.js +102 -0
- data/app/assets/javascripts/xregexp-unicode-scripts.js +102 -0
- data/app/assets/javascripts/xregexp.js +664 -0
- data/lib/xregexp-rails.rb +9 -0
- data/lib/xregexp-rails/version.rb +12 -0
- metadata +75 -0
@@ -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
|
+
|