333
schangxiang@126.com
2025-09-19 18966e02fb573c7e2bb0c6426ed792b38b910940
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
// In Node.JS, `module` is a predefined object, which makes the QUnit `module` definitions fail
// unless we redefine it.
module = QUnit.module;
 
// When using node-qunit on the command line, the module is imported as is and we need to point at
// the XRegExp class inside the module. This does nothing in the browser, where XRegExp is already
// loaded in the global scope.
if (typeof XRegExp === "undefined" && typeof xregexp !== "undefined") {
    var XRegExp = xregexp.XRegExp;
}
 
//-------------------------------------------------------------------
module("API");
//-------------------------------------------------------------------
 
test("Basic availability", function () {
    ok(XRegExp, "XRegExp exists");
    ok(XRegExp.addToken, "XRegExp.addToken exists");
    ok(XRegExp.cache, "XRegExp.cache exists");
    ok(XRegExp.escape, "XRegExp.escape exists");
    ok(XRegExp.exec, "XRegExp.exec exists");
    ok(XRegExp.forEach, "XRegExp.forEach exists");
    ok(XRegExp.globalize, "XRegExp.globalize exists");
    ok(XRegExp.install, "XRegExp.install exists");
    ok(XRegExp.isInstalled, "XRegExp.isInstalled exists");
    ok(XRegExp.isRegExp, "XRegExp.isRegExp exists");
    ok(XRegExp.matchChain, "XRegExp.matchChain exists");
    ok(XRegExp.replace, "XRegExp.replace exists");
    ok(XRegExp.split, "XRegExp.split exists");
    ok(XRegExp.test, "XRegExp.test exists");
    ok(XRegExp.uninstall, "XRegExp.uninstall exists");
    ok(XRegExp.union, "XRegExp.union exists");
    ok(XRegExp.version, "XRegExp.version exists");
});
 
test("XRegExp", function () {
    var blankRegex = XRegExp("(?:)"),
        regexGIM = XRegExp("(?:)", "gim");
 
    equal(XRegExp("").source, new RegExp("").source, "Empty regex source (test 1)");
    equal(XRegExp("(?:)").source, /(?:)/.source, "Empty regex source (test 2)");
    equal(XRegExp().source, new RegExp().source, "undefined regex source");
    equal(XRegExp(null).source, new RegExp(null).source, "null regex source");
    equal(XRegExp(NaN).source, new RegExp(NaN).source, "NaN regex source");
    equal(XRegExp(1).source, new RegExp(1).source, "numeric regex source");
    equal(XRegExp({}).source, new RegExp({}).source, "object regex source");
    equal(XRegExp("").global, false, "Regex without flags is not global");
    ok(XRegExp("", "g").global, "Regex with global flag is global");
    ok(XRegExp("", "i").ignoreCase, "Regex with ignoreCase flag is ignoreCase");
    ok(XRegExp("", "m").multiline, "Regex with multiline flag is multiline");
    ok(regexGIM.global && regexGIM.ignoreCase && regexGIM.multiline, "Regex with flags gim is global, ignoreCase, multiline");
    deepEqual(blankRegex, XRegExp(blankRegex), "Regex copy and original are alike");
    notEqual(blankRegex, XRegExp(blankRegex), "Regex copy is new instance");
    ok(XRegExp("").xregexp, "XRegExp has xregexp property");
    notStrictEqual(XRegExp("").xregexp.captureNames, undefined, "XRegExp has captureNames property");
    equal(XRegExp("").xregexp.captureNames, null, "Empty XRegExp has null captureNames");
    notStrictEqual(XRegExp("").xregexp.isNative, undefined, "XRegExp has isNative property");
    equal(XRegExp("").xregexp.isNative, false, "XRegExp has isNative false");
    equal(XRegExp(XRegExp("")).xregexp.isNative, false, "Copied XRegExp has isNative false");
    equal(XRegExp(new RegExp("")).xregexp.isNative, true, "Copied RegExp has isNative true");
    equal(XRegExp.exec("aa", XRegExp(XRegExp("(?<name>a)\\k<name>"))).name, "a", "Copied XRegExp retains named capture properties");
    raises(function () {XRegExp(/(?:)/, "g");}, Error, "Regex copy with flag throws");
    ok(XRegExp("") instanceof RegExp, "XRegExp object is instanceof RegExp");
    equal(XRegExp("").constructor, RegExp, "XRegExp object constructor is RegExp");
    raises(function () {XRegExp("", "gg");}, SyntaxError, "Regex with duplicate native flags throws");
    raises(function () {XRegExp("", "ss");}, SyntaxError, "Regex with duplicate nonnative flags throws (test 1)");
    raises(function () {XRegExp("", "sis");}, SyntaxError, "Regex with duplicate nonnative flags throws (test 2)");
    raises(function () {XRegExp("", "?");}, SyntaxError, "Unsupported flag throws");
    ok(!XRegExp("(?:)", "x").extended, "Nonnative flag x does not set extended property");
});
 
test("XRegExp.addToken", function () {
    XRegExp.install("extensibility");
    XRegExp.addToken(/\x01/, function () {return "1";});
    XRegExp.addToken(/\x02/, function () {return "2";}, {scope: "class"});
    XRegExp.addToken(/\x03/, function () {return "3";}, {scope: "default"});
    XRegExp.addToken(/\x04/, function () {return "4";}, {scope: "all"});
    XRegExp.addToken(/\x05/, function () {return "5";}, {
        scope: "default",
        trigger: function () {return this.hasFlag("5");},
        customFlags: "5"
    });
    XRegExp.uninstall("extensibility");
 
    ok(XRegExp("\x01").test("1"), "Default scope matches outside class");
    ok(!XRegExp("[\x01]").test("1"), "Default scope doesn't match inside class");
    ok(!XRegExp("\x02").test("2"), "Explicit class scope doesn't match outside class");
    ok(XRegExp("[\x02]").test("2"), "Explicit class scope matches inside class");
    ok(XRegExp("\x03").test("3"), "Explicit default scope matches outside class");
    ok(!XRegExp("[\x03]").test("3"), "Explicit default scope doesn't match inside class");
    ok(XRegExp("\x04").test("4"), "Explicit all scope matches outside class");
    ok(XRegExp("[\x04]").test("4"), "Explicit all scope matches inside class");
    ok(!XRegExp("\x05").test("5"), "Trigger with hasFlag skips token when flag is missing");
    ok(XRegExp("\x05", "5").test("5"), "Trigger with hasFlag uses token when flag is included");
});
 
