├── .babelrc.js
├── .gitignore
├── .prettierrc
├── .travis.yml
├── README.md
├── __tests__
└── index.spec.js
├── package.json
└── src
├── index.js
└── query-selector
├── gen-parser.md
├── parser-grammar.kison
├── parser.js
└── util.js
/.babelrc.js:
--------------------------------------------------------------------------------
1 | console.log('Load babel config');
2 |
3 | module.exports = {
4 | presets: [
5 | [
6 | '@babel/preset-env',
7 | {
8 | loose: true,
9 | modules: false,
10 | },
11 | ],
12 | ],
13 | env: {
14 | test: {
15 | presets: ['@babel/preset-env'],
16 | },
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.log
3 | .idea
4 | .ipr
5 | .iws
6 | *~
7 | ~*
8 | *.diff
9 | *.patch
10 | *.bak
11 | .DS_Store
12 | Thumbs.db
13 | .project
14 | .*proj
15 | .svn
16 | *.swp
17 | *.swo
18 | *.pyc
19 | *.pyo
20 | node_modules
21 | .cache
22 | build
23 | coverage
24 | *.map
25 | /pkg/
26 | yarn.lock
27 | /storybook-static/
28 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | notifications:
3 | email:
4 | - yiminghe@gmail.com
5 | node_js:
6 | - 10.15.0
7 | script:
8 | - npm run coverage
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # query-selector
2 | ---
3 |
4 | querySelectorAll in javascript
5 |
6 | [](https://npmjs.org/package/query-selector)
7 | [](https://npmjs.org/package/query-selector)
8 | [](https://travis-ci.org/yiminghe/query-selector)
9 | [](https://coveralls.io/r/yiminghe/query-selector?branch=master)
10 |
11 |
12 | ## usage
13 |
14 | ```
15 | import querySelectorAll from 'query-selector'; // var querySelectorAll = require('query-selector').default;
16 | import {jsdom} from "jsdom";
17 | var doc = jsdom('
1 2
');
18 | var time = Date.now();
19 | console.log(doc.querySelectorAll('#t span', doc).length);
20 | console.log(doc.querySelectorAll('#t span', doc)[0].innerHTML);
21 | console.log(Date.now()-time);
22 | time = Date.now();
23 | console.log(querySelectorAll('#t span', doc).length);
24 | console.log(querySelectorAll('#t span', doc)[0].innerHTML);
25 | console.log(Date.now()-time);
26 | ```
27 |
28 | ## history
29 |
30 | ### 2.0.0 / 2019.08.14
31 |
32 | - use new build tool and test tool
33 | - change export: `var querySelectorAll = require('query-selector').default;`
34 |
--------------------------------------------------------------------------------
/__tests__/index.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * css3 selector tc modified from Sizzle
3 | * @author yiminghe@gmail.com
4 | */
5 |
6 |
7 | import select from '../src/index';
8 | import $ from 'jquery';
9 |
10 | var matches = select.matches;
11 | var ieVersion = select.util.ie;
12 |
13 | document.documentElement.id = 'html';
14 |
15 | function makeArray(arr) {
16 | var ret = [];
17 | for (var i = 0; i < arr.length; i++) {
18 | ret[i] = arr[i];
19 | }
20 | return ret;
21 | }
22 |
23 | function matchesSelector(el, selector) {
24 | return matches(selector, [el]).length === 1;
25 | }
26 |
27 | function ok(a, name) {
28 | it(name, function () {
29 | if (typeof a === 'function') {
30 | a = a();
31 | }
32 | expect(!!a).toEqual(true);
33 | });
34 | }
35 |
36 | function getAttr(el, name) {
37 | var ret = el && el.getAttributeNode(name);
38 | return ret && ret.nodeValue;
39 | }
40 |
41 | /**
42 | * Asserts that a select matches the given IDs
43 | * @param {String} a - Assertion name
44 | * @param {String} b - selector
45 | * @param {String} c - Array of ids to construct what is expected
46 | * @example t("Check for something", "//[a]", ["foo", "baar"]);
47 | * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar'
48 | */
49 | function t(a, b, c, only) {
50 | const f = only ? it.only : it;
51 | f(a, function () {
52 | var f = select(b),
53 | s = [],
54 | i = 0;
55 | for (; i < f.length; i++) {
56 | s.push(getAttr(f[i], 'id'));
57 | }
58 | expect(s).toEqual(c);
59 | });
60 |
61 | }
62 |
63 | /**
64 | * Returns an array of elements with the given IDs
65 | * @example q("main", "foo", "bar")
66 | * @result [,
, ]
67 | */
68 | function q() {
69 | var r = [],
70 | i = 0;
71 |
72 | for (; i < arguments.length; i++) {
73 | r.push(document.getElementById(arguments[i]));
74 | }
75 | return r;
76 | }
77 |
78 | function broken(name, selector) {
79 |
80 | it(name + ": " + selector, function () {
81 | try {
82 | select(selector);
83 | } catch (e) {
84 | expect(e.message.toLowerCase().indexOf("syntax error")).toBeGreaterThan(-1);
85 | }
86 | });
87 | }
88 |
89 | function equal(a, b, name) {
90 | it(name, function () {
91 | if (typeof a === 'function') {
92 | a = a();
93 | }
94 | if (typeof b === 'function') {
95 | b = b();
96 | }
97 | expect(a).toEqual(b);
98 | });
99 | }
100 |
101 | var xml = `
102 |
103 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | 1
115 |
116 |
117 |
118 |
119 | foo
120 |
121 |
122 |
123 |
124 |
125 |
126 | `;
127 |
128 | const fixtureHtml = `
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
374 |
375 |
376 |
377 | `;
378 |
379 | $('body').append(fixtureHtml);
380 |
381 | var createWithFriesXML = function () {
382 | return $.parseXML(xml);
383 | };
384 |
385 | describe("element", function () {
386 |
387 | document.body.id = 'body';
388 |
389 | var form = document.getElementById("form");
390 |
391 | it('fix href normalization', function () {
392 | expect(select('a[href="test.html"]').length).toEqual(1);
393 | });
394 |
395 | it('Select all', function () {
396 |
397 | expect(select("*").length).toBeGreaterThan(30);
398 | });
399 |
400 | it('Select all elements, no comment nodes', function () {
401 | var all = select("*"), good = true;
402 | for (var i = 0; i < all.length; i++) {
403 | if (all[i].nodeType === 8) {
404 | good = false;
405 | }
406 | }
407 | expect(good).toEqual(true);
408 | });
409 |
410 | t("Element Selector html", "html", ["html"]);
411 | t("Element Selector body", "body", ["body"]);
412 | t("Element Selector p", "#qunit-fixture p", ["firstp", "ap", "sndp", "en", "sap", "first"]);
413 |
414 | t("Parent Element", "dl ol", ["empty", "listWithTabIndex"]);
415 | t("Parent Element (non-space descendant combinator)", "dl\tol", ["empty", "listWithTabIndex"]);
416 |
417 | it("Object/param as context", function () {
418 | var obj1 = document.getElementById("object1");
419 | expect(select("param", obj1).length).toEqual(2);
420 | });
421 |
422 | it("Finding selects with a context.", function () {
423 | expect(select("select", form)).toEqual(q("select1", "select2", "select3", "select4", "select5"));
424 | });
425 |
426 | // Check for unique-ness and sort order
427 | it("Check for duplicates: p, div p", function () {
428 | expect(select("p, div p")).toEqual(select('p'));
429 | });
430 |
431 | t("Checking sort order -1", "#fixture h2, #fixture h1", ["qunit-header", "qunit-banner", "qunit-userAgent"]);
432 | t("Checking sort order -2", "#qunit-fixture p, #qunit-fixture p a", ["firstp", "simon1", "ap", "google", "groups", "anchor1", "mark", "sndp", "en", "yahoo", "sap", "anchor2", "simon", "first"]);
433 |
434 | // Test Conflict ID
435 | var lengthtest = document.getElementById("lengthtest");
436 |
437 | it("Finding element with id of ID. -1", function () {
438 | expect(select("#idTest", lengthtest)).toEqual(q('idTest'));
439 | });
440 |
441 | it("Finding element with id of ID. -2", function () {
442 | expect(select("[name='id']", lengthtest)).toEqual(q('idTest'));
443 | });
444 |
445 | it("Finding elements with id of ID.", function () {
446 | expect(select("input[id='idTest']", lengthtest)).toEqual(q('idTest'));
447 | });
448 |
449 | var siblingTest = document.getElementById("siblingTest");
450 | it("Element-rooted QSA select based on document context", function () {
451 | expect(select("div em", siblingTest)).toEqual(q('siblingfirst',
452 | 'siblingnext',
453 | 'siblingthird',
454 | 'siblingchild',
455 | 'siblinggrandchild',
456 | 'siblinggreatgrandchild'));
457 | });
458 |
459 | it("Other document as context", function () {
460 | var iframe = document.getElementById("iframe"),
461 | iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
462 | iframeDoc.open();
463 | iframeDoc.write("bar
");
464 | iframeDoc.close();
465 | expect(select("p#foo", iframeDoc)).toEqual([iframeDoc.getElementById("foo")]);
466 | });
467 |
468 | it("No stack or performance problems with large amounts of descendents", function () {
469 | var html = "";
470 | for (var i = 0; i < 100; i++) {
471 | html = "" + html + "
";
472 | }
473 | html = $(html).appendTo(document.body);
474 |
475 | expect(select("body div div div").length).toBeGreaterThan(0);
476 | expect(select("body>div div div").length).toBeGreaterThan(0);
477 |
478 | html.remove();
479 | });
480 |
481 | it('', function () {
482 | q("qunit-fixture")[0].appendChild(document.createElement("toString")).id = "toString";
483 | });
484 |
485 | t("Element name matches Object.prototype property", "toString#toString", ["toString"]);
486 |
487 | it('', function () {
488 | $('#toString').remove();
489 | });
490 | });
491 |
492 | describe("XML Document Selectors", function () {
493 | var xml = createWithFriesXML();
494 |
495 | it("Element Selector with underscore", function () {
496 | expect(select("foo_bar", xml).length).toEqual(1);
497 | });
498 |
499 | it("Class selector", function () {
500 | expect(select(".component", xml).length).toEqual(1);
501 | });
502 |
503 | it("Attribute selector for class", function () {
504 | expect(select("component[class*=component]", xml).length).toEqual(1);
505 | });
506 |
507 | it("Attribute selector with name", function () {
508 | expect(select("property[name=prop2]", xml).length).toEqual(1);
509 | });
510 |
511 | it("Attribute selector with name -2", function () {
512 | expect(select("[name=prop2]", xml).length).toEqual(1);
513 | });
514 |
515 | it("Attribute selector with ID", function () {
516 | expect(select("#seite1", xml).length).toEqual(1);
517 | });
518 |
519 | it("Attribute selector with ID -2", function () {
520 | expect(select("component#seite1", xml).length).toEqual(1);
521 | });
522 |
523 | it("Attribute selector filter with ID", function () {
524 | expect(matches("#seite1", select("component", xml)).length).toEqual(1);
525 | });
526 |
527 | it("Descendent selector and dir caching", function () {
528 | expect(select("meta property thing", select("component", xml)[0]).length).toEqual(2);
529 | });
530 |
531 | it("Check for namespaced element", function () {
532 | expect(matches("soap\\:Envelope", [xml.lastChild]).length).toEqual(1);
533 | });
534 | });
535 |
536 | describe("broken", function () {
537 |
538 | broken("Broken Selector", "[");
539 | broken("Broken Selector", "(");
540 | broken("Broken Selector", "{");
541 | broken("Broken Selector", "<");
542 | broken("Broken Selector", "()");
543 | broken("Broken Selector", "<>");
544 | broken("Broken Selector", "{}");
545 | broken("Broken Selector", ",");
546 | broken("Broken Selector", ",a");
547 | broken("Broken Selector", "a,");
548 | // Hangs on IE 9 if regular expression is inefficient
549 | broken("Broken Selector", "[id=012345678901234567890123456789");
550 | broken("Doesn't exist", ":visble");
551 | broken("Nth-child", ":nth-child");
552 |
553 | broken("Nth-child", ":nth-child(2n+-0)");
554 | broken("Nth-child", ":nth-child(2+0)");
555 | broken("Nth-child", ":nth-child(- 1n)");
556 | broken("Nth-child", ":nth-child(-1 n)");
557 | broken("First-child", ":first-child(n)");
558 | broken("Last-child", ":last-child(n)");
559 | broken("Only-child", ":only-child(n)");
560 | broken("Nth-last-last-child", ":nth-last-last-child(1)");
561 | broken("First-last-child", ":first-last-child");
562 | broken("Last-last-child", ":last-last-child");
563 | broken("Only-last-child", ":only-last-child");
564 |
565 | // Make sure attribute value quoting works correctly. See: #6093
566 | var a = $(" ").appendTo("#qunit-fixture");
567 |
568 | broken("Attribute not escaped", "input[name=foo.baz]");
569 | broken("Attribute not escaped", "input[name=foo[baz]]");
570 | it('', function () {
571 | a.remove();
572 | });
573 | });
574 |
575 | describe("id", function () {
576 | t("ID Selector", "#body", ["body"]);
577 | t("ID Selector w/ Element", "body#body", ["body"]);
578 | t("ID Selector w/ Element", "ul#first", []);
579 | t("ID selector with existing ID descendant", "#firstp #simon1", ["simon1"]);
580 | t("ID selector with non-existant descendant", "#firstp #foobar", []);
581 | t("ID selector using UTF8", "#台北Táiběi", ["台北Táiběi"]);
582 | t("Multiple ID selectors using UTF8", "#台北Táiběi, #台北", ["台北Táiběi", "台北"]);
583 | t("Descendant ID selector using UTF8", "div #台北", ["台北"]);
584 | t("Child ID selector using UTF8", "form > #台北", ["台北"]);
585 |
586 | t("Escaped ID", "#foo\\:bar", ["foo:bar"]);
587 | t("Escaped ID", "#test\\.foo\\[5\\]bar", ["test.foo[5]bar"]);
588 | t("Descendant escaped ID", "div #foo\\:bar", ["foo:bar"]);
589 | t("Descendant escaped ID", "div #test\\.foo\\[5\\]bar", ["test.foo[5]bar"]);
590 | t("Child escaped ID", "form > #foo\\:bar", ["foo:bar"]);
591 | t("Child escaped ID", "form > #test\\.foo\\[5\\]bar", ["test.foo[5]bar"]);
592 |
593 | it("Escaped ID as context", function () {
594 | var tmp = $("
").appendTo("#qunit-fixture");
595 | expect(select("#fiddle\\\\Foo > span")).toEqual(q(["fiddleSpan"]));
596 | tmp.remove();
597 | });
598 |
599 | t("ID Selector, child ID present", "#form > #radio1", ["radio1"]); // bug #267
600 | t("ID Selector, not an ancestor ID", "#form #first", []);
601 | t("ID Selector, not a child ID", "#form > #option1a", []);
602 |
603 | t("All Children of ID", "#foo > *", ["sndp", "en", "sap"]);
604 | t("All Children of ID with no children", "#firstUL > *", []);
605 |
606 | var tmpNode = $("").appendTo("#qunit-fixture");
611 |
612 | equal(select("#tName1")[0].id, "tName1", "ID selector with same value for a name attribute");
613 | equal(select("#tName2"), [], "ID selector non-existing but name attribute on an A tag");
614 | equal(select("#tName2 code"), [], "Leading ID selector non-existing but name attribute on an A tag");
615 | it('', function () {
616 | tmpNode.remove();
617 | });
618 |
619 | it('', function () {
620 | tmpNode = $(" ").appendTo("#qunit-fixture");
621 | });
622 |
623 | t("ID Selector contains backslash", "#backslash\\\\foo", ["backslash\\foo"]);
624 |
625 | t("ID Selector on Form with an input that has a name of 'id'", "#lengthtest", ["lengthtest"]);
626 |
627 | t("ID selector with non-existant ancestor", "#asdfasdf #foobar", []); // bug #986
628 |
629 | equal(select("div#form", document.body), [], "ID selector within the context of another element");
630 |
631 | t("Underscore ID", "#types_all", ["types_all"]);
632 | t("Dash ID", "#qunit-fixture", ["qunit-fixture"]);
633 |
634 | t("ID with weird characters in it", "#name\\+value", ["name+value"]);
635 | it('', function () {
636 | tmpNode.remove();
637 | });
638 | });
639 |
640 | describe("class", function () {
641 |
642 | t("Class Selector", ".blog", ["mark", "simon"]);
643 | t("Class Selector", ".GROUPS", ["groups"]);
644 | t("Class Selector", ".blog.link", ["simon"]);
645 | t("Class Selector w/ Element", "a.blog", ["mark", "simon"]);
646 | t("Parent Class Selector", "p .blog", ["mark", "simon"]);
647 |
648 | t("Class selector using UTF8", ".台北Táiběi", ["utf8class1"]);
649 | //t( "Class selector using UTF8", ".台北", ["utf8class1","utf8class2"] );
650 | t("Class selector using UTF8", ".台北Táiběi.台北", ["utf8class1"]);
651 | t("Class selector using UTF8", ".台北Táiběi, .台北", ["utf8class1", "utf8class2"]);
652 | t("Descendant class selector using UTF8", "div .台北Táiběi", ["utf8class1"]);
653 | t("Child class selector using UTF8", "form > .台北Táiběi", ["utf8class1"]);
654 |
655 | t("Escaped Class", ".foo\\:bar", ["foo:bar"]);
656 | t("Escaped Class", ".test\\.foo\\[5\\]bar", ["test.foo[5]bar"]);
657 | t("Descendant escaped Class", "div .foo\\:bar", ["foo:bar"]);
658 | t("Descendant escaped Class", "div .test\\.foo\\[5\\]bar", ["test.foo[5]bar"]);
659 | t("Child escaped Class", "form > .foo\\:bar", ["foo:bar"]);
660 | t("Child escaped Class", "form > .test\\.foo\\[5\\]bar", ["test.foo[5]bar"]);
661 |
662 |
663 | it("Finding a second class.", function () {
664 | var div = document.createElement("div");
665 | div.innerHTML = "
";
666 | expect(select(".e", div)).toEqual([div.firstChild]);
667 |
668 | });
669 |
670 | it("Finding a modified class.", function () {
671 | var div = document.createElement("div");
672 | div.innerHTML = "
";
673 | div.lastChild.className = "e";
674 | expect(select(".e", div)).toEqual([div.firstChild, div.lastChild]);
675 | });
676 | it('".null does not match an element with no class"', function () {
677 | var div = document.createElement("div");
678 | div.innerHTML = "
";
679 | expect(matches('.null', [div]).length).toEqual(0);
680 | div.className = "null";
681 | expect(matches('.null', [div]).length).toEqual(1);
682 | });
683 |
684 | it('".null does not match an element with no class"', function () {
685 | var div = document.createElement("div");
686 | div.innerHTML = "
";
687 | expect(matches('.null div', [div.firstChild]).length).toEqual(0);
688 | div.className = "null";
689 | expect(matches('.null div', [div.firstChild]).length).toEqual(1);
690 | });
691 |
692 |
693 | it("Classes match Object.prototype properties", function () {
694 | var div = document.createElement("div");
695 | div.innerHTML = "
";
696 |
697 | div.lastChild.className += " hasOwnProperty toString";
698 | expect(select(".hasOwnProperty.toString", div)).toEqual([div.lastChild]);
699 | });
700 | });
701 |
702 | describe("name", function () {
703 |
704 | t("Name selector", "input[name=action]", ["text1"]);
705 | t("Name selector with single quotes", "input[name='action']", ["text1"]);
706 | t("Name selector with double quotes", "input[name=\"action\"]", ["text1"]);
707 |
708 | t("Name selector non-input", "[name=example]", ["name-is-example"]);
709 | t("Name selector non-input", "[name=div]", ["name-is-div"]);
710 | t("Name selector non-input", "*[name=iframe]", ["iframe"]);
711 |
712 | t("Name selector for grouped input", "input[name='types[]']", ["types_all", "types_anime", "types_movie"]);
713 |
714 |
715 | it("Name selector within the context of another element", function () {
716 | var form = document.getElementById("form");
717 | expect(select("input[name=action]", form)).toEqual(q("text1"));
718 | });
719 |
720 | it("Name selector for grouped form element within the context of another element", function () {
721 | var form = document.getElementById("form");
722 | expect(select("input[name='foo[bar]']", form)).toEqual(q("hidden2"));
723 | });
724 |
725 | it("Make sure that rooted queries on forms (with possible expandos) work.", function () {
726 | var form = $(" ").appendTo("#fixture");
727 | expect(select("input", form[0]).length).toEqual(1);
728 | });
729 |
730 | describe('name nested', function () {
731 | var a;
732 | beforeEach(function () {
733 | a = $("")
734 | .appendTo("#qunit-fixture").children();
735 | });
736 |
737 | afterEach(function () {
738 | a.parent().remove();
739 | });
740 |
741 | equal(function () {
742 | return a.length;
743 | }, 3, "Make sure the right number of elements were inserted.");
744 | equal(function () {
745 | return a[1].id;
746 | }, "tName2ID", "Make sure the right number of elements were inserted.");
747 |
748 | equal(function () {
749 | return select("[name=tName1]")[0];
750 | }, function () {
751 | return a[0];
752 | }, "Find elements that have similar IDs");
753 | equal(function () {
754 | return select("[name=tName2]")[0];
755 | }, function () {
756 | return a[1];
757 | }, "Find elements that have similar IDs");
758 | t("Find elements that have similar IDs", "#tName2ID", ["tName2ID"]);
759 |
760 | });
761 |
762 |
763 | });
764 |
765 | describe("multiple", function () {
766 | t("Comma Support1", "#fixture h2, #qunit-fixture p", ["qunit-banner", "qunit-userAgent", "firstp", "ap", "sndp", "en", "sap", "first"]);
767 | t("Comma Support2", "#fixture h2 , #qunit-fixture p", ["qunit-banner", "qunit-userAgent", "firstp", "ap", "sndp", "en", "sap", "first"]);
768 | t("Comma Support3", "#fixture h2 , #qunit-fixture p", ["qunit-banner", "qunit-userAgent", "firstp", "ap", "sndp", "en", "sap", "first"]);
769 | t("Comma Support4", "#fixture h2,#qunit-fixture p", ["qunit-banner", "qunit-userAgent", "firstp", "ap", "sndp", "en", "sap", "first"]);
770 | t("Comma Support5", "#fixture h2,#qunit-fixture p", ["qunit-banner", "qunit-userAgent", "firstp", "ap", "sndp", "en", "sap", "first"]);
771 | t("Comma Support6", "#fixture h2\t,\r#qunit-fixture p", ["qunit-banner", "qunit-userAgent", "firstp", "ap", "sndp", "en", "sap", "first"]);
772 | });
773 |
774 | describe("child and adjacent", function () {
775 |
776 | t("Child", "p > a", ["simon1", "google", "groups", "mark", "yahoo", "simon"]);
777 | t("Child", "p> a", ["simon1", "google", "groups", "mark", "yahoo", "simon"]);
778 | t("Child", "p >a", ["simon1", "google", "groups", "mark", "yahoo", "simon"]);
779 | t("Child", "p>a", ["simon1", "google", "groups", "mark", "yahoo", "simon"]);
780 | t("Child w/ Class", "p > a.blog", ["mark", "simon"]);
781 | t("All Children", "code > *", ["anchor1", "anchor2"]);
782 | t("All Grandchildren", "p > * > *", ["anchor1", "anchor2"]);
783 | t("Adjacent1", "#qunit-fixture a + a", ["groups"]);
784 | t("Adjacent2", "#qunit-fixture a +a", ["groups"]);
785 | t("Adjacent3", "#qunit-fixture a+ a", ["groups"]);
786 | t("Adjacent4", "#qunit-fixture a+a", ["groups"]);
787 | t("Adjacent5", "p + p", ["ap", "en", "sap"]);
788 | t("Adjacent6", "p#firstp + p", ["ap"]);
789 | t("Adjacent7", "p[lang=en] + p", ["sap"]);
790 | t("Adjacent8", "a.GROUPS + code + a", ["mark"]);
791 | t("Comma, Child, and Adjacent", "#qunit-fixture a + a, code > a", ["groups", "anchor1", "anchor2"]);
792 | t("Element Preceded By1", "#qunit-fixture p ~ div",
793 | ["foo", 'nothiddendiv', "moretests", "tabindex-tests", "liveHandlerOrder", "siblingTest"]);
794 | t("Element Preceded By2", "#first ~ div", ["moretests", "tabindex-tests", "liveHandlerOrder", "siblingTest"]);
795 | t("Element Preceded By3", "#groups ~ a", ["mark"]);
796 | t("Element Preceded By4", "#length ~ input", ["idTest"]);
797 | t("Element Preceded By5", "#siblingfirst ~ em", ["siblingnext", "siblingthird"]);
798 | t("Element Preceded By6 (multiple)", "#siblingTest em ~ em ~ em ~ span", ["siblingspan"]);
799 |
800 |
801 | equal(select("#siblingfirst ~ em"), q("siblingnext", "siblingthird"), "Element Preceded By with a context.");
802 | equal(select("#siblingfirst + em"), q("siblingnext"), "Element Directly Preceded By with a context.");
803 |
804 |
805 | equal(select("#en + p,#en a"), q("yahoo", "sap"),
806 | "Compound selector with context, beginning with sibling test.");
807 | equal(select("#en a, #en + p"), q("yahoo", "sap"),
808 | "Compound selector with context, containing sibling test.");
809 |
810 | t("Multiple combinators selects all levels", "#siblingTest em *", ["siblingchild", "siblinggrandchild", "siblinggreatgrandchild"]);
811 | t("Multiple combinators selects all levels", "#siblingTest > em *", ["siblingchild", "siblinggrandchild", "siblinggreatgrandchild"]);
812 | t("Multiple sibling combinators doesn't miss general siblings", "#siblingTest > em:first-child + em ~ span", ["siblingspan"]);
813 |
814 | equal(select("#listWithTabIndex").length, 1, "Parent div for next test is found via ID ");
815 | equal(select("#__sizzle__").length, 0, "Make sure the temporary id assigned is cleared out");
816 | equal(select("#listWithTabIndex").length, 1, "Parent div for previous test is still found via ID");
817 |
818 | t("Verify deep class selector", "div.blah > p > a", []);
819 |
820 | t("No element deep selector", "div.foo > span > a", []);
821 |
822 | t("Non-existant ancestors", ".fototab > .thumbnails > a", []);
823 | });
824 |
825 | describe("attributes", function () {
826 |
827 | var opt, input, div;
828 |
829 | t("Attribute Exists1", "#qunit-fixture a[title]", ["google"]);
830 | t("Attribute Exists2 (case-insensitive)", "#qunit-fixture a[TITLE]", ["google"]);
831 | t("Attribute Exists3", "#qunit-fixture *[title]", ["google", "text1"]);
832 | t("Attribute Exists4", "#qunit-fixture [title]", ["google", "text1"]);
833 | t("Attribute Exists5", "#qunit-fixture a[ title ]", ["google"]);
834 |
835 | if (!ieVersion || ieVersion > 8) {
836 | // TODO ie67
837 | t("Boolean attribute exists0", "#option2d[selected]", ["option2d"]);
838 | t("Boolean attribute exists1", "#select2 option[selected]", ["option2d"]);
839 | t("Boolean attribute equals2", "#select2 option[selected='selected']", ["option2d"]);
840 | }
841 |
842 | t("Attribute Equals1", "#qunit-fixture a[rel='bookmark']", ["simon1"]);
843 | t("Attribute Equals2", "#qunit-fixture a[rel='bookmark']", ["simon1"]);
844 | t("Attribute Equals3", "#qunit-fixture a[rel=bookmark]", ["simon1"]);
845 | t("Attribute Equals4", "#qunit-fixture a[href='//www.google.com/']", ["google"]);
846 | t("Attribute Equals5", "#qunit-fixture a[ rel = 'bookmark' ]", ["simon1"]);
847 | t("Attribute Equals6 Number", "#qunit-fixture option[value=1]",
848 | ["option1b", "option2b", "option3b", "option4b", "option5c"]);
849 | t("Attribute Equals8 Number", "#foodWithNegativeTabIndex[tabIndex='-1']", ["foodWithNegativeTabIndex"]);
850 | t("Attribute Equals7 Number", "#qunit-fixture li[tabIndex='-1']", ["foodWithNegativeTabIndex"]);
851 |
852 | document.getElementById("anchor2").href = "#2";
853 | t("href Attribute", "p a[href^='#']", ["anchor2"]);
854 | t("href Attribute", "p a[href*='#']", ["simon1", "anchor2"]);
855 |
856 | t("for Attribute", "form label[for]", ["label-for"]);
857 | t("for Attribute in form", "#form [for=text1]", ["label-for"]);
858 |
859 | t("Attribute containing []1", "input[name^='foo[']", ["hidden2"]);
860 | t("Attribute containing []2", "input[name^='foo[bar]']", ["hidden2"]);
861 | t("Attribute containing []3", "input[name*='[bar]']", ["hidden2"]);
862 | t("Attribute containing []4", "input[name$='bar]']", ["hidden2"]);
863 | t("Attribute containing []5", "input[name$='[bar]']", ["hidden2"]);
864 | t("Attribute containing []6", "input[name$='foo[bar]']", ["hidden2"]);
865 | t("Attribute containing []7", "input[name*='foo[bar]']", ["hidden2"]);
866 |
867 | equal(select("input[data-comma='0,1']"), [document.getElementById("el12087")], "Without context, single-quoted attribute containing ','");
868 | equal(select("input[data-comma=\"0,1\"]"), [document.getElementById("el12087")], "Without context, double-quoted attribute containing ','");
869 | equal(select("input[data-comma='0,1']", document.getElementById("t12087")), [document.getElementById("el12087")], "With context, single-quoted attribute containing ','");
870 | equal(select("input[data-comma=\"0,1\"]", document.getElementById("t12087")), [document.getElementById("el12087")], "With context, double-quoted attribute containing ','");
871 |
872 | t("Multiple Attribute Equals", "#form input[type='radio'], #form input[type='hidden']", ["radio1", "radio2", "hidden1"]);
873 | t("Multiple Attribute Equals", "#form input[type='radio'], #form input[type=\"hidden\"]", ["radio1", "radio2", "hidden1"]);
874 | t("Multiple Attribute Equals", "#form input[type='radio'], #form input[type=hidden]", ["radio1", "radio2", "hidden1"]);
875 |
876 | t("Attribute selector using UTF8", "span[lang=中文]", ["台北"]);
877 |
878 | t("Attribute Begins With", "a[href ^= '//www']", ["google", "yahoo"]);
879 | t("Attribute Ends With", "a[href $= 'org/']", ["mark"]);
880 | t("Attribute Contains", "a[href *= 'google']", ["google", "groups"]);
881 |
882 | opt = document.getElementById("option1a");
883 | opt.setAttribute("test", "");
884 |
885 | ok(matchesSelector(opt, "[id*=option1]"), "Attribute With No Quotes Contains Matches");
886 | ok(matchesSelector(opt, "[test]"), "Attribute With No Quotes No Content Matches");
887 | ok(!matchesSelector(opt, "[test^='']"),
888 | "Attribute with empty string value does not match startsWith selector (^=)");
889 | ok(matchesSelector(opt, "[id=option1a]"), "Attribute With No Quotes Equals Matches");
890 | ok(matchesSelector(document.getElementById("simon1"), "a[href*='#']"), "Attribute With No Quotes Href Contains Matches");
891 |
892 | t("Empty values", "#select1 option[value='']", ["option1a"]);
893 |
894 | t("Grouped Form Elements", "input[name='foo[bar]']", ["hidden2"]);
895 |
896 | input = document.getElementById("text1");
897 | input.title = "Don't click me";
898 |
899 | ok(matchesSelector(input, "input[title=\"Don't click me\"]"), "Quote within attribute value does not mess up tokenizer");
900 |
901 | // Make sure attribute value quoting works correctly. See $ #6093; #6428
902 | var tmp;
903 |
904 | it('', function () {
905 | tmp = $(
906 | " " +
907 | " " +
908 | " " +
909 | " " +
910 | " " +
911 | " " +
912 | " " +
913 | " "
914 | ).appendTo("#qunit-fixture");
915 | });
916 |
917 | t("Underscores don't need escaping", "input[id=types_all]", ["types_all"]);
918 |
919 | t("Escaped dot", "input[name=foo\\.baz]", ["attrbad_dot"]);
920 | t("Escaped brackets", "input[name=foo\\[baz\\]]", ["attrbad_brackets"]);
921 | t("Escaped quote + right bracket", "input[data-attr='foo_baz\\']']", ["attrbad_injection"]);
922 |
923 | t("Quoted quote", "input[data-attr='\\'']", ["attrbad_quote"]);
924 |
925 | t("Quoted backslash", "input[data-attr='\\\\']", ["attrbad_backslash"]);
926 | t("Quoted backslash quote", "input[data-attr='\\\\\\'']", ["attrbad_backslash_quote"]);
927 | t("Quoted backslash backslash", "input[data-attr='\\\\\\\\']", ["attrbad_backslash_backslash"]);
928 |
929 | t("Quoted backslash backslash (numeric escape)", "input[data-attr='\\5C\\\\']", ["attrbad_backslash_backslash"]);
930 | t("Quoted backslash backslash (numeric escape with trailing space)", "input[data-attr='\\5C \\\\']", ["attrbad_backslash_backslash"]);
931 | t("Quoted backslash backslash (numeric escape with trailing tab)", "input[data-attr='\\5C\t\\\\']", ["attrbad_backslash_backslash"]);
932 | t("Long numeric escape (BMP)", "input[data-attr='\\04e00']", ["attrbad_unicode"]);
933 |
934 | it('', function () {
935 | document.getElementById("attrbad_unicode").setAttribute("data-attr", "\uD834\uDF06A");
936 | });
937 |
938 |
939 | // It was too much code to fix Safari 5.x Supplemental Plane crashes (see ba5f09fa404379a87370ec905ffa47f8ac40aaa3)
940 | // t( "Long numeric escape (non-BMP)", "input[data-attr='\\01D306A']", ["attrbad_unicode"] );
941 |
942 | if (!ieVersion || ieVersion > 8) {
943 | if (!ieVersion || ieVersion > 9) {
944 | t("input[type=search]0", "#search[type=search]", ["search"]);
945 | t("input[type=text]", "#form input[type=text]", ["text1", "text2", "hidden2", "name"]);
946 | t("input[type=search]", "#form input[type=search]", ["search"]);
947 | } else {
948 | t("input[type=text]", "#form input[type=text]", ["text1", "text2", "hidden2", "name", "search"]);
949 | }
950 | }
951 |
952 | // #3279
953 | div = document.createElement("div");
954 | div.innerHTML = "
";
955 |
956 | equal(select("[xml\\:test]", div), [div.firstChild], "Finding by attribute with escaped characters.");
957 |
958 | it('', function () {
959 | tmp.remove();
960 | });
961 |
962 | });
963 |
964 | describe("pseudo - (parent|empty)", function () {
965 | t("Empty", "#fixture ul:empty", ["firstUL"]);
966 | t("Empty with comment node", "ol:empty", ["empty"]);
967 | t("Not Empty with comment node and extra node", "b:empty", []);
968 | });
969 |
970 | describe("pseudo - (first|last|only)-(child|of-type)", function () {
971 |
972 | t("First Child", "p:first-child", ["firstp", "sndp"]);
973 | t("First Child (leading id)", "#qunit-fixture p:first-child", ["firstp", "sndp"]);
974 | t("First Child (leading class)", ".nothiddendiv div:first-child", ["nothiddendivchild"]);
975 | t("First Child (case-insensitive)", "#qunit-fixture p:FIRST-CHILD", ["firstp", "sndp"]);
976 |
977 | t("Last Child", "p:last-child", ["sap"]);
978 | t("Last Child (leading id)", "#qunit-fixture a:last-child",
979 | ["simon1", "anchor1", "mark", "yahoo", "anchor2", "simon", "liveLink1", "liveLink2"]);
980 |
981 | t("Only Child", "#qunit-fixture a:only-child", ["simon1", "anchor1", "yahoo", "anchor2", "liveLink1", "liveLink2"]);
982 |
983 | t("First-of-type", "#qunit-fixture > p:first-of-type", ["firstp"]);
984 | t("Last-of-type", "#qunit-fixture > p:last-of-type", ["first"]);
985 | t("Only-of-type", "#qunit-fixture > :only-of-type", ["name+value", "firstUL", "empty", "floatTest", "iframe", "table"]);
986 |
987 | it("No longer second child", function () {
988 | // Verify that the child position isn't being cached improperly
989 | var secondChildren = $("p:nth-child(2)").before("
");
990 | expect(select("p:nth-child(2)")).toEqual([]);
991 | secondChildren.prev().remove();
992 | });
993 |
994 | t("Restored second child", "p:nth-child(2)", ["ap", "en"]);
995 | });
996 |
997 | describe("pseudo - nth-child", function () {
998 |
999 | t("Nth-child", "p:nth-child(1)", ["firstp", "sndp"]);
1000 | t("Nth-child (with whitespace)", "p:nth-child( 1 )", ["firstp", "sndp"]);
1001 | t("Not nth-child", "#qunit-fixture p:not(:nth-child(1))", ["ap", "en", "sap", "first"]);
1002 |
1003 | t("Nth-child(2)", "#qunit-fixture form#form > *:nth-child(2)", ["text1"]);
1004 | t("Nth-child(2)", "#qunit-fixture form#form > :nth-child(2)", ["text1"]);
1005 |
1006 | equal(select(":nth-child(n)", null, [document.createElement("a")].concat(q("ap"))), q("ap"), "Seeded nth-child");
1007 | });
1008 |
1009 | describe("pseudo - nth-last-child", function () {
1010 |
1011 | t("Nth-last-child", "form:nth-last-child(5)", ["testForm"]);
1012 | t("Nth-last-child (with whitespace)", "form:nth-last-child( 5 )", ["testForm"]);
1013 | t("Not nth-last-child", "#qunit-fixture p:not(:nth-last-child(1))", ["firstp", "ap", "sndp", "en", "first"]);
1014 |
1015 | equal(select(":nth-last-child(n)", null, [document.createElement("a")].concat(q("ap"))), q("ap"),
1016 | "Seeded nth-last-child");
1017 | });
1018 |
1019 | describe("pseudo - nth-of-type", function () {
1020 | t("Nth-of-type(-1)", ":nth-of-type(-1)", []);
1021 | t("Nth-of-type(3)", "#ap :nth-of-type(3)", ["mark"]);
1022 | t("Nth-of-type(n)", "#ap :nth-of-type(n)", ["google", "groups", "code1", "anchor1", "mark"]);
1023 | t("Nth-of-type(0n+3)", "#ap :nth-of-type(0n+3)", ["mark"]);
1024 | t("Nth-of-type(2n)", "#ap :nth-of-type(2n)", ["groups"]);
1025 | t("Nth-of-type(even)", "#ap :nth-of-type(even)", ["groups"]);
1026 | t("Nth-of-type(2n+1)", "#ap :nth-of-type(2n+1)", ["google", "code1", "anchor1", "mark"]);
1027 | t("Nth-of-type(odd)", "#ap :nth-of-type(odd)", ["google", "code1", "anchor1", "mark"]);
1028 |
1029 |
1030 | it("Nth-of-type(-n+2)", function () {
1031 | expect(select("#qunit-fixture > :nth-of-type(-n+2)", null, select('#qunit-fixture > *')))
1032 | .toEqual(makeArray($("#qunit-fixture > :nth-of-type(-n+2)")));
1033 | });
1034 |
1035 | });
1036 |
1037 | describe("pseudo - nth-last-of-type", function () {
1038 | t("Nth-last-of-type(-1)", ":nth-last-of-type(-1)", []);
1039 | t("Nth-last-of-type(3)", "#ap :nth-last-of-type(3)", ["google"]);
1040 | t("Nth-last-of-type(n)", "#ap :nth-last-of-type(n)", ["google", "groups", "code1", "anchor1", "mark"]);
1041 | t("Nth-last-of-type(0n+3)", "#ap :nth-last-of-type(0n+3)", ["google"]);
1042 | t("Nth-last-of-type(2n)", "#ap :nth-last-of-type(2n)", ["groups"]);
1043 | t("Nth-last-of-type(even)", "#ap :nth-last-of-type(even)", ["groups"]);
1044 | t("Nth-last-of-type(2n+1)", "#ap :nth-last-of-type(2n+1)", ["google", "code1", "anchor1", "mark"]);
1045 | t("Nth-last-of-type(odd)", "#ap :nth-last-of-type(odd)", ["google", "code1", "anchor1", "mark"]);
1046 |
1047 | t("Nth-last-of-type(-n+2)",
1048 | "#qunit-fixture > :nth-last-of-type(-n+2)",
1049 | ["ap", "name+value", "first", "firstUL", "empty", "floatTest", "iframe", "table", "name-tests", "testForm", "liveHandlerOrder", "siblingTest"]);
1050 | });
1051 |
1052 | describe("pseudo - misc", function () {
1053 |
1054 | // Recreate tmp
1055 |
1056 | it('div focus', function () {
1057 | var tmp = document.createElement("div");
1058 | tmp.id = "tmp_input";
1059 | tmp.innerHTML = "Hello I am focusable. ";
1060 | // Setting tabIndex should make the element focusable
1061 | // http://dev.w3.org/html5/spec/single-page.html#focus-management
1062 | document.body.appendChild(tmp);
1063 | tmp.tabIndex = 0;
1064 | tmp.focus();
1065 | if (document.activeElement !== tmp || (document.hasFocus && !document.hasFocus()) ||
1066 | (document.querySelectorAll && !document.querySelectorAll("div:focus").length)) {
1067 | } else {
1068 | expect(select(":focus")).toEqual([tmp]);
1069 | expect(matchesSelector(tmp, ":focus")).toEqual(true);
1070 | }
1071 |
1072 | // Blur tmp
1073 | tmp.blur();
1074 | document.body.focus();
1075 | expect(matchesSelector(tmp, ":focus")).toEqual(false);
1076 | document.body.removeChild(tmp);
1077 | });
1078 |
1079 |
1080 | it('input focus', function () {
1081 | var tmp = document.createElement("input");
1082 | tmp.type = "text";
1083 | tmp.id = "tmp_input";
1084 |
1085 | document.body.appendChild(tmp);
1086 | tmp.focus();
1087 |
1088 | if (document.activeElement !== tmp || (document.hasFocus && !document.hasFocus()) ||
1089 | (document.querySelectorAll && !document.querySelectorAll("div:focus").length)) {
1090 | } else {
1091 | expect(select(":focus")).toEqual(q("tmp_input"));
1092 | expect(matchesSelector(tmp, ":focus")).toEqual(true);
1093 | }
1094 |
1095 | // Blur tmp
1096 | tmp.blur();
1097 | document.body.focus();
1098 | expect(matchesSelector(tmp, ":focus")).toEqual(false);
1099 | document.body.removeChild(tmp);
1100 | });
1101 |
1102 |
1103 | equal(
1104 | select("[id='select1'] *:not(:last-child), [id='select2'] *:not(:last-child)", q("qunit-fixture")[0]),
1105 | q("option1a", "option1b", "option1c", "option2a", "option2b", "option2c"),
1106 | "caching system tolerates recursive selection"
1107 | );
1108 |
1109 | });
1110 |
1111 |
1112 | describe("pseudo - :not", function () {
1113 |
1114 | t("Not", "a.blog:not(.link)", ["mark"]);
1115 |
1116 | t(":not() failing interior", "#qunit-fixture p:not(.foo)", ["firstp", "ap", "sndp", "en", "sap", "first"]);
1117 | t(":not() failing interior", "#qunit-fixture p:not(#blargh)", ["firstp", "ap", "sndp", "en", "sap", "first"]);
1118 | t(":not() failing interior", "#qunit-fixture p:not(#blargh)", ["firstp", "ap", "sndp", "en", "sap", "first"]);
1119 | t(":not() failing interior", "#qunit-fixture p:not(#blargh)", ["firstp", "ap", "sndp", "en", "sap", "first"]);
1120 |
1121 | t(":not Multiple", "#qunit-fixture p:not(a)", ["firstp", "ap", "sndp", "en", "sap", "first"]);
1122 | t(":not Multiple", "#qunit-fixture p:not( a )", ["firstp", "ap", "sndp", "en", "sap", "first"]);
1123 | t(":not Multiple", "#qunit-fixture p:not( p )", []);
1124 | t(":not Multiple", "p:not(p)", []);
1125 | t(":not Multiple", "p:not(p)", []);
1126 | t(":not Multiple", "p:not(p)", []);
1127 | t(":not Multiple", "p:not(p)", []);
1128 |
1129 | t("No element not selector", ".container div:not(.excluded) div", []);
1130 |
1131 | t(":not() Existing attribute", "#form select:not([multiple])", ["select1", "select2", "select5"]);
1132 | t(":not() Equals attribute", "#form select:not([name=select1])", ["select2", "select3", "select4", "select5"]);
1133 | t(":not() Equals quoted attribute", "#form select:not([name='select1'])", ["select2", "select3", "select4", "select5"]);
1134 |
1135 | t(":not() Multiple Class", "#foo a:not(.blog)", ["yahoo", "anchor2"]);
1136 | t(":not() Multiple Class", "#foo a:not(.link)", ["yahoo", "anchor2"]);
1137 | });
1138 |
1139 | describe("pseudo - form", function () {
1140 |
1141 | var extraTexts = $(" ").appendTo("#form");
1142 |
1143 |
1144 | t("Selected Option Element are also :checked", "#form option:checked", ["option1a", "option2d", "option3b", "option3c", "option4b", "option4c", "option4d", "option5a"]);
1145 | t("Hidden inputs should be treated as enabled. See QSA test.", "#hidden1:enabled", ["hidden1"]);
1146 |
1147 | it('xx', function () {
1148 | extraTexts.remove();
1149 | });
1150 |
1151 | });
1152 |
1153 | describe("pseudo - :target and :root", function () {
1154 |
1155 | it(':target', function () {
1156 | // Target
1157 | var $link = $(" ").attr({
1158 | href: "#",
1159 | id: "new-link"
1160 | }).appendTo("#qunit-fixture");
1161 |
1162 | var oldHash = window.location.hash;
1163 | window.location.hash = "new-link";
1164 |
1165 | expect(select(":target")).toEqual([$link[0]]);
1166 |
1167 | $link.remove();
1168 | window.location.hash = oldHash;
1169 | });
1170 |
1171 | // Root
1172 | equal(select(":root")[0], document.documentElement, ":root selector");
1173 | });
1174 |
1175 | describe("pseudo - :lang", function () {
1176 |
1177 | document.documentElement.lang = 'en-us';
1178 |
1179 | var docElem = document.documentElement,
1180 | docXmlLang = docElem.getAttribute("xml:lang"),
1181 | docLang = docElem.lang,
1182 | foo = document.getElementById("foo"),
1183 | anchor = document.getElementById("anchor2"),
1184 | xml = createWithFriesXML(),
1185 | testLang = function (text, elem, container, lang, extra) {
1186 |
1187 | it(lang + "-" + extra, function () {
1188 | if (typeof elem === 'function') {
1189 | elem = elem();
1190 | }
1191 | if (typeof container === 'function') {
1192 | container = container();
1193 | }
1194 | var message,
1195 | full = lang + "-" + extra;
1196 |
1197 | var isXML = container.ownerDocument.documentElement.nodeName.toUpperCase() !== "HTML";
1198 |
1199 | // while (cur && cur != container.ownerDocument.documentElement) {
1200 | // cur.lang = '';
1201 | // cur = cur.parentNode;
1202 | // }
1203 |
1204 | message = "lang=" + lang + " " + text;
1205 | container.setAttribute(
1206 | !isXML ?
1207 | "lang" : "xml:lang", lang);
1208 |
1209 | assertMatch(message, elem, ":lang(" + lang + ")");
1210 | assertMatch(message, elem, ":lang(" + mixCase(lang) + ")");
1211 | assertNoMatch(message, elem, ":lang(" + full + ")");
1212 | assertNoMatch(message, elem, ":lang(" + mixCase(full) + ")");
1213 | assertNoMatch(message, elem, ":lang(" + lang + "-)");
1214 | assertNoMatch(message, elem, ":lang(" + full + "-)");
1215 | assertNoMatch(message, elem, ":lang(" + lang + "glish)");
1216 | assertNoMatch(message, elem, ":lang(" + full + "glish)");
1217 |
1218 | message = "lang=" + full + " " + text;
1219 | container.setAttribute(
1220 | !isXML ?
1221 | "lang" : "xml:lang", full);
1222 |
1223 | assertMatch(message, elem, ":lang(" + lang + ")");
1224 | assertMatch(message, elem, ":lang(" + mixCase(lang) + ")");
1225 | assertMatch(message, elem, ":lang(" + full + ")");
1226 | assertMatch(message, elem, ":lang(" + mixCase(full) + ")");
1227 | assertNoMatch(message, elem, ":lang(" + lang + "-)");
1228 | assertNoMatch(message, elem, ":lang(" + full + "-)");
1229 | assertNoMatch(message, elem, ":lang(" + lang + "glish)");
1230 | assertNoMatch(message, elem, ":lang(" + full + "glish)");
1231 | });
1232 |
1233 | },
1234 | mixCase = function (str) {
1235 | var ret = str.split(""),
1236 | i = ret.length;
1237 | while (i--) {
1238 | if (i & 1) {
1239 | ret[i] = ret[i].toUpperCase();
1240 | }
1241 | }
1242 | return ret.join("");
1243 | },
1244 | assertMatch = function (text, elem, selector) {
1245 |
1246 | var r = matchesSelector(elem, selector);
1247 | if (!r) {
1248 | matchesSelector(elem, selector);
1249 | }
1250 | expect(r).toEqual(true);
1251 | if (!r) {
1252 | console.error('error:' + text + " should match " + selector);
1253 | }
1254 |
1255 | },
1256 | assertNoMatch = function (text, elem, selector) {
1257 | var r = matchesSelector(elem, selector);
1258 | expect(r).toEqual(false);
1259 | if (r) {
1260 | console.error('error:' + text + " should fail " + selector);
1261 | }
1262 | };
1263 |
1264 | // Prefixing and inheritance
1265 | ok(function () {
1266 | return matchesSelector(docElem, ":lang(" + docElem.lang + ")");
1267 | }, "starting :lang");
1268 |
1269 | testLang("document", function () {
1270 | return anchor;
1271 | }, docElem, "en", "us");
1272 |
1273 | testLang("grandparent", function () {
1274 | return anchor;
1275 | },
1276 | function () {
1277 | return anchor.parentNode.parentNode;
1278 | }, "yue", "hk");
1279 |
1280 | ok(function () {
1281 | return !matchesSelector(anchor, ":lang(en), :lang(en-us)");
1282 | },
1283 | ":lang does not look above an ancestor with specified lang");
1284 |
1285 | testLang("self", function () {
1286 | return anchor;
1287 | }, function () {
1288 | return anchor;
1289 | }, "es", "419");
1290 |
1291 | ok(function () {
1292 | return !matchesSelector(anchor, ":lang(en), :lang(en-us), :lang(yue), :lang(yue-hk)");
1293 | },
1294 | ":lang does not look above self with specified lang");
1295 |
1296 | // Searching by language tag
1297 | it('', function () {
1298 | anchor.parentNode.parentNode.lang = "arab";
1299 | anchor.parentNode.lang = anchor.parentNode.id = "ara-sa";
1300 | anchor.lang = "ara";
1301 | });
1302 |
1303 | equal(function () {
1304 | return select(":lang(ara)", foo);
1305 | }, [anchor.parentNode, anchor], "Find by :lang");
1306 |
1307 | it('', function () {
1308 | // Selector validity
1309 | anchor.parentNode.lang = "ara";
1310 | anchor.lang = "ara\\b";
1311 | });
1312 |
1313 | equal(function () {
1314 | return select(":lang(ara\\b)", foo);
1315 | }, [], ":lang respects backslashes");
1316 |
1317 | equal(function () {
1318 | return select(":lang(ara\\\\b)", foo);
1319 | }, [anchor],
1320 | ":lang respects escaped backslashes");
1321 |
1322 | it(":lang value must be a valid identifier", function () {
1323 | try {
1324 | select("dl:lang(c++)");
1325 | } catch (e) {
1326 | expect(e.message.indexOf("Syntax error")).toBeGreaterThan(-1);
1327 | }
1328 | });
1329 |
1330 | it('', function () {
1331 | // XML
1332 | foo = $("response", xml)[0];
1333 | anchor = $("#seite1", xml)[0];
1334 |
1335 | });
1336 |
1337 | testLang("XML document", function () {
1338 | return anchor;
1339 | }, xml.documentElement, "en", "us");
1340 |
1341 | testLang("XML grandparent", function () {
1342 | return anchor;
1343 | }, function () {
1344 | return foo;
1345 | }, "yue", "hk");
1346 |
1347 | ok(function () {
1348 | return !matchesSelector(anchor, ":lang(en), :lang(en-us)");
1349 | },
1350 | "XML :lang does not look above an ancestor with specified lang");
1351 |
1352 | testLang("XML self", function () {
1353 | return anchor;
1354 | }, function () {
1355 | return anchor;
1356 | }, "es", "419");
1357 |
1358 | ok(function () {
1359 | return !matchesSelector(anchor, ":lang(en), :lang(en-us), :lang(yue), :lang(yue-hk)");
1360 | },
1361 | "XML :lang does not look above self with specified lang");
1362 |
1363 | it('', function () {
1364 | // Cleanup
1365 | if (docXmlLang === null) {
1366 | docElem.removeAttribute("xml:lang");
1367 | } else {
1368 | docElem.setAttribute("xml:lang", docXmlLang);
1369 | }
1370 | docElem.lang = docLang;
1371 | });
1372 | });
1373 |
1374 | describe("caching", function () {
1375 | select(":not(code)", document.getElementById("ap"));
1376 | equal(select(":not(code)", document.getElementById("foo")), q("sndp", "en", "yahoo", "sap", "anchor2", "simon"), "Reusing selector with new context");
1377 | });
1378 |
1379 | describe('clean', function () {
1380 | it('remove all', function () {
1381 | $('#fixture').remove();
1382 | });
1383 | });
1384 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "query-selector",
3 | "version": "2.0.0",
4 | "description": "javascript implementation of querySelectorAll",
5 | "author": "yiminghe ",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "http://github.com/yiminghe/query-selector.git"
10 | },
11 | "@pika/pack": {
12 | "pipeline": [
13 | [
14 | "@pika/plugin-standard-pkg",
15 | {
16 | "exclude": [
17 | "__tests__/**/*"
18 | ]
19 | }
20 | ],
21 | [
22 | "pika-plugin-build-web-babel"
23 | ],
24 | [
25 | "@pika/plugin-build-node"
26 | ]
27 | ]
28 | },
29 | "scripts": {
30 | "test": "jest",
31 | "coverage": "npm test -- --coverage && cat ./coverage/lcov.info | coveralls",
32 | "prettier": "prettier --write \"{src,stories}/**/*.{js,tsx}\"",
33 | "start": "start-storybook -p 6006",
34 | "pub": "npm run build && npm publish pkg && git push",
35 | "build": "pack build",
36 | "lint-staged": "lint-staged"
37 | },
38 | "devDependencies": {
39 | "@pika/pack": "^0.5.0",
40 | "@pika/plugin-build-node": "0.6.x",
41 | "@pika/plugin-standard-pkg": "0.6.x",
42 | "@pika/types": "0.6.x",
43 | "coveralls": "^3.0.6",
44 | "jest": "^24.8.0",
45 | "jquery": "^3.4.1",
46 | "lint-staged": "^9.2.1",
47 | "pika-plugin-build-web-babel": "^0.6.0",
48 | "pre-commit": "1.x",
49 | "prettier": "^1.18.2"
50 | },
51 | "lint-staged": {
52 | "*.{tsx,js,jsx,ts}": [
53 | "prettier --write",
54 | "git add"
55 | ]
56 | },
57 | "pre-commit": [
58 | "lint-staged"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @ignore
3 | * css3 selector engine for ie6-8
4 | * @author yiminghe@gmail.com
5 | */
6 |
7 | import util from './query-selector/util';
8 | import parser from './query-selector/parser';
9 |
10 | var EXPANDO_SELECTOR_KEY = '_ks_data_selector_id_',
11 | caches = {},
12 | isContextXML,
13 | uuid = 0,
14 | subMatchesCache = {},
15 | getAttr = function (el, name) {
16 | if (isContextXML) {
17 | return util.getSimpleAttr(el, name);
18 | } else {
19 | return util.attr(el, name);
20 | }
21 | },
22 | hasSingleClass = util.hasSingleClass,
23 | isTag = util.isTag,
24 | aNPlusB = /^(([+-]?(?:\d+)?)?n)?([+-]?\d+)?$/;
25 |
26 | // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
27 | var unescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
28 | unescapeFn = function (_, escaped) {
29 | var high = '0x' + escaped - 0x10000;
30 | // NaN means non-codepoint
31 | return isNaN(high) ?
32 | escaped :
33 | // BMP codepoint
34 | high < 0 ?
35 | String.fromCharCode(high + 0x10000) :
36 | // Supplemental Plane codepoint (surrogate pair)
37 | String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00);
38 | };
39 |
40 | var matchExpr;
41 |
42 | var pseudoFnExpr = {
43 | 'nth-child'(el, param) {
44 | var ab = getAb(param),
45 | a = ab.a,
46 | b = ab.b;
47 | if (a === 0 && b === 0) {
48 | return 0;
49 | }
50 | var index = 0,
51 | parent = el.parentNode;
52 | if (parent) {
53 | var childNodes = parent.childNodes,
54 | count = 0,
55 | child,
56 | ret,
57 | len = childNodes.length;
58 | for (; count < len; count++) {
59 | child = childNodes[count];
60 | if (child.nodeType === 1) {
61 | index++;
62 | ret = matchIndexByAb(index, a, b, child === el);
63 | if (ret !== undefined) {
64 | return ret;
65 | }
66 | }
67 | }
68 | }
69 | return 0;
70 | },
71 | 'nth-last-child'(el, param) {
72 | var ab = getAb(param),
73 | a = ab.a,
74 | b = ab.b;
75 | if (a === 0 && b === 0) {
76 | return 0;
77 | }
78 | var index = 0,
79 | parent = el.parentNode;
80 | if (parent) {
81 | var childNodes = parent.childNodes,
82 | len = childNodes.length,
83 | count = len - 1,
84 | child,
85 | ret;
86 | for (; count >= 0; count--) {
87 | child = childNodes[count];
88 | if (child.nodeType === 1) {
89 | index++;
90 | ret = matchIndexByAb(index, a, b, child === el);
91 | if (ret !== undefined) {
92 | return ret;
93 | }
94 | }
95 | }
96 | }
97 | return 0;
98 | },
99 | 'nth-of-type'(el, param) {
100 | var ab = getAb(param),
101 | a = ab.a,
102 | b = ab.b;
103 | if (a === 0 && b === 0) {
104 | return 0;
105 | }
106 | var index = 0,
107 | parent = el.parentNode;
108 | if (parent) {
109 | var childNodes = parent.childNodes,
110 | elType = el.tagName,
111 | count = 0,
112 | child,
113 | ret,
114 | len = childNodes.length;
115 | for (; count < len; count++) {
116 | child = childNodes[count];
117 | if (child.tagName === elType) {
118 | index++;
119 | ret = matchIndexByAb(index, a, b, child === el);
120 | if (ret !== undefined) {
121 | return ret;
122 | }
123 | }
124 | }
125 | }
126 | return 0;
127 | },
128 | 'nth-last-of-type'(el, param) {
129 | var ab = getAb(param),
130 | a = ab.a,
131 | b = ab.b;
132 | if (a === 0 && b === 0) {
133 | return 0;
134 | }
135 | var index = 0,
136 | parent = el.parentNode;
137 | if (parent) {
138 | var childNodes = parent.childNodes,
139 | len = childNodes.length,
140 | elType = el.tagName,
141 | count = len - 1,
142 | child,
143 | ret;
144 | for (; count >= 0; count--) {
145 | child = childNodes[count];
146 | if (child.tagName === elType) {
147 | index++;
148 | ret = matchIndexByAb(index, a, b, child === el);
149 | if (ret !== undefined) {
150 | return ret;
151 | }
152 | }
153 | }
154 | }
155 | return 0;
156 | },
157 | lang(el, lang) {
158 | var elLang;
159 | lang = unEscape(lang.toLowerCase());
160 | do {
161 | if ((elLang = (isContextXML ?
162 | el.getAttribute('xml:lang') || el.getAttribute('lang') :
163 | el.lang))) {
164 | elLang = elLang.toLowerCase();
165 | return elLang === lang || elLang.indexOf(lang + '-') === 0;
166 | }
167 | } while ((el = el.parentNode) && el.nodeType === 1);
168 | return false;
169 | },
170 | not(el, negationArg) {
171 | return !matchExpr[negationArg.t](el, negationArg.value);
172 | }
173 | };
174 |
175 | var pseudoIdentExpr = {
176 | empty(el) {
177 | var childNodes = el.childNodes,
178 | index = 0,
179 | len = childNodes.length,
180 | child,
181 | nodeType;
182 | for (; index < len; index++) {
183 | child = childNodes[index];
184 | nodeType = child.nodeType;
185 | // only element nodes and content nodes
186 | // (such as Dom [Dom-LEVEL-3-CORE] text nodes,
187 | // CDATA nodes, and entity references
188 | if (nodeType === 1 || nodeType === 3 || nodeType === 4 || nodeType === 5) {
189 | return 0;
190 | }
191 | }
192 | return 1;
193 | },
194 | root(el) {
195 | if (el.nodeType === 9) {
196 | return true;
197 | }
198 | return el.ownerDocument &&
199 | el === el.ownerDocument.documentElement;
200 | },
201 | 'first-child'(el) {
202 | return pseudoFnExpr['nth-child'](el, 1);
203 | },
204 | 'last-child'(el) {
205 | return pseudoFnExpr['nth-last-child'](el, 1);
206 | },
207 | 'first-of-type'(el) {
208 | return pseudoFnExpr['nth-of-type'](el, 1);
209 | },
210 | 'last-of-type'(el) {
211 | return pseudoFnExpr['nth-last-of-type'](el, 1);
212 | },
213 | 'only-child'(el) {
214 | return pseudoIdentExpr['first-child'](el) &&
215 | pseudoIdentExpr['last-child'](el);
216 | },
217 | 'only-of-type'(el) {
218 | return pseudoIdentExpr['first-of-type'](el) &&
219 | pseudoIdentExpr['last-of-type'](el);
220 | },
221 | focus(el) {
222 | var doc = el.ownerDocument;
223 | return doc && el === doc.activeElement &&
224 | (!doc.hasFocus || doc.hasFocus()) && !!(el.type || el.href || el.tabIndex >= 0);
225 | },
226 | target(el) {
227 | var hash = location.hash;
228 | return hash && hash.slice(1) === getAttr(el, 'id');
229 | },
230 | enabled(el) {
231 | return !el.disabled;
232 | },
233 | disabled(el) {
234 | return el.disabled;
235 | },
236 | checked(el) {
237 | var nodeName = el.nodeName.toLowerCase();
238 | return (nodeName === 'input' && el.checked) ||
239 | (nodeName === 'option' && el.selected);
240 | }
241 | };
242 |
243 | var attributeExpr = {
244 | '~='(elValue, value) {
245 | if (!value || value.indexOf(' ') > -1) {
246 | return 0;
247 | }
248 | return (' ' + elValue + ' ').indexOf(' ' + value + ' ') !== -1;
249 | },
250 | '|='(elValue, value) {
251 | return (' ' + elValue).indexOf(' ' + value + '-') !== -1;
252 | },
253 | '^='(elValue, value) {
254 | return value && util.startsWith(elValue, value);
255 | },
256 | '$='(elValue, value) {
257 | return value && util.endsWith(elValue, value);
258 | },
259 | '*='(elValue, value) {
260 | return value && elValue.indexOf(value) !== -1;
261 | },
262 | '='(elValue, value) {
263 | return elValue === value;
264 | }
265 | };
266 |
267 | var relativeExpr = {
268 | '>': {
269 | dir: 'parentNode',
270 | immediate: 1
271 | },
272 | ' ': {
273 | dir: 'parentNode'
274 | },
275 | '+': {
276 | dir: 'previousSibling',
277 | immediate: 1
278 | },
279 | '~': {
280 | dir: 'previousSibling'
281 | }
282 | };
283 |
284 | matchExpr = {
285 | tag: isTag,
286 | cls: hasSingleClass,
287 | id(el, value) {
288 | return getAttr(el, 'id') === value;
289 | },
290 | attrib(el, value) {
291 | var name = value.ident;
292 | if (!isContextXML) {
293 | name = name.toLowerCase();
294 | }
295 | var elValue = getAttr(el, name);
296 | var match = value.match;
297 | if (!match && elValue !== undefined) {
298 | return 1;
299 | } else if (match) {
300 | if (elValue === undefined) {
301 | return 0;
302 | }
303 | var matchFn = attributeExpr[match];
304 | if (matchFn) {
305 | return matchFn(elValue + '', value.value + '');
306 | }
307 | }
308 | return 0;
309 | },
310 | pseudo(el, value) {
311 | var fn, fnStr, ident;
312 | if ((fnStr = value.fn)) {
313 | if (!(fn = pseudoFnExpr[fnStr])) {
314 | throw new SyntaxError('Syntax error: not support pseudo: ' + fnStr);
315 | }
316 | return fn(el, value.param);
317 | }
318 | if ((ident = value.ident)) {
319 | if (!pseudoIdentExpr[ident]) {
320 | throw new SyntaxError('Syntax error: not support pseudo: ' + ident);
321 | }
322 | return pseudoIdentExpr[ident](el);
323 | }
324 | return 0;
325 | }
326 | };
327 |
328 | function unEscape(str) {
329 | return str.replace(unescape, unescapeFn);
330 | }
331 |
332 | parser.lexer.yy = {
333 | trim: util.trim,
334 | unEscape: unEscape,
335 | unEscapeStr(str) {
336 | return this.unEscape(str.slice(1, -1));
337 | }
338 | };
339 |
340 | function resetStatus() {
341 | subMatchesCache = {};
342 | }
343 |
344 | function dir(el, direction) {
345 | do {
346 | el = el[direction];
347 | } while (el && el.nodeType !== 1);
348 | return el;
349 | }
350 |
351 | function getAb(param) {
352 | var a = 0,
353 | match,
354 | b = 0;
355 | if (typeof param === 'number') {
356 | b = param;
357 | } else if (param === 'odd') {
358 | a = 2;
359 | b = 1;
360 | } else if (param === 'even') {
361 | a = 2;
362 | b = 0;
363 | } else if ((match = param.replace(/\s/g, '').match(aNPlusB))) {
364 | if (match[1]) {
365 | a = parseInt(match[2], 10);
366 | if (isNaN(a)) {
367 | if (match[2] === '-') {
368 | a = -1;
369 | } else {
370 | a = 1;
371 | }
372 | }
373 | } else {
374 | a = 0;
375 | }
376 | b = parseInt(match[3], 10) || 0;
377 | }
378 | return {
379 | a: a,
380 | b: b
381 | };
382 | }
383 |
384 | function matchIndexByAb(index, a, b, eq) {
385 | if (a === 0) {
386 | if (index === b) {
387 | return eq;
388 | }
389 | } else {
390 | if ((index - b) / a >= 0 && (index - b) % a === 0 && eq) {
391 | return 1;
392 | }
393 | }
394 | return undefined;
395 | }
396 |
397 | function isXML(elem) {
398 | var documentElement = elem && (elem.ownerDocument || elem).documentElement;
399 | return documentElement ? documentElement.nodeName.toLowerCase() !== 'html' : false;
400 | }
401 |
402 | function matches(str, seeds) {
403 | return select(str, null, seeds);
404 | }
405 |
406 | function singleMatch(el, match) {
407 | if (!match) {
408 | return true;
409 | }
410 | if (!el) {
411 | return false;
412 | }
413 |
414 | if (el.nodeType === 9) {
415 | return false;
416 | }
417 |
418 | var matched = 1,
419 | matchSuffix = match.suffix,
420 | matchSuffixLen,
421 | matchSuffixIndex;
422 |
423 | if (match.t === 'tag') {
424 | matched &= matchExpr.tag(el, match.value);
425 | }
426 |
427 | if (matched && matchSuffix) {
428 | matchSuffixLen = matchSuffix.length;
429 | matchSuffixIndex = 0;
430 | for (; matched && matchSuffixIndex < matchSuffixLen; matchSuffixIndex++) {
431 | var singleMatchSuffix = matchSuffix[matchSuffixIndex],
432 | singleMatchSuffixType = singleMatchSuffix.t;
433 | if (matchExpr[singleMatchSuffixType]) {
434 | matched &= matchExpr[singleMatchSuffixType](el, singleMatchSuffix.value);
435 | }
436 | }
437 | }
438 |
439 | return matched;
440 | }
441 |
442 | // match by adjacent immediate single selector match
443 | function matchImmediate(el, match) {
444 | var matched = 1,
445 | startEl = el,
446 | relativeOp,
447 | startMatch = match;
448 |
449 | do {
450 | matched &= singleMatch(el, match);
451 | if (matched) {
452 | // advance
453 | match = match && match.prev;
454 | if (!match) {
455 | return true;
456 | }
457 | relativeOp = relativeExpr[match.nextCombinator];
458 | el = dir(el, relativeOp.dir);
459 | if (!relativeOp.immediate) {
460 | return {
461 | // advance for non-immediate
462 | el: el,
463 | match: match
464 | };
465 | }
466 | } else {
467 | relativeOp = relativeExpr[match.nextCombinator];
468 | if (relativeOp.immediate) {
469 | // retreat but advance startEl
470 | return {
471 | el: dir(startEl, relativeExpr[startMatch.nextCombinator].dir),
472 | match: startMatch
473 | };
474 | } else {
475 | // advance (before immediate match + jump unmatched)
476 | return {
477 | el: el && dir(el, relativeOp.dir),
478 | match: match
479 | };
480 | }
481 | }
482 | } while (el);
483 |
484 | // only occur when match immediate
485 | return {
486 | el: dir(startEl, relativeExpr[startMatch.nextCombinator].dir),
487 | match: startMatch
488 | };
489 | }
490 |
491 | // find fixed part, fixed with seeds
492 | function findFixedMatchFromHead(el, head) {
493 | var relativeOp,
494 | cur = head;
495 |
496 | do {
497 | if (!singleMatch(el, cur)) {
498 | return null;
499 | }
500 | cur = cur.prev;
501 | if (!cur) {
502 | return true;
503 | }
504 | relativeOp = relativeExpr[cur.nextCombinator];
505 | el = dir(el, relativeOp.dir);
506 | } while (el && relativeOp.immediate);
507 | if (!el) {
508 | return null;
509 | }
510 | return {
511 | el: el,
512 | match: cur
513 | };
514 | }
515 |
516 | function genId(el) {
517 | var selectorId;
518 |
519 | if (isContextXML) {
520 | if (!(selectorId = el.getAttribute(EXPANDO_SELECTOR_KEY))) {
521 | el.setAttribute(EXPANDO_SELECTOR_KEY, selectorId = (+new Date() + '_' + (++uuid)));
522 | }
523 | } else {
524 | if (!(selectorId = el[EXPANDO_SELECTOR_KEY])) {
525 | selectorId = el[EXPANDO_SELECTOR_KEY] = (+new Date()) + '_' + (++uuid);
526 | }
527 | }
528 |
529 | return selectorId;
530 | }
531 |
532 | function matchSub(el, match) {
533 | var selectorId = genId(el),
534 | matchKey;
535 | matchKey = selectorId + '_' + (match.order || 0);
536 | if (matchKey in subMatchesCache) {
537 | return subMatchesCache[matchKey];
538 | }
539 | subMatchesCache[matchKey] = matchSubInternal(el, match);
540 | return subMatchesCache[matchKey];
541 | }
542 |
543 | // recursive match by sub selector string from right to left
544 | // grouped by immediate selectors
545 | function matchSubInternal(el, match) {
546 | var matchImmediateRet = matchImmediate(el, match);
547 | if (matchImmediateRet === true) {
548 | return true;
549 | } else {
550 | el = matchImmediateRet.el;
551 | match = matchImmediateRet.match;
552 | while (el) {
553 | if (matchSub(el, match)) {
554 | return true;
555 | }
556 | el = dir(el, relativeExpr[match.nextCombinator].dir);
557 | }
558 | return false;
559 | }
560 | }
561 |
562 | function select(str, context, seeds) {
563 | if (!caches[str]) {
564 | caches[str] = parser.parse(str);
565 | }
566 |
567 | var selector = caches[str],
568 | groupIndex = 0,
569 | groupLen = selector.length,
570 | contextDocument,
571 | group,
572 | ret = [];
573 |
574 | if (seeds) {
575 | context = context || seeds[0].ownerDocument;
576 | }
577 |
578 | contextDocument = context && context.ownerDocument || typeof document !== 'undefined' && document;
579 |
580 | if (context && context.nodeType === 9 && !contextDocument) {
581 | contextDocument = context;
582 | }
583 |
584 | context = context || contextDocument;
585 |
586 | isContextXML = isXML(context);
587 |
588 | for (; groupIndex < groupLen; groupIndex++) {
589 | resetStatus();
590 |
591 | group = selector[groupIndex];
592 |
593 | var suffix = group.suffix,
594 | suffixIndex,
595 | suffixLen,
596 | seedsIndex,
597 | mySeeds = seeds,
598 | seedsLen,
599 | id = null;
600 |
601 | if (!mySeeds) {
602 | if (suffix && !isContextXML) {
603 | suffixIndex = 0;
604 | suffixLen = suffix.length;
605 | for (; suffixIndex < suffixLen; suffixIndex++) {
606 | var singleSuffix = suffix[suffixIndex];
607 | if (singleSuffix.t === 'id') {
608 | id = singleSuffix.value;
609 | break;
610 | }
611 | }
612 | }
613 |
614 | if (id) {
615 | // http://yiminghe.github.io/lab/playground/fragment-selector/selector.html
616 | var doesNotHasById = !context.getElementById,
617 | contextInDom = util.contains(contextDocument, context),
618 | tmp = doesNotHasById ? (
619 | contextInDom ?
620 | contextDocument.getElementById(id) :
621 | null
622 | ) : context.getElementById(id);
623 | // id bug
624 | // https://github.com/kissyteam/kissy/issues/67
625 | if (!tmp && doesNotHasById || tmp && getAttr(tmp, 'id') !== id) {
626 | var tmps = util.getElementsByTagName('*', context),
627 | tmpLen = tmps.length,
628 | tmpI = 0;
629 | for (; tmpI < tmpLen; tmpI++) {
630 | tmp = tmps[tmpI];
631 | if (getAttr(tmp, 'id') === id) {
632 | mySeeds = [tmp];
633 | break;
634 | }
635 | }
636 | if (tmpI === tmpLen) {
637 | mySeeds = [];
638 | }
639 | } else {
640 | if (contextInDom && tmp && context !== contextDocument) {
641 | tmp = util.contains(context, tmp) ? tmp : null;
642 | }
643 | mySeeds = tmp ? [tmp] : [];
644 | }
645 | } else {
646 | mySeeds = util.getElementsByTagName(group.value || '*', context);
647 | }
648 | }
649 |
650 | seedsIndex = 0;
651 | seedsLen = mySeeds.length;
652 |
653 | if (!seedsLen) {
654 | continue;
655 | }
656 |
657 | for (; seedsIndex < seedsLen; seedsIndex++) {
658 | var seed = mySeeds[seedsIndex];
659 | var matchHead = findFixedMatchFromHead(seed, group);
660 | if (matchHead === true) {
661 | ret.push(seed);
662 | } else if (matchHead) {
663 | if (matchSub(matchHead.el, matchHead.match)) {
664 | ret.push(seed);
665 | }
666 | }
667 | }
668 | }
669 |
670 | if (groupLen > 1) {
671 | ret = util.unique(ret);
672 | }
673 |
674 | return ret;
675 | }
676 |
677 | export default select;
678 |
679 | select.parse = function (str) {
680 | return parser.parse(str);
681 | };
682 |
683 | select.matches = matches;
684 |
685 | select.util = util;
686 | /**
687 | * @ignore
688 | * note 2013-03-28
689 | * - use recursive call to replace backtracking algorithm
690 | *
691 | * refer
692 | * - http://www.w3.org/TR/selectors/
693 | * - http://www.impressivewebs.com/browser-support-css3-selectors/
694 | * - http://blogs.msdn.com/ie/archive/2010/05/13/the-css-corner-css3-selectors.aspx
695 | * - http://sizzlejs.com/
696 | */
697 |
--------------------------------------------------------------------------------
/src/query-selector/gen-parser.md:
--------------------------------------------------------------------------------
1 | ```
2 | node ../../node_modules/kison/bin/kison -g parser-grammar.kison
3 | ```
4 |
--------------------------------------------------------------------------------
/src/query-selector/parser-grammar.kison:
--------------------------------------------------------------------------------
1 | /**
2 | * lalr grammar and lexer rules for css selector.
3 | * refer: http://www.w3.org/TR/selectors/
4 | * @author yiminghe@gmail.com
5 | */
6 | (function () {
7 | var escape = '(?:\\\\[^\\n\\r\\f0-9a-f])';
8 |
9 | var nmstart = '(?:[\\w]|[^\\x00-\\xa0]|' + escape + ')';
10 |
11 | var nmchar = '(?:[\\w\\d-]|[^\\x00-\\xa0]|' + escape + ')';
12 |
13 | var ident = '(?:' + nmstart + nmchar + '*)';
14 |
15 | var name = '(?:' + nmchar + '+)';
16 |
17 | var w = '(?:[\\t\\r\\n\\f\\x20]*)';
18 |
19 | var s = '(?:[\\t\\r\\n\\f\\x20]+)';
20 |
21 | return {
22 | productions: [
23 | // ---------------------- selectors_group
24 | {
25 | symbol: 'selectors_group',
26 | rhs: ['selector'],
27 | action: function () {
28 | return [this.$1];
29 | }
30 | },
31 | {
32 | symbol: 'selectors_group',
33 | rhs: ['selectors_group', 'COMMA', 'selector'],
34 | action: function () {
35 | this.$1.push(this.$3);
36 | }
37 | },
38 |
39 | // ---------------------- selector
40 | {
41 | symbol: 'selector',
42 | rhs: ['simple_selector_sequence']
43 | },
44 | {
45 | symbol: 'selector',
46 | rhs: ['selector', 'combinator', 'simple_selector_sequence'],
47 | action: function () {
48 | // LinkedList
49 |
50 | this.$1.nextCombinator = this.$3.prevCombinator = this.$2;
51 | var order;
52 | order = this.$1.order = this.$1.order || 0;
53 | this.$3.order = order + 1;
54 | this.$3.prev = this.$1;
55 | this.$1.next = this.$3;
56 | return this.$3;
57 | }
58 | },
59 |
60 | // ---------------------- combinator
61 | {
62 | symbol: 'combinator',
63 | rhs: ['PLUS']
64 | },
65 | {
66 | symbol: 'combinator',
67 | rhs: ['GREATER']
68 | },
69 | {
70 | symbol: 'combinator',
71 | rhs: ['TILDE']
72 | },
73 | {
74 | symbol: 'combinator',
75 | rhs: ['S'],
76 | action: function () {
77 | return ' ';
78 | }
79 | },
80 |
81 | // ---------------------- type_selector
82 | {
83 | symbol: 'type_selector',
84 | rhs: ['IDENT'],
85 | action: function () {
86 | return {
87 | t: 'tag',
88 | value: this.$1
89 | };
90 | }
91 | },
92 | {
93 | symbol: 'type_selector',
94 | rhs: ['UNIVERSAL'],
95 | action: function () {
96 | return {
97 | t: 'tag',
98 | value: this.$1
99 | };
100 | }
101 | },
102 |
103 | {
104 | symbol: 'id_selector',
105 | rhs: ['HASH'],
106 | action: function () {
107 | return {
108 | t: 'id',
109 | value: this.$1
110 | };
111 | }
112 | },
113 |
114 | {
115 | symbol: 'class_selector',
116 | rhs: ['CLASS'],
117 | action: function () {
118 | return {
119 | t: 'cls',
120 | value: this.$1
121 | };
122 | }
123 | },
124 |
125 | // ---------------------- attrib_match
126 | {
127 | symbol: 'attrib_match',
128 | rhs: ['PREFIX_MATCH']
129 | },
130 | {
131 | symbol: 'attrib_match',
132 | rhs: ['SUFFIX_MATCH']
133 | },
134 | {
135 | symbol: 'attrib_match',
136 | rhs: ['SUBSTRING_MATCH']
137 | },
138 | {
139 | symbol: 'attrib_match',
140 | rhs: ['ALL_MATCH']
141 | },
142 | {
143 | symbol: 'attrib_match',
144 | rhs: ['INCLUDES']
145 | },
146 | {
147 | symbol: 'attrib_match',
148 | rhs: ['DASH_MATCH']
149 | },
150 |
151 | // ---------------------- attrib
152 | {
153 | symbol: 'attrib',
154 | rhs: ['LEFT_BRACKET', 'IDENT', 'RIGHT_BRACKET'],
155 | action: function () {
156 | return {
157 | t: 'attrib',
158 | value: {
159 | ident: this.$2
160 | }
161 | };
162 | }
163 | },
164 | {
165 | symbol: 'attrib_val',
166 | rhs: ['IDENT']
167 | },
168 | {
169 | symbol: 'attrib_val',
170 | rhs: ['STRING']
171 | },
172 | {
173 | symbol: 'attrib',
174 | rhs: ['LEFT_BRACKET', 'IDENT', 'attrib_match', 'attrib_val', 'RIGHT_BRACKET'],
175 | action: function () {
176 | return {
177 | t: 'attrib',
178 | value: {
179 | ident: this.$2,
180 | match: this.$3,
181 | value: this.$4
182 | }
183 | };
184 | }
185 | },
186 |
187 | // ---------------------- pseudo
188 | {
189 | symbol: 'pseudo',
190 | rhs: ['COLON', 'FUNCTION', 'PARAMETER', 'RIGHT_PARENTHESES'],
191 | action: function () {
192 | return {
193 | t: 'pseudo',
194 | value: {
195 | fn: this.$2.toLowerCase(),
196 | param: this.$3
197 | }
198 | };
199 | }
200 | },
201 | {
202 | symbol: 'pseudo',
203 | rhs: ['COLON', 'IDENT'],
204 | action: function () {
205 | return {
206 | t: 'pseudo',
207 | value: {
208 | ident: this.$2.toLowerCase()
209 | }
210 | };
211 | }
212 | },
213 |
214 | // ---------------------- negation
215 | {
216 | symbol: 'negation',
217 | rhs: ['NOT', 'negation_arg', 'RIGHT_PARENTHESES'],
218 | action: function () {
219 | return {
220 | t: 'pseudo',
221 | value: {
222 | fn: 'not',
223 | param: this.$2
224 | }
225 | };
226 | }
227 | },
228 | {
229 | symbol: 'negation_arg',
230 | rhs: ['type_selector']
231 | },
232 | {
233 | symbol: 'negation_arg',
234 | rhs: ['id_selector']
235 | },
236 | {
237 | symbol: 'negation_arg',
238 | rhs: ['class_selector']
239 | },
240 | {
241 | symbol: 'negation_arg',
242 | rhs: ['attrib']
243 | },
244 | {
245 | symbol: 'negation_arg',
246 | rhs: ['pseudo']
247 | },
248 |
249 | // ---------------------- suffix_selector
250 | {
251 | symbol: 'suffix_selector',
252 | rhs: ['id_selector']
253 | },
254 | {
255 | symbol: 'suffix_selector',
256 | rhs: ['class_selector']
257 | },
258 | {
259 | symbol: 'suffix_selector',
260 | rhs: ['attrib']
261 | },
262 | {
263 | symbol: 'suffix_selector',
264 | rhs: ['pseudo']
265 | },
266 | {
267 | symbol: 'suffix_selector',
268 | rhs: ['negation']
269 | },
270 | {
271 | symbol: 'suffix_selectors',
272 | rhs: ['suffix_selector'],
273 | action: function () {
274 | return [ this.$1 ];
275 | }
276 | },
277 | {
278 | symbol: 'suffix_selectors',
279 | rhs: ['suffix_selectors', 'suffix_selector'],
280 | action: function () {
281 | this.$1.push(this.$2);
282 | }
283 | },
284 |
285 | // ---------------------- simple_selector_sequence
286 | {
287 | symbol: 'simple_selector_sequence',
288 | rhs: ['type_selector']
289 | },
290 | {
291 | symbol: 'simple_selector_sequence',
292 | rhs: ['suffix_selectors'],
293 | action: function () {
294 | return {
295 | suffix: this.$1
296 | };
297 | }
298 | },
299 | {
300 | symbol: 'simple_selector_sequence',
301 | rhs: ['type_selector', 'suffix_selectors'],
302 | action: function () {
303 | return {
304 | t: 'tag',
305 | value: this.$1.value,
306 | suffix: this.$2
307 | };
308 | }
309 | }
310 | ],
311 | lexer: {
312 | rules: [
313 | {
314 | regexp: new RegExp('^' + '\\[' + w),
315 | token: 'LEFT_BRACKET',
316 | action: function () {
317 | this.text = this.yy.trim(this.text);
318 | }
319 | },
320 | {
321 | regexp: new RegExp('^' + w + '\\]'),
322 | token: 'RIGHT_BRACKET',
323 | action: function () {
324 | this.text = this.yy.trim(this.text);
325 | }
326 | },
327 | {
328 | regexp: new RegExp('^' + w + '~=' + w),
329 | token: 'INCLUDES',
330 | action: function () {
331 | this.text = this.yy.trim(this.text);
332 | }
333 | },
334 | {
335 | regexp: new RegExp('^' + w + '\\|=' + w),
336 | token: 'DASH_MATCH',
337 | action: function () {
338 | this.text = this.yy.trim(this.text);
339 | }
340 | },
341 | {
342 | regexp: new RegExp('^' + w + '\\^=' + w),
343 | token: 'PREFIX_MATCH',
344 | action: function () {
345 | this.text = this.yy.trim(this.text);
346 | }
347 | },
348 | {
349 | regexp: new RegExp('^' + w + '\\$=' + w),
350 | token: 'SUFFIX_MATCH',
351 | action: function () {
352 | this.text = this.yy.trim(this.text);
353 | }
354 | },
355 | {
356 | regexp: new RegExp('^' + w + '\\*=' + w),
357 | token: 'SUBSTRING_MATCH',
358 | action: function () {
359 | this.text = this.yy.trim(this.text);
360 | }
361 | },
362 | {
363 | regexp: new RegExp('^' + w + '\\=' + w),
364 | token: 'ALL_MATCH',
365 | action: function () {
366 | this.text = this.yy.trim(this.text);
367 | }
368 | },
369 | {
370 | regexp: new RegExp('^' + ident + '\\('),
371 | token: 'FUNCTION',
372 | action: function () {
373 | this.text = this.yy.trim(this.text).slice(0, -1);
374 | this.pushState('fn');
375 | }
376 | },
377 | {
378 | state: ['fn'],
379 | regexp: /^[^\)]*/,
380 | token: 'PARAMETER',
381 | action: function () {
382 | this.popState();
383 | }
384 | },
385 | {
386 | regexp: new RegExp('^' + w + '\\)'),
387 | token: 'RIGHT_PARENTHESES',
388 | action: function () {
389 | this.text = this.yy.trim(this.text);
390 | }
391 | },
392 | {
393 | regexp: new RegExp('^:not\\(' + w,'i'),
394 | token: 'NOT',
395 | action: function () {
396 | this.text = this.yy.trim(this.text);
397 | }
398 | },
399 | {
400 | regexp: new RegExp('^' + ident),
401 | token: 'IDENT',
402 | action: function () {
403 | this.text = this.yy.unEscape(this.text);
404 | }
405 | },
406 | {
407 | regexp: /^"(\\"|[^"])*"/,
408 | token: 'STRING',
409 | action: function () {
410 | this.text = this.yy.unEscapeStr(this.text);
411 | }
412 | },
413 | {
414 | regexp: /^'(\\'|[^'])*'/,
415 | token: 'STRING',
416 | action: function () {
417 | this.text = this.yy.unEscapeStr(this.text);
418 | }
419 | },
420 | {
421 | regexp: new RegExp('^#' + name),
422 | token: 'HASH',
423 | action: function () {
424 | this.text = this.yy.unEscape(this.text.slice(1));
425 | }
426 | },
427 | {
428 | regexp: new RegExp('^\\.' + ident),
429 | token: 'CLASS',
430 | action: function () {
431 | this.text = this.yy.unEscape(this.text.slice(1));
432 | }
433 | },
434 | {
435 | regexp: new RegExp('^' + w + ',' + w),
436 | token: 'COMMA',
437 | action: function () {
438 | this.text = this.yy.trim(this.text);
439 | }
440 | },
441 | {
442 | regexp: /^::?/,
443 | token: 'COLON'
444 | },
445 | {
446 | regexp: new RegExp('^' + w + '\\+' + w),
447 | token: 'PLUS',
448 | action: function () {
449 | this.text = this.yy.trim(this.text);
450 | }
451 | },
452 | {
453 | regexp: new RegExp('^' + w + '>' + w),
454 | token: 'GREATER',
455 | action: function () {
456 | this.text = this.yy.trim(this.text);
457 | }
458 | },
459 | {
460 | regexp: new RegExp('^' + w + '~' + w),
461 | token: 'TILDE',
462 | action: function () {
463 | this.text = this.yy.trim(this.text);
464 | }
465 | },
466 | {
467 | regexp: /^\*/,
468 | token: 'UNIVERSAL'
469 | },
470 | {
471 | regexp: new RegExp('^' + s),
472 | token: 'S'
473 | },
474 | {
475 | regexp: /^./,
476 | token: 'INVALID'
477 | }
478 | ]
479 | }
480 | };
481 | })();
--------------------------------------------------------------------------------
/src/query-selector/parser.js:
--------------------------------------------------------------------------------
1 | /*
2 | Generated by kison.*/
3 | /*jshint quotmark:false, loopfunc:true, indent:false, unused:false, asi:true, boss:true*/
4 | /* Generated by kison */
5 | var parser = {},
6 | GrammarConst = {
7 | SHIFT_TYPE: 1,
8 | REDUCE_TYPE: 2,
9 | ACCEPT_TYPE: 0,
10 | TYPE_INDEX: 0,
11 | PRODUCTION_INDEX: 1,
12 | TO_INDEX: 2,
13 | };
14 |
15 | /*jslint quotmark: false*/
16 | function mix(to, from) {
17 | for (var f in from) {
18 | to[f] = from[f];
19 | }
20 | }
21 |
22 | function isArray(obj) {
23 | return '[object Array]' === Object.prototype.toString.call(obj);
24 | }
25 |
26 | function each(object, fn, context) {
27 | if (object) {
28 | var key,
29 | val,
30 | length,
31 | i = 0;
32 |
33 | context = context || null;
34 |
35 | if (!isArray(object)) {
36 | for (key in object) {
37 | // can not use hasOwnProperty
38 | if (fn.call(context, object[key], key, object) === false) {
39 | break;
40 | }
41 | }
42 | } else {
43 | length = object.length;
44 | for (val = object[0]; i < length; val = object[++i]) {
45 | if (fn.call(context, val, i, object) === false) {
46 | break;
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
53 | function inArray(item, arr) {
54 | for (var i = 0, l = arr.length; i < l; i++) {
55 | if (arr[i] === item) {
56 | return true;
57 | }
58 | }
59 | return false;
60 | }
61 |
62 | var Lexer = function Lexer(cfg) {
63 | var self = this;
64 |
65 | /*
66 | lex rules.
67 | @type {Object[]}
68 | @example
69 | [
70 | {
71 | regexp:'\\w+',
72 | state:['xx'],
73 | token:'c',
74 | // this => lex
75 | action:function(){}
76 | }
77 | ]
78 | */
79 | self.rules = [];
80 |
81 | mix(self, cfg);
82 |
83 | /*
84 | Input languages
85 | @type {String}
86 | */
87 |
88 | self.resetInput(self.input);
89 | };
90 | Lexer.prototype = {
91 | resetInput: function(input) {
92 | mix(this, {
93 | input: input,
94 | matched: '',
95 | stateStack: [Lexer.STATIC.INITIAL],
96 | match: '',
97 | text: '',
98 | firstLine: 1,
99 | lineNumber: 1,
100 | lastLine: 1,
101 | firstColumn: 1,
102 | lastColumn: 1,
103 | });
104 | },
105 | getCurrentRules: function() {
106 | var self = this,
107 | currentState = self.stateStack[self.stateStack.length - 1],
108 | rules = [];
109 | //#JSCOVERAGE_IF
110 | if (self.mapState) {
111 | currentState = self.mapState(currentState);
112 | }
113 | each(self.rules, function(r) {
114 | var state = r.state || r[3];
115 | if (!state) {
116 | if (currentState === Lexer.STATIC.INITIAL) {
117 | rules.push(r);
118 | }
119 | } else if (inArray(currentState, state)) {
120 | rules.push(r);
121 | }
122 | });
123 | return rules;
124 | },
125 | pushState: function(state) {
126 | this.stateStack.push(state);
127 | },
128 | popState: function(num) {
129 | num = num || 1;
130 | var ret;
131 | while (num--) {
132 | ret = this.stateStack.pop();
133 | }
134 | return ret;
135 | },
136 | showDebugInfo: function() {
137 | var self = this,
138 | DEBUG_CONTEXT_LIMIT = Lexer.STATIC.DEBUG_CONTEXT_LIMIT,
139 | matched = self.matched,
140 | match = self.match,
141 | input = self.input;
142 | matched = matched.slice(0, matched.length - match.length);
143 | //#JSCOVERAGE_IF 0
144 | var past =
145 | (matched.length > DEBUG_CONTEXT_LIMIT ? '...' : '') +
146 | matched.slice(0 - DEBUG_CONTEXT_LIMIT).replace(/\n/, ' '),
147 | next = match + input;
148 | //#JSCOVERAGE_ENDIF
149 | next =
150 | next.slice(0, DEBUG_CONTEXT_LIMIT) +
151 | (next.length > DEBUG_CONTEXT_LIMIT ? '...' : '');
152 | return past + next + '\n' + new Array(past.length + 1).join('-') + '^';
153 | },
154 | mapSymbol: function mapSymbolForCodeGen(t) {
155 | return this.symbolMap[t];
156 | },
157 | mapReverseSymbol: function(rs) {
158 | var self = this,
159 | symbolMap = self.symbolMap,
160 | i,
161 | reverseSymbolMap = self.reverseSymbolMap;
162 | if (!reverseSymbolMap && symbolMap) {
163 | reverseSymbolMap = self.reverseSymbolMap = {};
164 | for (i in symbolMap) {
165 | reverseSymbolMap[symbolMap[i]] = i;
166 | }
167 | }
168 | //#JSCOVERAGE_IF
169 | if (reverseSymbolMap) {
170 | return reverseSymbolMap[rs];
171 | } else {
172 | return rs;
173 | }
174 | },
175 | lex: function() {
176 | var self = this,
177 | input = self.input,
178 | i,
179 | rule,
180 | m,
181 | ret,
182 | lines,
183 | rules = self.getCurrentRules();
184 |
185 | self.match = self.text = '';
186 |
187 | if (!input) {
188 | return self.mapSymbol(Lexer.STATIC.END_TAG);
189 | }
190 |
191 | for (i = 0; i < rules.length; i++) {
192 | rule = rules[i];
193 | //#JSCOVERAGE_IF 0
194 | var regexp = rule.regexp || rule[1],
195 | token = rule.token || rule[0],
196 | action = rule.action || rule[2] || undefined;
197 | //#JSCOVERAGE_ENDIF
198 | if ((m = input.match(regexp))) {
199 | lines = m[0].match(/\n.*/g);
200 | if (lines) {
201 | self.lineNumber += lines.length;
202 | }
203 | mix(self, {
204 | firstLine: self.lastLine,
205 | lastLine: self.lineNumber + 1,
206 | firstColumn: self.lastColumn,
207 | lastColumn: lines
208 | ? lines[lines.length - 1].length - 1
209 | : self.lastColumn + m[0].length,
210 | });
211 | var match;
212 | // for error report
213 | match = self.match = m[0];
214 |
215 | // all matches
216 | self.matches = m;
217 | // may change by user
218 | self.text = match;
219 | // matched content utils now
220 | self.matched += match;
221 | ret = action && action.call(self);
222 | if (ret === undefined) {
223 | ret = token;
224 | } else {
225 | ret = self.mapSymbol(ret);
226 | }
227 | input = input.slice(match.length);
228 | self.input = input;
229 |
230 | if (ret) {
231 | return ret;
232 | } else {
233 | // ignore
234 | return self.lex();
235 | }
236 | }
237 | }
238 | },
239 | };
240 | Lexer.STATIC = {
241 | INITIAL: 'I',
242 | DEBUG_CONTEXT_LIMIT: 20,
243 | END_TAG: '$EOF',
244 | };
245 | var lexer = new Lexer({
246 | rules: [
247 | [
248 | 'b',
249 | /^\[(?:[\t\r\n\f\x20]*)/,
250 | function() {
251 | this.text = this.yy.trim(this.text);
252 | },
253 | ],
254 | [
255 | 'c',
256 | /^(?:[\t\r\n\f\x20]*)\]/,
257 | function() {
258 | this.text = this.yy.trim(this.text);
259 | },
260 | ],
261 | [
262 | 'd',
263 | /^(?:[\t\r\n\f\x20]*)~=(?:[\t\r\n\f\x20]*)/,
264 | function() {
265 | this.text = this.yy.trim(this.text);
266 | },
267 | ],
268 | [
269 | 'e',
270 | /^(?:[\t\r\n\f\x20]*)\|=(?:[\t\r\n\f\x20]*)/,
271 | function() {
272 | this.text = this.yy.trim(this.text);
273 | },
274 | ],
275 | [
276 | 'f',
277 | /^(?:[\t\r\n\f\x20]*)\^=(?:[\t\r\n\f\x20]*)/,
278 | function() {
279 | this.text = this.yy.trim(this.text);
280 | },
281 | ],
282 | [
283 | 'g',
284 | /^(?:[\t\r\n\f\x20]*)\$=(?:[\t\r\n\f\x20]*)/,
285 | function() {
286 | this.text = this.yy.trim(this.text);
287 | },
288 | ],
289 | [
290 | 'h',
291 | /^(?:[\t\r\n\f\x20]*)\*=(?:[\t\r\n\f\x20]*)/,
292 | function() {
293 | this.text = this.yy.trim(this.text);
294 | },
295 | ],
296 | [
297 | 'i',
298 | /^(?:[\t\r\n\f\x20]*)\=(?:[\t\r\n\f\x20]*)/,
299 | function() {
300 | this.text = this.yy.trim(this.text);
301 | },
302 | ],
303 | [
304 | 'j',
305 | /^(?:(?:[\w]|[^\x00-\xa0]|(?:\\[^\n\r\f0-9a-f]))(?:[\w\d-]|[^\x00-\xa0]|(?:\\[^\n\r\f0-9a-f]))*)\(/,
306 | function() {
307 | this.text = this.yy.trim(this.text).slice(0, -1);
308 | this.pushState('fn');
309 | },
310 | ],
311 | [
312 | 'k',
313 | /^[^\)]*/,
314 | function() {
315 | this.popState();
316 | },
317 | ['fn'],
318 | ],
319 | [
320 | 'l',
321 | /^(?:[\t\r\n\f\x20]*)\)/,
322 | function() {
323 | this.text = this.yy.trim(this.text);
324 | },
325 | ],
326 | [
327 | 'm',
328 | /^:not\((?:[\t\r\n\f\x20]*)/i,
329 | function() {
330 | this.text = this.yy.trim(this.text);
331 | },
332 | ],
333 | [
334 | 'n',
335 | /^(?:(?:[\w]|[^\x00-\xa0]|(?:\\[^\n\r\f0-9a-f]))(?:[\w\d-]|[^\x00-\xa0]|(?:\\[^\n\r\f0-9a-f]))*)/,
336 | function() {
337 | this.text = this.yy.unEscape(this.text);
338 | },
339 | ],
340 | [
341 | 'o',
342 | /^"(\\"|[^"])*"/,
343 | function() {
344 | this.text = this.yy.unEscapeStr(this.text);
345 | },
346 | ],
347 | [
348 | 'o',
349 | /^'(\\'|[^'])*'/,
350 | function() {
351 | this.text = this.yy.unEscapeStr(this.text);
352 | },
353 | ],
354 | [
355 | 'p',
356 | /^#(?:(?:[\w\d-]|[^\x00-\xa0]|(?:\\[^\n\r\f0-9a-f]))+)/,
357 | function() {
358 | this.text = this.yy.unEscape(this.text.slice(1));
359 | },
360 | ],
361 | [
362 | 'q',
363 | /^\.(?:(?:[\w]|[^\x00-\xa0]|(?:\\[^\n\r\f0-9a-f]))(?:[\w\d-]|[^\x00-\xa0]|(?:\\[^\n\r\f0-9a-f]))*)/,
364 | function() {
365 | this.text = this.yy.unEscape(this.text.slice(1));
366 | },
367 | ],
368 | [
369 | 'r',
370 | /^(?:[\t\r\n\f\x20]*),(?:[\t\r\n\f\x20]*)/,
371 | function() {
372 | this.text = this.yy.trim(this.text);
373 | },
374 | ],
375 | ['s', /^::?/, 0],
376 | [
377 | 't',
378 | /^(?:[\t\r\n\f\x20]*)\+(?:[\t\r\n\f\x20]*)/,
379 | function() {
380 | this.text = this.yy.trim(this.text);
381 | },
382 | ],
383 | [
384 | 'u',
385 | /^(?:[\t\r\n\f\x20]*)>(?:[\t\r\n\f\x20]*)/,
386 | function() {
387 | this.text = this.yy.trim(this.text);
388 | },
389 | ],
390 | [
391 | 'v',
392 | /^(?:[\t\r\n\f\x20]*)~(?:[\t\r\n\f\x20]*)/,
393 | function() {
394 | this.text = this.yy.trim(this.text);
395 | },
396 | ],
397 | ['w', /^\*/, 0],
398 | ['x', /^(?:[\t\r\n\f\x20]+)/, 0],
399 | ['y', /^./, 0],
400 | ],
401 | });
402 | parser.lexer = lexer;
403 | lexer.symbolMap = {
404 | $EOF: 'a',
405 | LEFT_BRACKET: 'b',
406 | RIGHT_BRACKET: 'c',
407 | INCLUDES: 'd',
408 | DASH_MATCH: 'e',
409 | PREFIX_MATCH: 'f',
410 | SUFFIX_MATCH: 'g',
411 | SUBSTRING_MATCH: 'h',
412 | ALL_MATCH: 'i',
413 | FUNCTION: 'j',
414 | PARAMETER: 'k',
415 | RIGHT_PARENTHESES: 'l',
416 | NOT: 'm',
417 | IDENT: 'n',
418 | STRING: 'o',
419 | HASH: 'p',
420 | CLASS: 'q',
421 | COMMA: 'r',
422 | COLON: 's',
423 | PLUS: 't',
424 | GREATER: 'u',
425 | TILDE: 'v',
426 | UNIVERSAL: 'w',
427 | S: 'x',
428 | INVALID: 'y',
429 | $START: 'z',
430 | selectors_group: 'aa',
431 | selector: 'ab',
432 | simple_selector_sequence: 'ac',
433 | combinator: 'ad',
434 | type_selector: 'ae',
435 | id_selector: 'af',
436 | class_selector: 'ag',
437 | attrib_match: 'ah',
438 | attrib: 'ai',
439 | attrib_val: 'aj',
440 | pseudo: 'ak',
441 | negation: 'al',
442 | negation_arg: 'am',
443 | suffix_selector: 'an',
444 | suffix_selectors: 'ao',
445 | };
446 | parser.productions = [
447 | ['z', ['aa']],
448 | [
449 | 'aa',
450 | ['ab'],
451 | function() {
452 | return [this.$1];
453 | },
454 | ],
455 | [
456 | 'aa',
457 | ['aa', 'r', 'ab'],
458 | function() {
459 | this.$1.push(this.$3);
460 | },
461 | ],
462 | ['ab', ['ac']],
463 | [
464 | 'ab',
465 | ['ab', 'ad', 'ac'],
466 | function() {
467 | // LinkedList
468 |
469 | this.$1.nextCombinator = this.$3.prevCombinator = this.$2;
470 | var order;
471 | order = this.$1.order = this.$1.order || 0;
472 | this.$3.order = order + 1;
473 | this.$3.prev = this.$1;
474 | this.$1.next = this.$3;
475 | return this.$3;
476 | },
477 | ],
478 | ['ad', ['t']],
479 | ['ad', ['u']],
480 | ['ad', ['v']],
481 | [
482 | 'ad',
483 | ['x'],
484 | function() {
485 | return ' ';
486 | },
487 | ],
488 | [
489 | 'ae',
490 | ['n'],
491 | function() {
492 | return {
493 | t: 'tag',
494 | value: this.$1,
495 | };
496 | },
497 | ],
498 | [
499 | 'ae',
500 | ['w'],
501 | function() {
502 | return {
503 | t: 'tag',
504 | value: this.$1,
505 | };
506 | },
507 | ],
508 | [
509 | 'af',
510 | ['p'],
511 | function() {
512 | return {
513 | t: 'id',
514 | value: this.$1,
515 | };
516 | },
517 | ],
518 | [
519 | 'ag',
520 | ['q'],
521 | function() {
522 | return {
523 | t: 'cls',
524 | value: this.$1,
525 | };
526 | },
527 | ],
528 | ['ah', ['f']],
529 | ['ah', ['g']],
530 | ['ah', ['h']],
531 | ['ah', ['i']],
532 | ['ah', ['d']],
533 | ['ah', ['e']],
534 | [
535 | 'ai',
536 | ['b', 'n', 'c'],
537 | function() {
538 | return {
539 | t: 'attrib',
540 | value: {
541 | ident: this.$2,
542 | },
543 | };
544 | },
545 | ],
546 | ['aj', ['n']],
547 | ['aj', ['o']],
548 | [
549 | 'ai',
550 | ['b', 'n', 'ah', 'aj', 'c'],
551 | function() {
552 | return {
553 | t: 'attrib',
554 | value: {
555 | ident: this.$2,
556 | match: this.$3,
557 | value: this.$4,
558 | },
559 | };
560 | },
561 | ],
562 | [
563 | 'ak',
564 | ['s', 'j', 'k', 'l'],
565 | function() {
566 | return {
567 | t: 'pseudo',
568 | value: {
569 | fn: this.$2.toLowerCase(),
570 | param: this.$3,
571 | },
572 | };
573 | },
574 | ],
575 | [
576 | 'ak',
577 | ['s', 'n'],
578 | function() {
579 | return {
580 | t: 'pseudo',
581 | value: {
582 | ident: this.$2.toLowerCase(),
583 | },
584 | };
585 | },
586 | ],
587 | [
588 | 'al',
589 | ['m', 'am', 'l'],
590 | function() {
591 | return {
592 | t: 'pseudo',
593 | value: {
594 | fn: 'not',
595 | param: this.$2,
596 | },
597 | };
598 | },
599 | ],
600 | ['am', ['ae']],
601 | ['am', ['af']],
602 | ['am', ['ag']],
603 | ['am', ['ai']],
604 | ['am', ['ak']],
605 | ['an', ['af']],
606 | ['an', ['ag']],
607 | ['an', ['ai']],
608 | ['an', ['ak']],
609 | ['an', ['al']],
610 | [
611 | 'ao',
612 | ['an'],
613 | function() {
614 | return [this.$1];
615 | },
616 | ],
617 | [
618 | 'ao',
619 | ['ao', 'an'],
620 | function() {
621 | this.$1.push(this.$2);
622 | },
623 | ],
624 | ['ac', ['ae']],
625 | [
626 | 'ac',
627 | ['ao'],
628 | function() {
629 | return {
630 | suffix: this.$1,
631 | };
632 | },
633 | ],
634 | [
635 | 'ac',
636 | ['ae', 'ao'],
637 | function() {
638 | return {
639 | t: 'tag',
640 | value: this.$1.value,
641 | suffix: this.$2,
642 | };
643 | },
644 | ],
645 | ];
646 | parser.table = {
647 | gotos: {
648 | '0': {
649 | aa: 8,
650 | ab: 9,
651 | ae: 10,
652 | af: 11,
653 | ag: 12,
654 | ai: 13,
655 | ak: 14,
656 | al: 15,
657 | an: 16,
658 | ao: 17,
659 | ac: 18,
660 | },
661 | '2': {
662 | ae: 20,
663 | af: 21,
664 | ag: 22,
665 | ai: 23,
666 | ak: 24,
667 | am: 25,
668 | },
669 | '9': {
670 | ad: 33,
671 | },
672 | '10': {
673 | af: 11,
674 | ag: 12,
675 | ai: 13,
676 | ak: 14,
677 | al: 15,
678 | an: 16,
679 | ao: 34,
680 | },
681 | '17': {
682 | af: 11,
683 | ag: 12,
684 | ai: 13,
685 | ak: 14,
686 | al: 15,
687 | an: 35,
688 | },
689 | '19': {
690 | ah: 43,
691 | },
692 | '28': {
693 | ab: 46,
694 | ae: 10,
695 | af: 11,
696 | ag: 12,
697 | ai: 13,
698 | ak: 14,
699 | al: 15,
700 | an: 16,
701 | ao: 17,
702 | ac: 18,
703 | },
704 | '33': {
705 | ae: 10,
706 | af: 11,
707 | ag: 12,
708 | ai: 13,
709 | ak: 14,
710 | al: 15,
711 | an: 16,
712 | ao: 17,
713 | ac: 47,
714 | },
715 | '34': {
716 | af: 11,
717 | ag: 12,
718 | ai: 13,
719 | ak: 14,
720 | al: 15,
721 | an: 35,
722 | },
723 | '43': {
724 | aj: 50,
725 | },
726 | '46': {
727 | ad: 33,
728 | },
729 | },
730 | action: {
731 | '0': {
732 | b: [1, undefined, 1],
733 | m: [1, undefined, 2],
734 | n: [1, undefined, 3],
735 | p: [1, undefined, 4],
736 | q: [1, undefined, 5],
737 | s: [1, undefined, 6],
738 | w: [1, undefined, 7],
739 | },
740 | '1': {
741 | n: [1, undefined, 19],
742 | },
743 | '2': {
744 | b: [1, undefined, 1],
745 | n: [1, undefined, 3],
746 | p: [1, undefined, 4],
747 | q: [1, undefined, 5],
748 | s: [1, undefined, 6],
749 | w: [1, undefined, 7],
750 | },
751 | '3': {
752 | a: [2, 9],
753 | r: [2, 9],
754 | t: [2, 9],
755 | u: [2, 9],
756 | v: [2, 9],
757 | x: [2, 9],
758 | p: [2, 9],
759 | q: [2, 9],
760 | b: [2, 9],
761 | s: [2, 9],
762 | m: [2, 9],
763 | l: [2, 9],
764 | },
765 | '4': {
766 | a: [2, 11],
767 | r: [2, 11],
768 | t: [2, 11],
769 | u: [2, 11],
770 | v: [2, 11],
771 | x: [2, 11],
772 | p: [2, 11],
773 | q: [2, 11],
774 | b: [2, 11],
775 | s: [2, 11],
776 | m: [2, 11],
777 | l: [2, 11],
778 | },
779 | '5': {
780 | a: [2, 12],
781 | r: [2, 12],
782 | t: [2, 12],
783 | u: [2, 12],
784 | v: [2, 12],
785 | x: [2, 12],
786 | p: [2, 12],
787 | q: [2, 12],
788 | b: [2, 12],
789 | s: [2, 12],
790 | m: [2, 12],
791 | l: [2, 12],
792 | },
793 | '6': {
794 | j: [1, undefined, 26],
795 | n: [1, undefined, 27],
796 | },
797 | '7': {
798 | a: [2, 10],
799 | r: [2, 10],
800 | t: [2, 10],
801 | u: [2, 10],
802 | v: [2, 10],
803 | x: [2, 10],
804 | p: [2, 10],
805 | q: [2, 10],
806 | b: [2, 10],
807 | s: [2, 10],
808 | m: [2, 10],
809 | l: [2, 10],
810 | },
811 | '8': {
812 | a: [0],
813 | r: [1, undefined, 28],
814 | },
815 | '9': {
816 | a: [2, 1],
817 | r: [2, 1],
818 | t: [1, undefined, 29],
819 | u: [1, undefined, 30],
820 | v: [1, undefined, 31],
821 | x: [1, undefined, 32],
822 | },
823 | '10': {
824 | a: [2, 38],
825 | r: [2, 38],
826 | t: [2, 38],
827 | u: [2, 38],
828 | v: [2, 38],
829 | x: [2, 38],
830 | b: [1, undefined, 1],
831 | m: [1, undefined, 2],
832 | p: [1, undefined, 4],
833 | q: [1, undefined, 5],
834 | s: [1, undefined, 6],
835 | },
836 | '11': {
837 | a: [2, 31],
838 | r: [2, 31],
839 | t: [2, 31],
840 | u: [2, 31],
841 | v: [2, 31],
842 | x: [2, 31],
843 | p: [2, 31],
844 | q: [2, 31],
845 | b: [2, 31],
846 | s: [2, 31],
847 | m: [2, 31],
848 | },
849 | '12': {
850 | a: [2, 32],
851 | r: [2, 32],
852 | t: [2, 32],
853 | u: [2, 32],
854 | v: [2, 32],
855 | x: [2, 32],
856 | p: [2, 32],
857 | q: [2, 32],
858 | b: [2, 32],
859 | s: [2, 32],
860 | m: [2, 32],
861 | },
862 | '13': {
863 | a: [2, 33],
864 | r: [2, 33],
865 | t: [2, 33],
866 | u: [2, 33],
867 | v: [2, 33],
868 | x: [2, 33],
869 | p: [2, 33],
870 | q: [2, 33],
871 | b: [2, 33],
872 | s: [2, 33],
873 | m: [2, 33],
874 | },
875 | '14': {
876 | a: [2, 34],
877 | r: [2, 34],
878 | t: [2, 34],
879 | u: [2, 34],
880 | v: [2, 34],
881 | x: [2, 34],
882 | p: [2, 34],
883 | q: [2, 34],
884 | b: [2, 34],
885 | s: [2, 34],
886 | m: [2, 34],
887 | },
888 | '15': {
889 | a: [2, 35],
890 | r: [2, 35],
891 | t: [2, 35],
892 | u: [2, 35],
893 | v: [2, 35],
894 | x: [2, 35],
895 | p: [2, 35],
896 | q: [2, 35],
897 | b: [2, 35],
898 | s: [2, 35],
899 | m: [2, 35],
900 | },
901 | '16': {
902 | a: [2, 36],
903 | r: [2, 36],
904 | t: [2, 36],
905 | u: [2, 36],
906 | v: [2, 36],
907 | x: [2, 36],
908 | p: [2, 36],
909 | q: [2, 36],
910 | b: [2, 36],
911 | s: [2, 36],
912 | m: [2, 36],
913 | },
914 | '17': {
915 | a: [2, 39],
916 | r: [2, 39],
917 | t: [2, 39],
918 | u: [2, 39],
919 | v: [2, 39],
920 | x: [2, 39],
921 | b: [1, undefined, 1],
922 | m: [1, undefined, 2],
923 | p: [1, undefined, 4],
924 | q: [1, undefined, 5],
925 | s: [1, undefined, 6],
926 | },
927 | '18': {
928 | a: [2, 3],
929 | r: [2, 3],
930 | t: [2, 3],
931 | u: [2, 3],
932 | v: [2, 3],
933 | x: [2, 3],
934 | },
935 | '19': {
936 | c: [1, undefined, 36],
937 | d: [1, undefined, 37],
938 | e: [1, undefined, 38],
939 | f: [1, undefined, 39],
940 | g: [1, undefined, 40],
941 | h: [1, undefined, 41],
942 | i: [1, undefined, 42],
943 | },
944 | '20': {
945 | l: [2, 26],
946 | },
947 | '21': {
948 | l: [2, 27],
949 | },
950 | '22': {
951 | l: [2, 28],
952 | },
953 | '23': {
954 | l: [2, 29],
955 | },
956 | '24': {
957 | l: [2, 30],
958 | },
959 | '25': {
960 | l: [1, undefined, 44],
961 | },
962 | '26': {
963 | k: [1, undefined, 45],
964 | },
965 | '27': {
966 | a: [2, 24],
967 | r: [2, 24],
968 | t: [2, 24],
969 | u: [2, 24],
970 | v: [2, 24],
971 | x: [2, 24],
972 | p: [2, 24],
973 | q: [2, 24],
974 | b: [2, 24],
975 | s: [2, 24],
976 | m: [2, 24],
977 | l: [2, 24],
978 | },
979 | '28': {
980 | b: [1, undefined, 1],
981 | m: [1, undefined, 2],
982 | n: [1, undefined, 3],
983 | p: [1, undefined, 4],
984 | q: [1, undefined, 5],
985 | s: [1, undefined, 6],
986 | w: [1, undefined, 7],
987 | },
988 | '29': {
989 | n: [2, 5],
990 | w: [2, 5],
991 | p: [2, 5],
992 | q: [2, 5],
993 | b: [2, 5],
994 | s: [2, 5],
995 | m: [2, 5],
996 | },
997 | '30': {
998 | n: [2, 6],
999 | w: [2, 6],
1000 | p: [2, 6],
1001 | q: [2, 6],
1002 | b: [2, 6],
1003 | s: [2, 6],
1004 | m: [2, 6],
1005 | },
1006 | '31': {
1007 | n: [2, 7],
1008 | w: [2, 7],
1009 | p: [2, 7],
1010 | q: [2, 7],
1011 | b: [2, 7],
1012 | s: [2, 7],
1013 | m: [2, 7],
1014 | },
1015 | '32': {
1016 | n: [2, 8],
1017 | w: [2, 8],
1018 | p: [2, 8],
1019 | q: [2, 8],
1020 | b: [2, 8],
1021 | s: [2, 8],
1022 | m: [2, 8],
1023 | },
1024 | '33': {
1025 | b: [1, undefined, 1],
1026 | m: [1, undefined, 2],
1027 | n: [1, undefined, 3],
1028 | p: [1, undefined, 4],
1029 | q: [1, undefined, 5],
1030 | s: [1, undefined, 6],
1031 | w: [1, undefined, 7],
1032 | },
1033 | '34': {
1034 | a: [2, 40],
1035 | r: [2, 40],
1036 | t: [2, 40],
1037 | u: [2, 40],
1038 | v: [2, 40],
1039 | x: [2, 40],
1040 | b: [1, undefined, 1],
1041 | m: [1, undefined, 2],
1042 | p: [1, undefined, 4],
1043 | q: [1, undefined, 5],
1044 | s: [1, undefined, 6],
1045 | },
1046 | '35': {
1047 | a: [2, 37],
1048 | r: [2, 37],
1049 | t: [2, 37],
1050 | u: [2, 37],
1051 | v: [2, 37],
1052 | x: [2, 37],
1053 | p: [2, 37],
1054 | q: [2, 37],
1055 | b: [2, 37],
1056 | s: [2, 37],
1057 | m: [2, 37],
1058 | },
1059 | '36': {
1060 | a: [2, 19],
1061 | r: [2, 19],
1062 | t: [2, 19],
1063 | u: [2, 19],
1064 | v: [2, 19],
1065 | x: [2, 19],
1066 | p: [2, 19],
1067 | q: [2, 19],
1068 | b: [2, 19],
1069 | s: [2, 19],
1070 | m: [2, 19],
1071 | l: [2, 19],
1072 | },
1073 | '37': {
1074 | n: [2, 17],
1075 | o: [2, 17],
1076 | },
1077 | '38': {
1078 | n: [2, 18],
1079 | o: [2, 18],
1080 | },
1081 | '39': {
1082 | n: [2, 13],
1083 | o: [2, 13],
1084 | },
1085 | '40': {
1086 | n: [2, 14],
1087 | o: [2, 14],
1088 | },
1089 | '41': {
1090 | n: [2, 15],
1091 | o: [2, 15],
1092 | },
1093 | '42': {
1094 | n: [2, 16],
1095 | o: [2, 16],
1096 | },
1097 | '43': {
1098 | n: [1, undefined, 48],
1099 | o: [1, undefined, 49],
1100 | },
1101 | '44': {
1102 | a: [2, 25],
1103 | r: [2, 25],
1104 | t: [2, 25],
1105 | u: [2, 25],
1106 | v: [2, 25],
1107 | x: [2, 25],
1108 | p: [2, 25],
1109 | q: [2, 25],
1110 | b: [2, 25],
1111 | s: [2, 25],
1112 | m: [2, 25],
1113 | },
1114 | '45': {
1115 | l: [1, undefined, 51],
1116 | },
1117 | '46': {
1118 | a: [2, 2],
1119 | r: [2, 2],
1120 | t: [1, undefined, 29],
1121 | u: [1, undefined, 30],
1122 | v: [1, undefined, 31],
1123 | x: [1, undefined, 32],
1124 | },
1125 | '47': {
1126 | a: [2, 4],
1127 | r: [2, 4],
1128 | t: [2, 4],
1129 | u: [2, 4],
1130 | v: [2, 4],
1131 | x: [2, 4],
1132 | },
1133 | '48': {
1134 | c: [2, 20],
1135 | },
1136 | '49': {
1137 | c: [2, 21],
1138 | },
1139 | '50': {
1140 | c: [1, undefined, 52],
1141 | },
1142 | '51': {
1143 | a: [2, 23],
1144 | r: [2, 23],
1145 | t: [2, 23],
1146 | u: [2, 23],
1147 | v: [2, 23],
1148 | x: [2, 23],
1149 | p: [2, 23],
1150 | q: [2, 23],
1151 | b: [2, 23],
1152 | s: [2, 23],
1153 | m: [2, 23],
1154 | l: [2, 23],
1155 | },
1156 | '52': {
1157 | a: [2, 22],
1158 | r: [2, 22],
1159 | t: [2, 22],
1160 | u: [2, 22],
1161 | v: [2, 22],
1162 | x: [2, 22],
1163 | p: [2, 22],
1164 | q: [2, 22],
1165 | b: [2, 22],
1166 | s: [2, 22],
1167 | m: [2, 22],
1168 | l: [2, 22],
1169 | },
1170 | },
1171 | };
1172 | parser.parse = function parse(input, filename) {
1173 | var self = this,
1174 | lexer = self.lexer,
1175 | state,
1176 | symbol,
1177 | action,
1178 | table = self.table,
1179 | gotos = table.gotos,
1180 | tableAction = table.action,
1181 | productions = self.productions,
1182 | valueStack = [null],
1183 | // for debug info
1184 | prefix = filename ? 'in file: ' + filename + ' ' : '',
1185 | stack = [0];
1186 |
1187 | lexer.resetInput(input);
1188 |
1189 | while (1) {
1190 | // retrieve state number from top of stack
1191 | state = stack[stack.length - 1];
1192 |
1193 | if (!symbol) {
1194 | symbol = lexer.lex();
1195 | }
1196 |
1197 | if (symbol) {
1198 | // read action for current state and first input
1199 | action = tableAction[state] && tableAction[state][symbol];
1200 | } else {
1201 | action = null;
1202 | }
1203 |
1204 | if (!action) {
1205 | var expected = [],
1206 | error;
1207 | //#JSCOVERAGE_IF
1208 | if (tableAction[state]) {
1209 | for (var symbolForState in tableAction[state]) {
1210 | expected.push(self.lexer.mapReverseSymbol(symbolForState));
1211 | }
1212 | }
1213 | error =
1214 | prefix +
1215 | 'syntax error at line ' +
1216 | lexer.lineNumber +
1217 | ':\n' +
1218 | lexer.showDebugInfo() +
1219 | '\n' +
1220 | 'expect ' +
1221 | expected.join(', ');
1222 | throw new Error(error);
1223 | }
1224 |
1225 | switch (action[GrammarConst.TYPE_INDEX]) {
1226 | case GrammarConst.SHIFT_TYPE:
1227 | stack.push(symbol);
1228 |
1229 | valueStack.push(lexer.text);
1230 |
1231 | // push state
1232 | stack.push(action[GrammarConst.TO_INDEX]);
1233 |
1234 | // allow to read more
1235 | symbol = null;
1236 |
1237 | break;
1238 |
1239 | case GrammarConst.REDUCE_TYPE:
1240 | var production = productions[action[GrammarConst.PRODUCTION_INDEX]],
1241 | reducedSymbol = production.symbol || production[0],
1242 | reducedAction = production.action || production[2],
1243 | reducedRhs = production.rhs || production[1],
1244 | len = reducedRhs.length,
1245 | i = 0,
1246 | ret,
1247 | $$ = valueStack[valueStack.length - len]; // default to $$ = $1
1248 |
1249 | ret = undefined;
1250 |
1251 | self.$$ = $$;
1252 |
1253 | for (; i < len; i++) {
1254 | self['$' + (len - i)] = valueStack[valueStack.length - 1 - i];
1255 | }
1256 |
1257 | if (reducedAction) {
1258 | ret = reducedAction.call(self);
1259 | }
1260 |
1261 | if (ret !== undefined) {
1262 | $$ = ret;
1263 | } else {
1264 | $$ = self.$$;
1265 | }
1266 |
1267 | stack = stack.slice(0, -1 * len * 2);
1268 | valueStack = valueStack.slice(0, -1 * len);
1269 |
1270 | stack.push(reducedSymbol);
1271 |
1272 | valueStack.push($$);
1273 |
1274 | var newState = gotos[stack[stack.length - 2]][stack[stack.length - 1]];
1275 |
1276 | stack.push(newState);
1277 |
1278 | break;
1279 |
1280 | case GrammarConst.ACCEPT_TYPE:
1281 | return $$;
1282 | }
1283 | }
1284 | };
1285 | export default parser;
1286 |
--------------------------------------------------------------------------------
/src/query-selector/util.js:
--------------------------------------------------------------------------------
1 | /**
2 | * attr fix for old ie
3 | * @author yiminghe@gmail.com
4 | */
5 | var R_BOOLEAN = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
6 | R_FOCUSABLE = /^(?:button|input|object|select|textarea)$/i,
7 | R_CLICKABLE = /^a(?:rea)?$/i,
8 | R_INVALID_CHAR = /:|^on/;
9 |
10 | var attrFix = {},
11 | propFix,
12 | attrHooks = {
13 | // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
14 | tabindex: {
15 | get: function (el) {
16 | // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
17 | var attributeNode = el.getAttributeNode('tabindex');
18 | return attributeNode && attributeNode.specified ?
19 | parseInt(attributeNode.value, 10) :
20 | R_FOCUSABLE.test(el.nodeName) ||
21 | R_CLICKABLE.test(el.nodeName) && el.href ?
22 | 0 :
23 | undefined;
24 | }
25 | }
26 | },
27 | boolHook = {
28 | get: function (elem, name) {
29 | // 转发到 prop 方法
30 | return elem[propFix[name] || name] ?
31 | // 根据 w3c attribute , true 时返回属性名字符串
32 | name.toLowerCase() :
33 | undefined;
34 | }
35 | },
36 | attrNodeHook = {};
37 |
38 | attrHooks.style = {
39 | get: function (el) {
40 | return el.style.cssText;
41 | }
42 | };
43 |
44 | propFix = {
45 | hidefocus: 'hideFocus',
46 | tabindex: 'tabIndex',
47 | readonly: 'readOnly',
48 | 'for': 'htmlFor',
49 | 'class': 'className',
50 | maxlength: 'maxLength',
51 | cellspacing: 'cellSpacing',
52 | cellpadding: 'cellPadding',
53 | rowspan: 'rowSpan',
54 | colspan: 'colSpan',
55 | usemap: 'useMap',
56 | frameborder: 'frameBorder',
57 | contenteditable: 'contentEditable'
58 | };
59 |
60 | var ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';
61 | var doc = typeof document !== 'undefined' ? document : {};
62 |
63 | function numberify(s) {
64 | var c = 0;
65 | // convert '1.2.3.4' to 1.234
66 | return parseFloat(s.replace(/\./g, function () {
67 | return (c++ === 0) ? '.' : '';
68 | }));
69 | }
70 |
71 | function ieVersion() {
72 | var m, v;
73 | if ((m = ua.match(/MSIE ([^;]*)|Trident.*; rv(?:\s|:)?([0-9.]+)/)) &&
74 | (v = (m[1] || m[2]))) {
75 | return doc.documentMode || numberify(v);
76 | }
77 | }
78 |
79 | function mix(s, t) {
80 | for (var p in t) {
81 | s[p] = t[p];
82 | }
83 | }
84 |
85 | function each(arr, fn) {
86 | var i = 0, l = arr.length;
87 | for (; i < l; i++) {
88 | if (fn(arr[i], i) === false) {
89 | break;
90 | }
91 | }
92 | }
93 | var ie = ieVersion();
94 |
95 | if (ie && ie < 8) {
96 | attrHooks.style.set = function (el, val) {
97 | el.style.cssText = val;
98 | };
99 |
100 | // get attribute value from attribute node for ie
101 | mix(attrNodeHook, {
102 | get: function (elem, name) {
103 | var ret = elem.getAttributeNode(name);
104 | // Return undefined if attribute node specified by user
105 | return ret && (
106 | // fix #100
107 | ret.specified || ret.nodeValue) ?
108 | ret.nodeValue :
109 | undefined;
110 | }
111 | });
112 |
113 | // ie6,7 不区分 attribute 与 property
114 | mix(attrFix, propFix);
115 |
116 | // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
117 | attrHooks.tabIndex = attrHooks.tabindex;
118 |
119 | // 不光是 href, src, 还有 rowspan 等非 mapping 属性,也需要用第 2 个参数来获取原始值
120 | // 注意 colSpan rowSpan 已经由 propFix 转为大写
121 | each(['href', 'src', 'width', 'height', 'colSpan', 'rowSpan'], function (name) {
122 | attrHooks[name] = {
123 | get: function (elem) {
124 | var ret = elem.getAttribute(name, 2);
125 | return ret === null ? undefined : ret;
126 | }
127 | };
128 | });
129 |
130 | attrHooks.placeholder = {
131 | get: function (elem, name) {
132 | return elem[name] || attrNodeHook.get(elem, name);
133 | }
134 | };
135 | }
136 |
137 | if (ie) {
138 | var hrefFix = attrHooks.href = attrHooks.href || {};
139 | hrefFix.set = function (el, val, name) {
140 | var childNodes = el.childNodes,
141 | b,
142 | len = childNodes.length,
143 | allText = len > 0;
144 | for (len = len - 1; len >= 0; len--) {
145 | if (childNodes[len].nodeType !== 3) {
146 | allText = 0;
147 | }
148 | }
149 | if (allText) {
150 | b = el.ownerDocument.createElement('b');
151 | b.style.display = 'none';
152 | el.appendChild(b);
153 | }
154 | el.setAttribute(name, '' + val);
155 | if (b) {
156 | el.removeChild(b);
157 | }
158 | };
159 | }
160 |
161 | var RE_TRIM = /^[\s\xa0]+|[\s\xa0]+$/g,
162 | trim = String.prototype.trim;
163 | var SPACE = ' ';
164 |
165 | var getElementsByTagName;
166 | getElementsByTagName = function (name, context) {
167 | return context.getElementsByTagName(name);
168 | };
169 |
170 | if (doc.createElement) {
171 | var div = doc.createElement('div');
172 | div.appendChild(document.createComment(''));
173 | if (div.getElementsByTagName('*').length) {
174 | getElementsByTagName = function (name, context) {
175 | var nodes = context.getElementsByTagName(name),
176 | needsFilter = name === '*';
177 | //
178 | if (needsFilter || typeof nodes.length !== 'number') {
179 | var ret = [],
180 | i = 0,
181 | el;
182 | while ((el = nodes[i++])) {
183 | if (!needsFilter || el.nodeType === 1) {
184 | ret.push(el);
185 | }
186 | }
187 | return ret;
188 | } else {
189 | return nodes;
190 | }
191 | };
192 | }
193 | }
194 |
195 | var compareNodeOrder = ('sourceIndex' in (doc && doc.documentElement || {})) ? function (a, b) {
196 | return a.sourceIndex - b.sourceIndex;
197 | } : function (a, b) {
198 | if (!a.compareDocumentPosition || !b.compareDocumentPosition) {
199 | return a.compareDocumentPosition ? -1 : 1;
200 | }
201 | var bit = a.compareDocumentPosition(b) & 4;
202 | return bit ? -1 : 1;
203 | };
204 |
205 | var util = {
206 | ie: ie,
207 |
208 | unique: (function () {
209 | var hasDuplicate,
210 | baseHasDuplicate = true;
211 |
212 | // Here we check if the JavaScript engine is using some sort of
213 | // optimization where it does not always call our comparison
214 | // function. If that is the case, discard the hasDuplicate value.
215 | // Thus far that includes Google Chrome.
216 | [0, 0].sort(function () {
217 | baseHasDuplicate = false;
218 | return 0;
219 | });
220 |
221 | function sortOrder(a, b) {
222 | if (a === b) {
223 | hasDuplicate = true;
224 | return 0;
225 | }
226 |
227 | return compareNodeOrder(a, b);
228 | }
229 |
230 | // 排序去重
231 | return function (elements) {
232 | hasDuplicate = baseHasDuplicate;
233 | elements.sort(sortOrder);
234 |
235 | if (hasDuplicate) {
236 | var i = 1, len = elements.length;
237 | while (i < len) {
238 | if (elements[i] === elements[i - 1]) {
239 | elements.splice(i, 1);
240 | --len;
241 | } else {
242 | i++;
243 | }
244 | }
245 | }
246 | return elements;
247 | };
248 | })(),
249 |
250 | getElementsByTagName: getElementsByTagName,
251 |
252 | getSimpleAttr: function (el, name) {
253 | var ret = el && el.getAttributeNode(name);
254 | if (ret && ret.specified) {
255 | return 'value' in ret ? ret.value : ret.nodeValue;
256 | }
257 | return undefined;
258 | },
259 |
260 | contains: ie ? function (a, b) {
261 | if (a.nodeType === 9) {
262 | a = a.documentElement;
263 | }
264 | // !a.contains => a===document || text
265 | // 注意原生 contains 判断时 a===b 也返回 true
266 | b = b.parentNode;
267 |
268 | if (a === b) {
269 | return true;
270 | }
271 |
272 | // when b is document, a.contains(b) 不支持的接口 in ie
273 | if (b && b.nodeType === 1) {
274 | return a.contains && a.contains(b);
275 | } else {
276 | return false;
277 | }
278 | } : function (a, b) {
279 | return !!(a.compareDocumentPosition(b) & 16);
280 | },
281 |
282 | isTag: function (el, value) {
283 | return value === '*' || el.nodeName.toLowerCase() === value.toLowerCase();
284 | },
285 |
286 | hasSingleClass: function (el, cls) {
287 | // consider xml
288 | // https://github.com/kissyteam/kissy/issues/532
289 | var className = el && util.getSimpleAttr(el, 'class');
290 | return className && (className = className.replace(/[\r\t\n]/g, SPACE)) &&
291 | (SPACE + className + SPACE).indexOf(SPACE + cls + SPACE) > -1;
292 | },
293 |
294 | startsWith: function (str, prefix) {
295 | return str.lastIndexOf(prefix, 0) === 0;
296 | },
297 |
298 | endsWith: function (str, suffix) {
299 | var ind = str.length - suffix.length;
300 | return ind >= 0 && str.indexOf(suffix, ind) === ind;
301 | },
302 |
303 | trim: trim ?
304 | function (str) {
305 | return str == null ? '' : trim.call(str);
306 | } :
307 | function (str) {
308 | return str == null ? '' : (str + '').replace(RE_TRIM, '');
309 | },
310 |
311 | attr: function (el, name) {
312 | var attrNormalizer, ret;
313 | // scrollLeft
314 | name = name.toLowerCase();
315 | // custom attrs
316 | name = attrFix[name] || name;
317 | if (R_BOOLEAN.test(name)) {
318 | attrNormalizer = boolHook;
319 | } else if (R_INVALID_CHAR.test(name)) {
320 | // only old ie?
321 | attrNormalizer = attrNodeHook;
322 | } else {
323 | attrNormalizer = attrHooks[name];
324 | }
325 | if (el && el.nodeType === 1) {
326 | // browsers index elements by id/name on forms, give priority to attributes.
327 | if (el.nodeName.toLowerCase() === 'form') {
328 | attrNormalizer = attrNodeHook;
329 | }
330 | if (attrNormalizer && attrNormalizer.get) {
331 | return attrNormalizer.get(el, name);
332 | }
333 | ret = el.getAttribute(name);
334 | if (ret === '') {
335 | var attrNode = el.getAttributeNode(name);
336 | if (!attrNode || !attrNode.specified) {
337 | return undefined;
338 | }
339 | }
340 | // standard browser non-existing attribute return null
341 | // ie<8 will return undefined , because it return property
342 | // so norm to undefined
343 | return ret === null ? undefined : ret;
344 | }
345 | }
346 | };
347 |
348 | export default util;
349 |
--------------------------------------------------------------------------------