test("XRegExp.cache", function () {
    var cached1 = XRegExp.cache("(?:)");
    var cached2 = XRegExp.cache("(?:)");
    var regexWithFlags = XRegExp(". +()\\1 1", "gimsx");
 
    ok(cached1 instanceof RegExp, "Returns RegExp");
    strictEqual(cached1, cached2, "References to separately cached patterns refer to same object");
    deepEqual(XRegExp.cache(". +()\\1 1", "gimsx"), regexWithFlags, "Cached pattern plus flags");
});
 
test("XRegExp.escape", function () {
    equal(XRegExp.escape("[()*+?.\\^$|"), "\\[\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|", "Metacharacters are escaped");
    equal(XRegExp.escape("]{}-, #"), "\\]\\{\\}\\-\\,\\ \\#", "Occasional metacharacters are escaped");
    equal(XRegExp.escape("abc_<123>!"), "abc_<123>!", "Nonmetacharacters are not escaped");
});
 
test("XRegExp.exec", function () {
    var rX = /x/g;
    var rA = /a/g;
    var xregexp = XRegExp("(?<name>a)"); // tests expect this to be nonglobal and use named capture
    var str = "abcxdef";
    var match;
 
    ok(XRegExp.exec(str, rX, 2), "Pos test 1");
    ok(!XRegExp.exec(str, rX, 5), "Pos test 2");
 
    rX.lastIndex = 5;
    ok(XRegExp.exec(str, rX, 2), "Pos ignores lastIndex test 1");
 
    rX.lastIndex = 0;
    ok(!XRegExp.exec(str, rX, 5), "Pos ignores lastIndex test 2");
 
    rA.lastIndex = 5;
    ok(XRegExp.exec(str, rA), "Pos ignores lastIndex test 3 (pos defaults to 0)");
 
    ok(XRegExp.exec(str, rX, 0), "Undefined sticky allows matching after pos");
    ok(XRegExp.exec(str, rX, 0, false), "Explicit sticky=false allows matching after pos");
    ok(!XRegExp.exec(str, rX, 0, true), "Sticky match fails if match possible after (but not at) pos");
    ok(!XRegExp.exec(str, rX, 0, "sticky"), "String 'sticky' triggers sticky mode");
    ok(XRegExp.exec(str, rX, 3, true), "Sticky match succeeds if match at pos");
    equal(XRegExp.exec(str, rX, 5), null, "Result of failure is null");
    deepEqual(XRegExp.exec(str, xregexp), ["a", "a"], "Result of successful match is array with backreferences");
 
    match = XRegExp.exec(str, xregexp);
    equal(match.name, "a", "Match result includes named capture properties");
 
    xregexp.lastIndex = 5;
    XRegExp.exec(str, xregexp);
    equal(xregexp.lastIndex, 5, "lastIndex of nonglobal regex left as is");
 
    rX.lastIndex = 0;
    XRegExp.exec(str, rX);
    equal(rX.lastIndex, 4, "lastIndex of global regex updated to end of match");
 
    rX.lastIndex = 5;
    XRegExp.exec(str, rX, 2, true);
    equal(rX.lastIndex, 0, "lastIndex of global regex updated to 0 after failure");
 
    equal(XRegExp.exec("abc", /x/, 5), null, "pos greater than string length results in failure");
 
    if (RegExp.prototype.sticky !== undefined) {
        var stickyRegex = new RegExp("x", "y"); // can't use /x/y even behind `if` because it errors during compilation in IE9
        ok(XRegExp.exec(str, stickyRegex, 0, false), "Explicit sticky=false overrides flag y");
        ok(!XRegExp.exec(str, stickyRegex, 0), "Sticky follows flag y when not explicitly specified");
    }
});
 
test("XRegExp.forEach", function () {
    var str = "abc 123 def";
    var regex = XRegExp("(?<first>\\w)\\w*");
    var regexG = XRegExp("(?<first>\\w)\\w*", "g");
 
    deepEqual(XRegExp.forEach(str, regex, function (m) {this.push(m[0]);}, []), ["abc", "123", "def"], "Match strings with nonglobal regex");
    deepEqual(XRegExp.forEach(str, regexG, function (m) {this.push(m[0]);}, []), ["abc", "123", "def"], "Match strings with global regex");
    deepEqual(XRegExp.forEach(str, regex, function (m) {this.push(m.first);}, []), ["a", "1", "d"], "Named backreferences");
    deepEqual(XRegExp.forEach(str, regex, function (m) {this.push(m.index);}, []), [0, 4, 8], "Match indexes");
    deepEqual(XRegExp.forEach(str, regex, function (m, i) {this.push(i);}, []), [0, 1, 2], "Match numbers");
    deepEqual(XRegExp.forEach(str, regex, function (m, i, s) {this.push(s);}, []), [str, str, str], "Source strings");
    deepEqual(XRegExp.forEach(str, regex, function (m, i, s, r) {this.push(r);}, []), [regex, regex, regex], "Source regexes");
 
    var str2 = str;
    deepEqual(XRegExp.forEach(str2, regex, function (m, i, s) {this.push(s); s += s; str2 += str2;}, []), [str, str, str], "Source string manipulation in callback doesn't affect iteration");
 
    var regex2 = XRegExp(regex);
    deepEqual(XRegExp.forEach(str, regex2, function (m, i, s, r) {this.push(i); r = /x/; regex2 = /x/;}, []), [0, 1, 2], "Source regex manipulation in callback doesn't affect iteration");
 
    regexG.lastIndex = 4;
    deepEqual(XRegExp.forEach(str, regexG, function (m) {this.push(m[0]);}, []), ["abc", "123", "def"], "Iteration starts at pos 0, ignoring lastIndex");
 
    regex.lastIndex = 4;
    XRegExp.forEach(str, regex, function () {});
    equal(regex.lastIndex, 4, "lastIndex of nonglobal regex unmodified after iteration");
 
    regexG.lastIndex = 4;
    XRegExp.forEach(str, regexG, function () {});
    equal(regexG.lastIndex, 0, "lastIndex of global regex reset to 0 after iteration");
 
    var rgOrig = /\d+/g, interimLastIndex1 = 0, interimLastIndex2 = 0;
    XRegExp.forEach(str, rgOrig, function (m, i, s, r) {
        interimLastIndex1 = rgOrig.lastIndex;
        interimLastIndex2 = r.lastIndex;
    });
    equal(interimLastIndex1, 7, "Global regex lastIndex updated during iterations (test 1)");
    equal(interimLastIndex2, 7, "Global regex lastIndex updated during iterations (test 2)");
 
    var rOrig = /\d+/, interimLastIndex1 = 0, interimLastIndex2 = 0;
    XRegExp.forEach(str, rOrig, function (m, i, s, r) {
        interimLastIndex1 = rOrig.lastIndex;
        interimLastIndex2 = r.lastIndex;
    });
    equal(interimLastIndex1, 0, "Nonglobal regex lastIndex not updated during iterations (test 1)");
    equal(interimLastIndex2, 0, "Nonglobal regex lastIndex not updated during iterations (test 2)");
});
 
test("XRegExp.globalize", function () {
    var hasNativeY = typeof RegExp.prototype.sticky !== "undefined";
    var regex = XRegExp("(?<name>a)\\k<name>", "im" + (hasNativeY ? "y" : ""));
    var globalCopy = XRegExp.globalize(regex);
    var globalOrig = XRegExp("(?:)", "g");
 
    notEqual(regex, globalCopy, "Copy is new instance");
    ok(globalCopy.global, "Copy is global");
    equal(regex.source, globalCopy.source, "Copy has same source");
    ok(regex.ignoreCase === globalCopy.ignoreCase && regex.multiline === globalCopy.multiline && regex.sticky === globalCopy.sticky, "Copy has same ignoreCase, multiline, and sticky properties");
    ok(XRegExp.exec("aa", globalCopy).name, "Copy retains named capture capabilities");
    ok(XRegExp.globalize(globalOrig).global, "Copy of global regex is global");
});
 
test("XRegExp.install", function () {
    expect(0);
    // TODO: Add tests
});
 
test("XRegExp.isInstalled", function () {
    expect(0);
    // TODO: Add tests
});
 
test("XRegExp.isRegExp", function () {
    ok(XRegExp.isRegExp(/(?:)/), "Regex built by regex literal is RegExp");
    ok(XRegExp.isRegExp(RegExp("(?:)")), "Regex built by RegExp is RegExp");
    ok(XRegExp.isRegExp(XRegExp("(?:)")), "Regex built by XRegExp is RegExp");
    ok(!XRegExp.isRegExp(undefined), "undefined is not RegExp");
    ok(!XRegExp.isRegExp(null), "null is not RegExp");
    ok(!XRegExp.isRegExp({}), "Object literal is not RegExp");
    ok(!XRegExp.isRegExp(function () {}), "Function literal is not RegExp");
 
    var fakeRegex = {};
    fakeRegex.constructor = RegExp;
    ok(!XRegExp.isRegExp(fakeRegex), "Object with assigned RegExp constructor is not RegExp");
 
    var tamperedRegex = /x/;
    tamperedRegex.constructor = {};
    ok(XRegExp.isRegExp(tamperedRegex), "RegExp with assigned Object constructor is RegExp");
 
    // Check whether `document` exists and only run the frame test if so. This ensures the test is
    // run only in the browser and not in server-side environments without a DOM.
    if (typeof document !== "undefined") {
        var iframe = document.createElement("iframe");
        iframe.width = iframe.height = iframe.border = 0; //iframe.style.display = "none";
        document.body.appendChild(iframe);
        frames[frames.length - 1].document.write("<script>var regex = /x/;<\/script>");
        ok(XRegExp.isRegExp(iframe.contentWindow.regex), "RegExp constructed in another frame is RegExp");
        iframe.parentNode.removeChild(iframe); // cleanup
    }
});
 
test("XRegExp.matchChain", function () {
    var html = '<html><img src="http://x.com/img.png"><script src="http://xregexp.com/path/file.ext"><img src="http://xregexp.com/path/to/img.jpg?x"><img src="http://xregexp.com/img2.gif"/></html>';
    var xregexpImgFileNames = XRegExp.matchChain(html, [
        {regex: /<img\b([^>]+)>/i, backref: 1}, // <img> tag attributes
        {regex: XRegExp('(?ix) \\s src=" (?<src> [^"]+ )'), backref: "src"}, // src attribute values
        {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths
        /[^\/]+$/ // filenames (strip directory paths)
    ]);
 
    deepEqual(xregexpImgFileNames, ["img.jpg", "img2.gif"], "Four-level chain with plain regex and regex/backref objects (using named and numbered backrefs)");
    deepEqual(XRegExp.matchChain("x", [/x/, /y/]), [], "Empty array returned if no matches");
    raises(function () {XRegExp.matchChain(html, []);}, Error, "Empty chain regex throws error");
});
 
test("XRegExp.replace", function () {
    equal(XRegExp.replace("test", "t", "x", "all"), "xesx", "string search with scope='all'");
    equal(XRegExp.replace("test", "t", "x", "one"), "xest", "string search with scope='one'");
    equal(XRegExp.replace("test", "t", "x"), "xest", "string search without scope");
    equal(XRegExp.replace("test", /t/, "x", "all"), "xesx", "regex search with scope='all'");
    equal(XRegExp.replace("test", /t/, "x", "one"), "xest", "regex search with scope='one'");
    equal(XRegExp.replace("test", /t/, "x"), "xest", "regex search without scope");
    equal(XRegExp.replace("test", /t/g, "x", "all"), "xesx", "global regex search with scope='all'");
    equal(XRegExp.replace("test", /t/g, "x", "one"), "xest", "global regex search with scope='one'");
    equal(XRegExp.replace("test", /t/g, "x"), "xesx", "global regex search without scope");
 
    // TODO: Add tests (above tests cover scope functionality only)
});
 
test("XRegExp.split", function () {
    expect(0);
    // TODO: Add tests
});
 
test("XRegExp.test", function () {
    expect(0);
    // TODO: Add tests
});
 
test("XRegExp.uninstall", function () {
    expect(0);
    // TODO: Add tests
});
 
test("XRegExp.union", function () {
    equal(XRegExp.union([XRegExp("(?<a>a)\\k<a>")], "n").test("aa"), true, "Apply flag n (test 1)");
    raises(function () {XRegExp.union([XRegExp("(?<a>a)\\k<a>"), /(b)\1/], "n");}, SyntaxError, "Apply flag n (test 2)");
    raises(function () {XRegExp.union([XRegExp("(?<a>a)\\k<a>"), /(b)\1/, XRegExp("(?<x>)")], "n");}, SyntaxError, "Apply flag n (test 3)");
 
    // TODO: Add tests
});
 
test("XRegExp.version", function () {
    var parts = XRegExp.version.split(".");
 
    equal(typeof XRegExp.version, "string", "Version is a string");
    equal(parts.length, 3, "Version is three dot-delimited parts");
    ok(!(isNaN(+parts[0]) || isNaN(+parts[1])), "Major and minor version parts are numeric");
});
 
//-------------------------------------------------------------------
module("Overriden natives");
//-------------------------------------------------------------------
 
test("RegExp.prototype.exec", function () {
    XRegExp.install("natives");
 
    deepEqual(/x/.exec("a"), null, "Nonmatch returns null");
    deepEqual(/a/.exec("a"), ["a"], "Match returns array");
    deepEqual(/(a)/.exec("a"), ["a", "a"], "Match returns array with backreferences");
    deepEqual(/()??/.exec("a"), ["", undefined], "Backrefernces to nonparticipating capturing groups returned as undefined");
    equal(/a/.exec("12a").index, 2, "Match array has index set to match start");
    equal(/a/.exec("12a").input, "12a", "Match array has input set to target string");
 
    var regex = /x/;
    regex.exec("123x567");
    equal(regex.lastIndex, 0, "Nonglobal regex lastIndex is 0 after match");
 
    regex.lastIndex = 1;
    regex.exec("123x567");
    equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after match");
 
    regex.exec("abc");
    equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after failure");
 
    var regexG = /x/g;
    regexG.exec("123x567");
    equal(regexG.lastIndex, 4, "Global regex lastIndex is updated after match");
 
    regexG.lastIndex = 4;
    equal(regexG.exec("123x567"), null, "Global regex starts match at lastIndex");
 
    equal(regexG.lastIndex, 0, "Global regex lastIndex reset to 0 after failure");
 
    var regexZeroLength = /^/g;
    regexZeroLength.exec("abc");
    equal(regexZeroLength.lastIndex, 0, "Global regex lastIndex is not incremented after zero-length match");
 
    regexG.lastIndex = "3";
    deepEqual(regexG.exec("123x567"), ["x"], "lastIndex converted to integer (test 1)");
 
    regexG.lastIndex = "4";
    deepEqual(regexG.exec("123x567"), null, "lastIndex converted to integer (test 2)");
 
    deepEqual(/1/.exec(1), ["1"], "Numeric argument converted to string (test 1)");
    deepEqual(/1()/.exec(1), ["1", ""], "Numeric argument converted to string (test 2)");
    deepEqual(/null/.exec(null), ["null"], "null argument converted to string");
    deepEqual(/NaN/.exec(NaN), ["NaN"], "NaN argument converted to string");
    // This is broken in old Firefox (tested v2.0; it works in v8+), but not for any fault of XRegExp.
    // Uncomment this test if future XRegExp fixes it for old Firefox.
    //deepEqual(/undefined/.exec(), ["undefined"], "undefined argument converted to string");
    raises(function () {RegExp.prototype.exec.call("\\d", "1");}, TypeError, "TypeError thrown when context is not type RegExp");
 
    XRegExp.uninstall("natives");
});
 
test("RegExp.prototype.test", function () {
    XRegExp.install("natives");
 
    deepEqual(/x/.test("a"), false, "Nonmatch returns false");
    deepEqual(/a/.test("a"), true, "Match returns true");
 
    var regex = /x/;
    regex.test("123x567");
    equal(regex.lastIndex, 0, "Nonglobal regex lastIndex is 0 after match");
 
    regex.lastIndex = 1;
    regex.test("123x567");
    equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after match");
 
    regex.test("abc");
    equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after failure");
 
    var regexG = /x/g;
    regexG.test("123x567");
    equal(regexG.lastIndex, 4, "Global regex lastIndex is updated after match");
 
    regexG.lastIndex = 4;
    equal(regexG.test("123x567"), false, "Global regex starts match at lastIndex");
 
    equal(regexG.lastIndex, 0, "Global regex lastIndex reset to 0 after failure");
 
    var regexZeroLength = /^/g;
    regexZeroLength.test("abc");
    equal(regexZeroLength.lastIndex, 0, "Global regex lastIndex is not incremented after zero-length match");
 
    regexG.lastIndex = "3";
    deepEqual(regexG.test("123x567"), true, "lastIndex converted to integer (test 1)");
 
    regexG.lastIndex = "4";
    deepEqual(regexG.test("123x567"), false, "lastIndex converted to integer (test 2)");
 
    deepEqual(/1/.test(1), true, "Argument converted to string");
    raises(function () {RegExp.prototype.test.call("\\d", "1");}, TypeError, "TypeError thrown when context is not type RegExp");
 
    XRegExp.uninstall("natives");
});
 
test("String.prototype.match", function () {
    XRegExp.install("natives");
 
    deepEqual("a".match(/x/), null, "Nonglobal regex: Nonmatch returns null");
    deepEqual("a".match(/a/), ["a"], "Nonglobal regex: Match returns array");
    deepEqual("a".match(/(a)/), ["a", "a"], "Nonglobal regex: Match returns array with backreferences");
    deepEqual("a".match(/()??/), ["", undefined], "Nonglobal regex: Backrefernces to nonparticipating capturing groups returned as undefined");
    equal("12a".match(/a/).index, 2, "Nonglobal regex: Match array has index set to match start");
    equal("12a".match(/a/).input, "12a", "Nonglobal regex: Match array has input set to target string");
 
    var regex = /x/;
    "123x567".match(regex);
    equal(regex.lastIndex, 0, "Nonglobal regex: lastIndex is 0 after match");
 
    regex.lastIndex = 1;
    "123x567".match(regex);
    equal(regex.lastIndex, 1, "Nonglobal regex: lastIndex is unmodified after match");
 
    "abc".match(regex);
    equal(regex.lastIndex, 1, "Nonglobal regex: lastIndex is unmodified after failure");
 
    var regexG = /x/g;
    "123x567".match(regexG);
    equal(regexG.lastIndex, 0, "Global regex: lastIndex is 0 after match");
 
    regexG.lastIndex = 4;
    deepEqual("123x567".match(regexG), ["x"], "Global regex: Search starts at pos zero despite lastIndex");
 
    regexG.lastIndex = 4;
    "abc".match(regexG);
    equal(regexG.lastIndex, 0, "Global regex: lastIndex reset to 0 after failure");
 
    deepEqual("1".match("^(1)"), ["1", "1"], "Argument converted to RegExp");
    deepEqual(String.prototype.match.call(1, /1/), ["1"], "Nonstring context is converted to string");
 
    XRegExp.uninstall("natives");
});
 
test("String.prototype.replace", function () {
    XRegExp.install("natives");
 
    equal("xaaa".replace(/a/, "b"), "xbaa", "Basic nonglobal regex search");
    equal("xaaa".replace(/a/g, "b"), "xbbb", "Basic global regex search");
    equal("xaaa".replace("a", "b"), "xbaa", "Basic string search");
    equal("xaaa".replace(/a(a)/, "$1b"), "xaba", "Backreference $1 in replacement string");
    equal("xaaa".replace(/a(a)/, "$01b"), "xaba", "Backreference $01 in replacement string");
    equal("xaaa".replace(/a()()()()()()()()()(a)/, "$10b"), "xaba", "Backreference $11 in replacement string");
    equal("xaaa".replace(/a()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()(a)/, "$99b"), "xaba", "Backreference $99 in replacement string");
    equal("xaaa".replace(/a()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()(a)/, "$100b"), "x0ba", "$100 in replacement string");
    equal("xaaa".replace(/aa/, "$&b"), "xaaba", "Backreference $& in replacement string");
    equal("xaaa".replace(/aa/, "$'b"), "xaba", "Backreference $' in replacement string");
    equal("xaaa".replace(/aa/, "$`b"), "xxba", "Backreference $` in replacement string");
    equal("xaaa".replace(/aa/, "$$b"), "x$ba", "$$ in replacement string");
    equal("xaaa".replace("a(a)", "$1b"), "xaaa", "Parentheses in string search doesn't match");
    equal("xaaa".replace("aa", "$&b"), "xaaba", "Backreference $& in replacement string for string search");
    equal("xaaa".replace("aa", "$'b"), "xaba", "Backreference $' in replacement string for string search");
    equal("xaaa".replace("aa", "$`b"), "xxba", "Backreference $` in replacement string for string search");
    equal("xaaa".replace("aa", "$$b"), "x$ba", "$$ in replacement string for string search");
    equal("xaaa".replace(/a/, function () {return "b";}), "xbaa", "Nonglobal regex search with basic function replacement");
    equal("xaaa".replace(/a/g, function () {return "b";}), "xbbb", "Global regex search with basic function replacement");
    equal("xaaa".replace(/aa/, function ($0) {return $0 + "b";}), "xaaba", "Regex search with function replacement, using match");
    equal("xaaa".replace(/a(a)/, function ($0, $1) {return $1 + "b";}), "xaba", "Regex search with function replacement, using backreference 1");
    equal("xaaa".replace(/a(a)/, function ($0, $1) {return "$1b";}), "x$1ba", "Regex search with function replacement, using $1 in return string");
    equal("xaaa".replace(/a/, function () {return "$&b";}), "x$&baa", "Regex search with function replacement, using $& in return string");
    equal("xaaa".replace(/a/g, function ($0, pos) {return "" + pos;}), "x123", "Regex search with function replacement, using pos in return string");
    equal("xaaa".replace(/(a)/g, function ($0, $1, pos) {return "" + pos;}), "x123", "Regex (with capturing group) search with function replacement, using pos in return string");
    equal("xaaa".replace(/a/, function ($0, pos, str) {return str;}), "xxaaaaa", "Regex search with function replacement, using source string in return string");
    equal("xaaa".replace(/(a)/, function ($0, $1, pos, str) {return str;}), "xxaaaaa", "Regex (with capturing group) search with function replacement, using source string in return string");
    equal("xaaa".replace("a", function () {return "b";}), "xbaa", "String search with basic function replacement");
    equal("xaaa".replace("a", function ($0) {return $0;}), "xaaa", "String search with function replacement, using match");
    // This is broken in Safari (tested v5.1.2/7534.52.7), but not for any fault of XRegExp.
    // Uncomment this test if future XRegExp fixes it for Safari.
    //equal("xaaa".replace("a", function () {return "$&";}), "x$&aa", "String search with function replacement, using $& in return string");
    equal("xaaa".replace("a", function ($0, pos) {return "" + pos;}), "x1aa", "String search with function replacement, using pos in return string");
    equal("xaaa".replace("a", function ($0, pos, str) {return str;}), "xxaaaaa", "String search with function replacement, using source string in return string");
    equal(String.prototype.replace.call(100, /0/g, "x"), "1xx", "Number as context");
    equal(String.prototype.replace.call(100, /(0)/g, "$1x"), "10x0x", "Number as context with backreference $1 in replacement string");
    equal(String.prototype.replace.call(100, /0/g, function ($0) {return $0 + "x";}), "10x0x", "Number as context with function replacement");
    equal(String.prototype.replace.call(100, "0", "x"), "1x0", "String search with number as context");
    equal(String.prototype.replace.call(100, "0", "$&x"), "10x0", "String search with number as context, with backreference $& in replacement string");
    equal(String.prototype.replace.call(["a","b"], /,/g, "x"), "axb", "Array as context");
    equal("10x10".replace(10, "x"), "xx10", "Number as search (converted to string)");
    equal("xaaa,ba,b".replace(["a","b"], "x"), "xaaxa,b", "Array as search (converted to string)");
    equal("xaaa".replace(/a/g, 1.1), "x1.11.11.1", "Number as replacement (converted to string)");
    equal("xaaa".replace(/a/g, ["a","b"]), "xa,ba,ba,b", "Array as replacement (converted to string)");
    equal("100".replace(/0/, function ($0, pos, str) {return typeof str;}), "1string0", "typeof last argument in replacement function is string");
    equal(new String("100").replace(/0/, function ($0, pos, str) {return typeof str;}), "1string0", "typeof last argument in replacement function is string, when called on String as context");
    equal(String.prototype.replace.call(100, /0/, function ($0, pos, str) {return typeof str;}), "1string0", "typeof last argument in replacement function is string, when called on number as context");
    equal("xaaa".replace(/a/), "xundefinedaa", "Replacement string is 'undefined', when not provided");
    equal("x".replace(/x/, /x/), "/x/", "Regex search with RegExp replacement");
    equal("xaaa".replace(), "xaaa", "Source returned when no replacement provided");
    equal("test".replace(/t|(e)/g, "$1"), "es", "Numbered backreference to nonparticipating group");
 
    var regex = /x/;
    "123x567".replace(regex, "_");
    equal(regex.lastIndex, 0, "Unaltered nonglobal regex lastIndex is 0 after match");
 
    regex.lastIndex = 1;
    "123x567".replace(regex, "_");
    equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after match");
 
    "abc".replace(regex, "_");
    equal(regex.lastIndex, 1, "Nonglobal regex lastIndex is unmodified after failure");
 
    var regexG = /x/g;
    "123x567".replace(regexG, "_");
    equal(regexG.lastIndex, 0, "Unaltered global regex lastIndex is 0 after match");
 
    regexG.lastIndex = 5;
    equal("123x567".replace(regexG, "_"), "123_567", "Global regex ignores lastIndex as start position");
 
    regexG.lastIndex = 5;
    "123x567".replace(regexG, "_");
    equal(regexG.lastIndex, 0, "Global regex lastIndex reset to 0");
 
    var regex2 = /x/g;
    var interimLastIndex = 0;
    "1x2".replace(regex2, function () {
        interimLastIndex = regex2.lastIndex;
    });
    equal(interimLastIndex, 2, "Global regex lastIndex updated during replacement iterations");
 
    XRegExp.uninstall("natives");
});
 
test("String.prototype.split", function () {
    XRegExp.install("natives");
 
    expect(0);
    // TODO: Add tests (basic functionality tests, not the long list from
    // the cross-browser fixes module)
 
    XRegExp.uninstall("natives");
});
 
//-------------------------------------------------------------------
module("Overriden natives extensions");
//-------------------------------------------------------------------
 
test("RegExp.prototype.exec", function () {
    XRegExp.install("natives");
 
    equal(XRegExp("(?<name>a)").exec("a").name, "a", "Match array has named capture properties");
 
    XRegExp.uninstall("natives");
});
 
// RegExp.prototype.test is overridden but not extended by XRegExp
//test("RegExp.prototype.test", function () {});
 
test("String.prototype.match", function () {
    XRegExp.install("natives");
 
    equal("a".match(XRegExp("(?<name>a)")).name, "a", "Match array has named capture properties");
 
    XRegExp.uninstall("natives");
});
 
test("String.prototype.replace", function () {
    XRegExp.install("natives");
 
    equal("xaaa".replace(/aa/, "$0b"), "xaaba", "$0 in replacement string works like $&");
    equal("xaaa".replace(/aa/, "$00b"), "xaaba", "$00 in replacement string works like $&");
    equal("xaaa".replace(/aa/, "$000b"), "xaa0ba", "$000 in replacement string works like $&0");
    raises(function () {"xaaa".replace(/aa/, "$1b");}, SyntaxError, "$1 throws in replacement string for regex with no backreference");
    raises(function () {"xaaa".replace(/aa/, "$01b");}, SyntaxError, "$01 throws in replacement string for regex with no backreference");
    equal("xaaa".replace(/aa/, "$001b"), "xaa1ba", "$001 works like $&1 in replacement string for regex with no backreference");
    raises(function () {"xaaa".replace(/a(a)/, "$2b");}, SyntaxError, "$2 throws in replacement string for regex with less than 2 backreferences");
    raises(function () {"xa(a)a".replace("a(a)", "$1b");}, SyntaxError, "$1 throws in replacement string for string search with parentheses");
    equal("xaaa".replace("aa", "$0b"), "xaaba", "$0 in replacement string for string search works like $&");
    equal("test".replace(/t|(e)/g, "${1}"), "es", "Numbered backreference in curly brackets to nonparticipating group");
    raises(function () {"test".replace(/t/, "${1}");}, SyntaxError, "Numbered backreference to undefined group in replacement string");
    equal("test".replace(XRegExp("(?<test>t)", "g"), ":${test}:"), ":t:es:t:", "Named backreference in replacement string");
    raises(function () {"test".replace(XRegExp("(?<test>t)", "g"), ":${x}:");}, SyntaxError, "Named backreference to undefined group in replacement string");
    equal("test".replace(XRegExp("(?<a>.)(?<a>.)", "g"), "${a}"), "et", "Named backreference uses last of groups with the same name");
 
    function mul(str, num) {
        return Array(num + 1).join(str);
    }
    // IE <= 8 doesn't allow backrefs greater than \99 in regex syntax
    var lottaGroups = new RegExp(
        "^(a)\\1" + mul("()", 8) +
        "(b)\\10" + mul("()", 89) +
        "(c)" + mul("()", 899) +
        "(d)$"
    );
    equal("aabbcd".replace(lottaGroups, "$0 $01 $001 $0001 $1 $10 $100 $1000"), "aabbcd a aabbcd1 aabbcd01 a b b0 b00", "Regex with 1,000 capturing groups, without curly brackets for backreferences");
    equal("aabbcd".replace(lottaGroups, "${0} ${01} ${001} ${0001} ${1} ${10} ${100} ${1000}"), "aabbcd a a a a b c d", "Regex with 1,000 capturing groups, with curly brackets for backreferences");
 
    // TODO: Add tests
 
    XRegExp.uninstall("natives");
});
 
// String.prototype.split is overridden but not extended by XRegExp
//test("String.prototype.split", function () {});
 
//-------------------------------------------------------------------
module("New syntax and flags");
//-------------------------------------------------------------------
 
test("Named capture and backreferences", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Inline comments", function () {
    ok(XRegExp("^a(?#)b$").test("ab"), "Comment is ignored");
    ok(XRegExp("^a(?#)+$").test("aaa"), "Quantifier following comment applies to preceding atom");
    ok(XRegExp("^(a)\\1(?#)2$").test("aa2"), "Comment separates atoms");
 
    // TODO: Add tests
});
 
test("Leading mode modifier", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Enhanced error handling", function () {
    raises(function () {XRegExp("\\1");}, SyntaxError, "Octals throw");
 
    // TODO: Add tests
 
    // Python-style named capture syntax was added to XRegExp to avoid octal-related errors in Opera. Recent Opera supports (?P<name>..) and (?P=name) based on abandoned ES4 proposals
    equal(XRegExp("(?P<name>a)(b)\\2").test("abb"), true, "Numbered backreference to Python-style named capture not treated as octal (test 1)");
    equal(XRegExp("(?P<name>a)(b)\\1").test("aba"), true, "Numbered backreference to Python-style named capture not treated as octal (test 2)");
});
 
test("n flag (explicit capture mode)", function () {
    expect(0);
    // TODO: Add tests
});
 
test("s flag (dotall mode)", function () {
    expect(0);
    // TODO: Add tests
});
 
test("x flag (extended mode)", function () {
    ok(XRegExp("^a b$", "x").test("ab"), "Whitespace is ignored");
    ok(XRegExp("^a#comment\nb$", "x").test("ab"), "Line comment is ignored");
    ok(XRegExp("^a +$", "x").test("aaa"), "Quantifier following whitespace applies to preceding atom");
    ok(XRegExp("^(a)\\1 2$", "x").test("aa2"), "Whitespace separates atoms");
    ok(XRegExp("^ [ #]+ $", "x").test(" #"), "Character classes do not use free-spacing");
 
    // TODO: Add tests
});
 
//-------------------------------------------------------------------
module("Cross-browser fixes");
//-------------------------------------------------------------------
 
test("Nonparticipating capture values", function () {
    expect(0);
    // TODO: Add tests
});
 
test("RegExp.prototype.lastIndex", function () {
    expect(0);
    // TODO: Add tests
});
 
test("String.prototype.split with regex separator", function () {
    XRegExp.install("natives");
 
    // Some of these tests are not known to fail in any browser, but many fail in at least one
    // version of one browser.
 
    deepEqual("".split(), [""]);
    deepEqual("".split(/./), [""]);
    deepEqual("".split(/.?/), []);
    deepEqual("".split(/.??/), []);
    deepEqual("ab".split(/a*/), ["", "b"]);
    deepEqual("ab".split(/a*?/), ["a", "b"]);
    deepEqual("ab".split(/(?:ab)/), ["", ""]);
    deepEqual("ab".split(/(?:ab)*/), ["", ""]);
    deepEqual("ab".split(/(?:ab)*?/), ["a", "b"]);
    deepEqual("test".split(""), ["t", "e", "s", "t"]);
    deepEqual("test".split(), ["test"]);
    deepEqual("111".split(1), ["", "", "", ""]);
    deepEqual("test".split(/(?:)/, 2), ["t", "e"]);
    deepEqual("test".split(/(?:)/, -1), ["t", "e", "s", "t"]);
    deepEqual("test".split(/(?:)/, undefined), ["t", "e", "s", "t"]);
    deepEqual("test".split(/(?:)/, null), []);
    deepEqual("test".split(/(?:)/, NaN), []);
    deepEqual("test".split(/(?:)/, true), ["t"]);
    deepEqual("test".split(/(?:)/, "2"), ["t", "e"]);
    deepEqual("test".split(/(?:)/, "two"), []);
    deepEqual("a".split(/-/), ["a"]);
    deepEqual("a".split(/-?/), ["a"]);
    deepEqual("a".split(/-??/), ["a"]);
    deepEqual("a".split(/a/), ["", ""]);
    deepEqual("a".split(/a?/), ["", ""]);
    deepEqual("a".split(/a??/), ["a"]);
    deepEqual("ab".split(/-/), ["ab"]);
    deepEqual("ab".split(/-?/), ["a", "b"]);
    deepEqual("ab".split(/-??/), ["a", "b"]);
    deepEqual("a-b".split(/-/), ["a", "b"]);
    deepEqual("a-b".split(/-?/), ["a", "b"]);
    deepEqual("a-b".split(/-??/), ["a", "-", "b"]);
    deepEqual("a--b".split(/-/), ["a", "", "b"]);
    deepEqual("a--b".split(/-?/), ["a", "", "b"]);
    deepEqual("a--b".split(/-??/), ["a", "-", "-", "b"]);
    deepEqual("".split(/()()/), []);
    deepEqual(".".split(/()()/), ["."]);
    deepEqual(".".split(/(.?)(.?)/), ["", ".", "", ""]);
    deepEqual(".".split(/(.??)(.??)/), ["."]);
    deepEqual(".".split(/(.)?(.)?/), ["", ".", undefined, ""]);
    deepEqual("A<B>bold</B>and<CODE>coded</CODE>".split(/<(\/)?([^<>]+)>/), ["A", undefined, "B", "bold", "/", "B", "and", undefined, "CODE", "coded", "/", "CODE", ""]);
    deepEqual("test".split(/(.?)/), ["","t","","e","","s","","t",""]);
    deepEqual("tesst".split(/(s)*/), ["t", undefined, "e", "s", "t"]);
    deepEqual("tesst".split(/(s)*?/), ["t", undefined, "e", undefined, "s", undefined, "s", undefined, "t"]);
    deepEqual("tesst".split(/(s*)/), ["t", "", "e", "ss", "t"]);
    deepEqual("tesst".split(/(s*?)/), ["t", "", "e", "", "s", "", "s", "", "t"]);
    deepEqual("tesst".split(/(?:s)*/), ["t", "e", "t"]);
    deepEqual("tesst".split(/(?=s+)/), ["te", "s", "st"]);
    deepEqual("test".split("t"), ["", "es", ""]);
    deepEqual("test".split("es"), ["t", "t"]);
    deepEqual("test".split(/t/), ["", "es", ""]);
    deepEqual("test".split(/es/), ["t", "t"]);
    deepEqual("test".split(/(t)/), ["", "t", "es", "t", ""]);
    deepEqual("test".split(/(es)/), ["t", "es", "t"]);
    deepEqual("test".split(/(t)(e)(s)(t)/), ["", "t", "e", "s", "t", ""]);
    deepEqual(".".split(/(((.((.??)))))/), ["", ".", ".", ".", "", "", ""]);
    deepEqual(".".split(/(((((.??)))))/), ["."]);
    deepEqual("a b c d".split(/ /, -(Math.pow(2, 32) - 1)), ["a"]); // very large negative number test by Brian O
    deepEqual("a b c d".split(/ /, Math.pow(2, 32) + 1), ["a"]);
    deepEqual("a b c d".split(/ /, Infinity), []);
 
    XRegExp.uninstall("natives");
});
 
test("Regular expression syntax", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Replacement text syntax", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Type conversion", function () {
    XRegExp.install("natives");
 
    // these are duplicated from String.prototype.replace tests in the overridden natives module
    equal(new String("100").replace(/0/, function ($0, pos, str) {return typeof str;}), "1string0", "String.prototype.replace: typeof last argument in replacement function is string, when called on String as context");
    equal(String.prototype.replace.call(100, /0/, function ($0, pos, str) {return typeof str;}), "1string0", "String.prototype.replace: typeof last argument in replacement function is string, when called on number as context");
 
    // TODO: Add tests
 
    XRegExp.uninstall("natives");
});
 
//-------------------------------------------------------------------
module("Addons");
//-------------------------------------------------------------------
 
test("Unicode base", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Unicode categories", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Unicode scripts", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Unicode blocks", function () {
    expect(0);
    // TODO: Add tests
});
 
test("Unicode properties", function () {
    expect(0);
    // TODO: Add tests
});
 
test("XRegExp.matchRecursive", function () {
    ok(XRegExp.matchRecursive, "XRegExp.matchRecursive exists");
 
    // TODO: Add tests
});
 
test("XRegExp.build", function () {
    ok(XRegExp.build, "XRegExp.build exists");
 
    var built = XRegExp.build("({{n1}})\\1(?<nX>{{n2}})\\2()\\3\\1\\2\\k<nX>", {
        n1: XRegExp("(?<yo>a)\\1"),
        n2: XRegExp("(?<yo>b)\\1")
    }); // Equivalent to XRegExp("(?<n1>(?<yo>a)\\2)\\1(?<nX>(?<yo>b)\\4)\\3()\\5\\1\\3\\k<nX>")
    var match = XRegExp.exec("aaaabbbbaabbbb", built);
 
    ok(match);
    equal(match.n1, "aa");
    equal(match.n2, undefined);
    equal(match.nX, "bb");
    equal(match.yo, "b");
 
    // IE v7-8 (not v6 or v9) throws an Error rather than SyntaxError
    raises(function () {var r = XRegExp.build('(?x)({{a}})', {a: /#/});}, Error, "Mode modifier in outer pattern applies to full regex with interpolated values (test 1)");
    equal(XRegExp.build("(?x){{a}}", {a: /1 2/}).test("12"), true, "Mode modifier in outer pattern applies to full regex with interpolated values (test 2)");
    equal(XRegExp.build("(?m){{a}}", {a: /a/}).multiline, true, "Mode modifier with native flag in outer pattern is applied to the final result");
 
    equal(XRegExp.build("^[{{a}}]$", {a: "x"}).test("x"), false, "Named subpattern not interpolated within character class (test 1)");
    equal(XRegExp.build("^{{a}}[{{a}}]$", {a: "x"}).test("x{"), true, "Named subpattern not interpolated within character class (test 2)");
 
    // TODO: Add tests
});
 
test("XRegExp.prototype.apply", function () {
    var regex = XRegExp("x");
 
    ok(XRegExp.prototype.apply, "XRegExp.prototype.apply exists");
    deepEqual(regex.apply(null, ["x"]), regex.test("x"), "Apply with match same as test");
    deepEqual(regex.apply(null, ["y"]), regex.test("y"), "Apply without match same as test");
});
 
test("XRegExp.prototype.call", function () {
    var regex = XRegExp("x");
 
    ok(XRegExp.prototype.call, "XRegExp.prototype.call exists");
    deepEqual(regex.call(null, "x"), regex.test("x"), "Call with match same as test");
    deepEqual(regex.call(null, "y"), regex.test("y"), "Call without match same as test");
});
 
test("XRegExp.prototype.forEach", function () {
    ok(XRegExp.prototype.forEach, "XRegExp.prototype.forEach exists");
 
    // TODO: Add tests
});
 
test("XRegExp.prototype.globalize", function () {
    ok(XRegExp.prototype.globalize, "XRegExp.prototype.globalize exists");
 
    // TODO: Add tests
});
 
test("XRegExp.prototype.xexec", function () {
    ok(XRegExp.prototype.xexec, "XRegExp.prototype.xexec exists");
 
    // TODO: Add tests
});
 
test("XRegExp.prototype.xtest", function () {
    ok(XRegExp.prototype.xtest, "XRegExp.prototype.xtest exists");
 
    // TODO: Add tests
});