Slick

Standalone CSS Selector Parser and Engine. An official MooTools project.

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/371600/623833/Slick.js

  1. /*
  2. ---
  3. name: Slick.Parser
  4. description: Standalone CSS3 Selector parser
  5. provides: Slick.Parser
  6. ...
  7. */
  8.  
  9. ;(function(){
  10.  
  11. var parsed,
  12. separatorIndex,
  13. combinatorIndex,
  14. reversed,
  15. cache = {},
  16. reverseCache = {},
  17. reUnescape = /\\/g;
  18.  
  19. var parse = function(expression, isReversed){
  20. if (expression == null) return null;
  21. if (expression.Slick === true) return expression;
  22. expression = ('' + expression).replace(/^\s+|\s+$/g, '');
  23. reversed = !!isReversed;
  24. var currentCache = (reversed) ? reverseCache : cache;
  25. if (currentCache[expression]) return currentCache[expression];
  26. parsed = {
  27. Slick: true,
  28. expressions: [],
  29. raw: expression,
  30. reverse: function(){
  31. return parse(this.raw, true);
  32. }
  33. };
  34. separatorIndex = -1;
  35. while (expression != (expression = expression.replace(regexp, parser)));
  36. parsed.length = parsed.expressions.length;
  37. return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
  38. };
  39.  
  40. var reverseCombinator = function(combinator){
  41. if (combinator === '!') return ' ';
  42. else if (combinator === ' ') return '!';
  43. else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
  44. else return '!' + combinator;
  45. };
  46.  
  47. var reverse = function(expression){
  48. var expressions = expression.expressions;
  49. for (var i = 0; i < expressions.length; i++){
  50. var exp = expressions[i];
  51. var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
  52.  
  53. for (var j = 0; j < exp.length; j++){
  54. var cexp = exp[j];
  55. if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
  56. cexp.combinator = cexp.reverseCombinator;
  57. delete cexp.reverseCombinator;
  58. }
  59.  
  60. exp.reverse().push(last);
  61. }
  62. return expression;
  63. };
  64.  
  65. var escapeRegExp = (function(){
  66. // Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
  67. var from = /(?=[\-\[\]{}()*+?.\\\^$|,#\s])/g, to = '\\';
  68. return function(string){ return string.replace(from, to) }
  69. }())
  70.  
  71. var regexp = new RegExp(
  72. /*
  73. #!/usr/bin/env ruby
  74. puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
  75. __END__
  76. "(?x)^(?:\
  77. \\s* ( , ) \\s* # Separator \n\
  78. | \\s* ( <combinator>+ ) \\s* # Combinator \n\
  79. | ( \\s+ ) # CombinatorChildren \n\
  80. | ( <unicode>+ | \\* ) # Tag \n\
  81. | \\# ( <unicode>+ ) # ID \n\
  82. | \\. ( <unicode>+ ) # ClassName \n\
  83. | # Attribute \n\
  84. \\[ \
  85. \\s* (<unicode1>+) (?: \
  86. \\s* ([*^$!~|]?=) (?: \
  87. \\s* (?:\
  88. ([\"']?)(.*?)\\9 \
  89. )\
  90. ) \
  91. )? \\s* \
  92. \\](?!\\]) \n\
  93. | :+ ( <unicode>+ )(?:\
  94. \\( (?:\
  95. (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
  96. ) \\)\
  97. )?\
  98. )"
  99. */
  100. "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
  101. .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
  102. .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
  103. .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
  104. );
  105.  
  106. function parser(
  107. rawMatch,
  108.  
  109. separator,
  110. combinator,
  111. combinatorChildren,
  112.  
  113. tagName,
  114. id,
  115. className,
  116.  
  117. attributeKey,
  118. attributeOperator,
  119. attributeQuote,
  120. attributeValue,
  121.  
  122. pseudoMarker,
  123. pseudoClass,
  124. pseudoQuote,
  125. pseudoClassQuotedValue,
  126. pseudoClassValue
  127. ){
  128. if (separator || separatorIndex === -1){
  129. parsed.expressions[++separatorIndex] = [];
  130. combinatorIndex = -1;
  131. if (separator) return '';
  132. }
  133.  
  134. if (combinator || combinatorChildren || combinatorIndex === -1){
  135. combinator = combinator || ' ';
  136. var currentSeparator = parsed.expressions[separatorIndex];
  137. if (reversed && currentSeparator[combinatorIndex])
  138. currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
  139. currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
  140. }
  141.  
  142. var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
  143.  
  144. if (tagName){
  145. currentParsed.tag = tagName.replace(reUnescape, '');
  146.  
  147. } else if (id){
  148. currentParsed.id = id.replace(reUnescape, '');
  149.  
  150. } else if (className){
  151. className = className.replace(reUnescape, '');
  152.  
  153. if (!currentParsed.classList) currentParsed.classList = [];
  154. if (!currentParsed.classes) currentParsed.classes = [];
  155. currentParsed.classList.push(className);
  156. currentParsed.classes.push({
  157. value: className,
  158. regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
  159. });
  160.  
  161. } else if (pseudoClass){
  162. pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
  163. pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
  164.  
  165. if (!currentParsed.pseudos) currentParsed.pseudos = [];
  166. currentParsed.pseudos.push({
  167. key: pseudoClass.replace(reUnescape, ''),
  168. value: pseudoClassValue,
  169. type: pseudoMarker.length == 1 ? 'class' : 'element'
  170. });
  171.  
  172. } else if (attributeKey){
  173. attributeKey = attributeKey.replace(reUnescape, '');
  174. attributeValue = (attributeValue || '').replace(reUnescape, '');
  175.  
  176. var test, regexp;
  177.  
  178. switch (attributeOperator){
  179. case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
  180. case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
  181. case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
  182. case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
  183. case '=' : test = function(value){
  184. return attributeValue == value;
  185. }; break;
  186. case '*=' : test = function(value){
  187. return value && value.indexOf(attributeValue) > -1;
  188. }; break;
  189. case '!=' : test = function(value){
  190. return attributeValue != value;
  191. }; break;
  192. default : test = function(value){
  193. return !!value;
  194. };
  195. }
  196.  
  197. if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
  198. return false;
  199. };
  200.  
  201. if (!test) test = function(value){
  202. return value && regexp.test(value);
  203. };
  204.  
  205. if (!currentParsed.attributes) currentParsed.attributes = [];
  206. currentParsed.attributes.push({
  207. key: attributeKey,
  208. operator: attributeOperator,
  209. value: attributeValue,
  210. test: test
  211. });
  212.  
  213. }
  214.  
  215. return '';
  216. };
  217.  
  218. // Slick NS
  219.  
  220. var Slick = (this.Slick || {});
  221.  
  222. Slick.parse = function(expression){
  223. return parse(expression);
  224. };
  225.  
  226. Slick.escapeRegExp = escapeRegExp;
  227.  
  228. if (!this.Slick) this.Slick = Slick;
  229.  
  230. }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
  231.  
  232. /*
  233. ---
  234. name: Slick.Finder
  235. description: The new, superfast css selector engine.
  236. provides: Slick.Finder
  237. requires: Slick.Parser
  238. ...
  239. */
  240.  
  241. ;(function(){
  242.  
  243. var local = {},
  244. featuresCache = {},
  245. toString = Object.prototype.toString;
  246.  
  247. // Feature / Bug detection
  248.  
  249. local.isNativeCode = function(fn){
  250. return (/\{\s*\[native code\]\s*\}/).test('' + fn);
  251. };
  252.  
  253. local.isXML = function(document){
  254. return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
  255. (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
  256. };
  257.  
  258. local.setDocument = function(document){
  259.  
  260. // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
  261. var nodeType = document.nodeType;
  262. if (nodeType == 9); // document
  263. else if (nodeType) document = document.ownerDocument; // node
  264. else if (document.navigator) document = document.document; // window
  265. else return;
  266.  
  267. // check if it's the old document
  268.  
  269. if (this.document === document) return;
  270. this.document = document;
  271.  
  272. // check if we have done feature detection on this document before
  273.  
  274. var root = document.documentElement,
  275. rootUid = this.getUIDXML(root),
  276. features = featuresCache[rootUid],
  277. feature;
  278.  
  279. if (features){
  280. for (feature in features){
  281. this[feature] = features[feature];
  282. }
  283. return;
  284. }
  285.  
  286. features = featuresCache[rootUid] = {};
  287.  
  288. features.root = root;
  289. features.isXMLDocument = this.isXML(document);
  290.  
  291. features.brokenStarGEBTN
  292. = features.starSelectsClosedQSA
  293. = features.idGetsName
  294. = features.brokenMixedCaseQSA
  295. = features.brokenGEBCN
  296. = features.brokenCheckedQSA
  297. = features.brokenEmptyAttributeQSA
  298. = features.isHTMLDocument
  299. = features.nativeMatchesSelector
  300. = false;
  301.  
  302. var starSelectsClosed, starSelectsComments,
  303. brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
  304. brokenFormAttributeGetter;
  305.  
  306. var selected, id = 'slick_uniqueid';
  307. var testNode = document.createElement('div');
  308.  
  309. var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
  310. testRoot.appendChild(testNode);
  311.  
  312. // on non-HTML documents innerHTML and getElementsById doesnt work properly
  313. try {
  314. testNode.innerHTML = '<a id="'+id+'"></a>';
  315. features.isHTMLDocument = !!document.getElementById(id);
  316. } catch(e){}
  317.  
  318. if (features.isHTMLDocument){
  319.  
  320. testNode.style.display = 'none';
  321.  
  322. // IE returns comment nodes for getElementsByTagName('*') for some documents
  323. testNode.appendChild(document.createComment(''));
  324. starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
  325.  
  326. // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
  327. // Should never inject incorrect markup on XML documents
  328. if (!features.isXMLDocument){
  329. try {
  330. testNode.innerHTML = 'foo</foo>';
  331. selected = testNode.getElementsByTagName('*');
  332. starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
  333. } catch(e){}
  334. }
  335.  
  336. features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
  337.  
  338. // native matchesSelector function
  339. features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
  340. if (features.nativeMatchesSelector) try {
  341. // if matchesSelector trows errors on incorrect sintaxes we can use it
  342. features.nativeMatchesSelector.call(root, ':slick');
  343. features.nativeMatchesSelector = null;
  344. } catch(e){}
  345.  
  346. // IE returns elements with the name instead of just id for getElementsById for some documents
  347. try {
  348. testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
  349. features.idGetsName = document.getElementById(id) === testNode.firstChild;
  350. } catch(e){}
  351.  
  352. if (testNode.getElementsByClassName){
  353.  
  354. // Safari 3.2 getElementsByClassName caches results
  355. try {
  356. testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
  357. testNode.getElementsByClassName('b').length;
  358. testNode.firstChild.className = 'b';
  359. cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
  360. } catch(e){}
  361.  
  362. // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
  363. try {
  364. testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
  365. brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
  366. } catch(e){}
  367.  
  368. features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
  369. }
  370.  
  371. if (testNode.querySelectorAll){
  372. // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
  373. // Should never inject incorrect markup on XML documents
  374. if (!features.isXMLDocument){
  375. try {
  376. testNode.innerHTML = 'foo</foo>';
  377. selected = testNode.querySelectorAll('*');
  378. features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
  379. } catch(e){}
  380. }
  381.  
  382. // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
  383. try {
  384. testNode.innerHTML = '<a class="MiX"></a>';
  385. features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
  386. } catch(e){}
  387.  
  388. // Webkit and Opera dont return selected options on querySelectorAll
  389. try {
  390. testNode.innerHTML = '<select><option selected="selected">a</option></select>';
  391. features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
  392. } catch(e){}
  393.  
  394. // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
  395. try {
  396. testNode.innerHTML = '<a class=""></a>';
  397. features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
  398. } catch(e){}
  399.  
  400. }
  401.  
  402. if (features.nativeMatchesSelector) {
  403. // Webkit does not match correctly with the :nth-child pseudo on elements inside the body
  404. // but if a style is applyed using this selector, the match starts working
  405. try {
  406. testNode.innerHTML = '_<style>:nth-child(2){}</style>';
  407. } catch(e){}
  408. }
  409.  
  410. // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
  411. try {
  412. testNode.innerHTML = '<form action="s"><input id="action"/></form>';
  413. brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
  414. } catch(e){}
  415.  
  416. }
  417.  
  418. try {
  419. root.slick_expando = 1;
  420. delete root.slick_expando;
  421. features.getUID = this.getUIDHTML;
  422. } catch(e) {
  423. features.getUID = this.getUIDXML;
  424. }
  425.  
  426. testRoot.removeChild(testNode);
  427. testNode = selected = testRoot = null;
  428.  
  429. // getAttribute
  430.  
  431. features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
  432. var method = this.attributeGetters[name];
  433. if (method) return method.call(node);
  434. var attributeNode = node.getAttributeNode(name);
  435. return (attributeNode) ? attributeNode.nodeValue : null;
  436. } : function(node, name){
  437. var method = this.attributeGetters[name];
  438. return (method) ? method.call(node) : node.getAttribute(name);
  439. };
  440.  
  441. // hasAttribute
  442.  
  443. features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
  444. return node.hasAttribute(attribute);
  445. } : function(node, attribute) {
  446. node = node.getAttributeNode(attribute);
  447. return !!(node && (node.specified || node.nodeValue));
  448. };
  449.  
  450. // contains
  451. // FIXME: Add specs: local.contains should be different for xml and html documents?
  452. var nativeRootContains = root && this.isNativeCode(root.contains),
  453. nativeDocumentContains = document && this.isNativeCode(document.contains);
  454.  
  455. features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
  456. return context.contains(node);
  457. } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
  458. // IE8 does not have .contains on document.
  459. return context === node || ((context === document) ? document.documentElement : context).contains(node);
  460. } : (root && root.compareDocumentPosition) ? function(context, node){
  461. return context === node || !!(context.compareDocumentPosition(node) & 16);
  462. } : function(context, node){
  463. if (node) do {
  464. if (node === context) return true;
  465. } while ((node = node.parentNode));
  466. return false;
  467. };
  468.  
  469. // document order sorting
  470. // credits to Sizzle (http://sizzlejs.com/)
  471.  
  472. features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
  473. if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
  474. return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
  475. } : ('sourceIndex' in root) ? function(a, b){
  476. if (!a.sourceIndex || !b.sourceIndex) return 0;
  477. return a.sourceIndex - b.sourceIndex;
  478. } : (document.createRange) ? function(a, b){
  479. if (!a.ownerDocument || !b.ownerDocument) return 0;
  480. var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
  481. aRange.setStart(a, 0);
  482. aRange.setEnd(a, 0);
  483. bRange.setStart(b, 0);
  484. bRange.setEnd(b, 0);
  485. return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
  486. } : null ;
  487.  
  488. root = null;
  489.  
  490. for (feature in features){
  491. this[feature] = features[feature];
  492. }
  493. };
  494.  
  495. // Main Method
  496.  
  497. var reSiblingContextSelector = /^\s*[+~]/,
  498. reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
  499. reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
  500. qsaFailExpCache = {};
  501.  
  502. local.search = function(context, expression, append, first){
  503.  
  504. var found = this.found = (first) ? null : (append || []);
  505.  
  506. if (!context) return found;
  507. else if (context.navigator) context = context.document; // Convert the node from a window to a document
  508. else if (!context.nodeType) return found;
  509.  
  510. // setup
  511.  
  512. var parsed, i,
  513. uniques = this.uniques = {},
  514. hasOthers = !!(append && append.length),
  515. contextIsDocument = (context.nodeType == 9);
  516.  
  517. if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
  518.  
  519. // avoid duplicating items already in the append array
  520. if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
  521.  
  522. // expression checks
  523.  
  524. if (typeof expression == 'string'){ // expression is a string
  525.  
  526. /*<simple-selectors-override>*/
  527. var simpleSelector = expression.match(reSimpleSelector);
  528. simpleSelectors: if (simpleSelector) {
  529.  
  530. var symbol = simpleSelector[1],
  531. name = simpleSelector[2],
  532. node, nodes;
  533.  
  534. if (!symbol){
  535.  
  536. if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
  537. nodes = context.getElementsByTagName(name);
  538. if (first) return nodes[0] || null;
  539. for (i = 0; node = nodes[i++];){
  540. if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
  541. }
  542.  
  543. } else if (symbol == '#'){
  544.  
  545. if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
  546. node = context.getElementById(name);
  547. if (!node) return found;
  548. if (this.idGetsName && this.getAttribute(node, 'id') != name) break simpleSelectors;
  549. if (first) return node || null;
  550. if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
  551.  
  552. } else if (symbol == '.'){
  553.  
  554. if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
  555. if (context.getElementsByClassName && !this.brokenGEBCN){
  556. nodes = context.getElementsByClassName(name);
  557. if (first) return nodes[0] || null;
  558. for (i = 0; node = nodes[i++];){
  559. if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
  560. }
  561. } else {
  562. var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
  563. nodes = context.getElementsByTagName('*');
  564. for (i = 0; node = nodes[i++];){
  565. className = node.className;
  566. if (!(className && matchClass.test(className))) continue;
  567. if (first) return node;
  568. if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
  569. }
  570. }
  571.  
  572. }
  573.  
  574. if (hasOthers) this.sort(found);
  575. return (first) ? null : found;
  576.  
  577. }
  578. /*</simple-selectors-override>*/
  579.  
  580. /*<query-selector-override>*/
  581. querySelector: if (context.querySelectorAll) {
  582.  
  583. if (!this.isHTMLDocument
  584. || qsaFailExpCache[expression]
  585. //TODO: only skip when expression is actually mixed case
  586. || this.brokenMixedCaseQSA
  587. || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
  588. || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
  589. || (!contextIsDocument //Abort when !contextIsDocument and...
  590. // there are multiple expressions in the selector
  591. // since we currently only fix non-document rooted QSA for single expression selectors
  592. && expression.indexOf(',') > -1
  593. )
  594. || Slick.disableQSA
  595. ) break querySelector;
  596.  
  597. var _expression = expression,
  598. _context = context;
  599.  
  600. if (!contextIsDocument){
  601. if (reSiblingContextSelector.test(_expression)) {
  602. context = _context.parentNode;
  603. }
  604. // non-document rooted QSA
  605. // credits to Andrew Dupont
  606. var currentId = _context.getAttribute('id'),
  607. slickid = 'slickid__';
  608. _context.setAttribute('id', slickid);
  609. _expression = '#' + slickid + ' ' + _expression;
  610. }
  611.  
  612. try {
  613. if (first) return context.querySelector(_expression) || null;
  614. else nodes = context.querySelectorAll(_expression);
  615. } catch(e) {
  616. qsaFailExpCache[expression] = 1;
  617. break querySelector;
  618. } finally {
  619. if (!contextIsDocument){
  620. if (currentId) _context.setAttribute('id', currentId);
  621. else _context.removeAttribute('id');
  622. context = _context;
  623. }
  624. }
  625.  
  626. if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
  627. if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
  628. } else for (i = 0; node = nodes[i++];){
  629. if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
  630. }
  631.  
  632. if (hasOthers) this.sort(found);
  633. return found;
  634.  
  635. }
  636. /*</query-selector-override>*/
  637.  
  638. parsed = this.Slick.parse(expression);
  639. if (!parsed.length) return found;
  640. } else if (expression == null){ // there is no expression
  641. return found;
  642. } else if (expression.Slick){ // expression is a parsed Slick object
  643. parsed = expression;
  644. } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
  645. (found) ? found.push(expression) : found = expression;
  646. return found;
  647. } else { // other junk
  648. return found;
  649. }
  650.  
  651. /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
  652.  
  653. // cache elements for the nth selectors
  654.  
  655. this.posNTH = {};
  656. this.posNTHLast = {};
  657. this.posNTHType = {};
  658. this.posNTHTypeLast = {};
  659.  
  660. /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
  661.  
  662. // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
  663. this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
  664.  
  665. if (found == null) found = [];
  666.  
  667. // default engine
  668.  
  669. var j, m, n,
  670. combinator, tag, id, classList, classes, attributes, pseudos,
  671. currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
  672.  
  673. search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
  674.  
  675. combinator = 'combinator:' + currentBit.combinator;
  676. if (!this[combinator]) continue search;
  677.  
  678. tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
  679. id = currentBit.id;
  680. classList = currentBit.classList;
  681. classes = currentBit.classes;
  682. attributes = currentBit.attributes;
  683. pseudos = currentBit.pseudos;
  684. lastBit = (j === (currentExpression.length - 1));
  685.  
  686. this.bitUniques = {};
  687.  
  688. if (lastBit){
  689. this.uniques = uniques;
  690. this.found = found;
  691. } else {
  692. this.uniques = {};
  693. this.found = [];
  694. }
  695.  
  696. if (j === 0){
  697. this[combinator](context, tag, id, classes, attributes, pseudos, classList);
  698. if (first && lastBit && found.length) break search;
  699. } else {
  700. if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
  701. this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
  702. if (found.length) break search;
  703. } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
  704. }
  705.  
  706. currentItems = this.found;
  707. }
  708.  
  709. // should sort if there are nodes in append and if you pass multiple expressions.
  710. if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
  711.  
  712. return (first) ? (found[0] || null) : found;
  713. };
  714.  
  715. // Utils
  716.  
  717. local.uidx = 1;
  718. local.uidk = 'slick-uniqueid';
  719.  
  720. local.getUIDXML = function(node){
  721. var uid = node.getAttribute(this.uidk);
  722. if (!uid){
  723. uid = this.uidx++;
  724. node.setAttribute(this.uidk, uid);
  725. }
  726. return uid;
  727. };
  728.  
  729. local.getUIDHTML = function(node){
  730. return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
  731. };
  732.  
  733. // sort based on the setDocument documentSorter method.
  734.  
  735. local.sort = function(results){
  736. if (!this.documentSorter) return results;
  737. results.sort(this.documentSorter);
  738. return results;
  739. };
  740.  
  741. /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
  742.  
  743. local.cacheNTH = {};
  744.  
  745. local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
  746.  
  747. local.parseNTHArgument = function(argument){
  748. var parsed = argument.match(this.matchNTH);
  749. if (!parsed) return false;
  750. var special = parsed[2] || false;
  751. var a = parsed[1] || 1;
  752. if (a == '-') a = -1;
  753. var b = +parsed[3] || 0;
  754. parsed =
  755. (special == 'n') ? {a: a, b: b} :
  756. (special == 'odd') ? {a: 2, b: 1} :
  757. (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
  758.  
  759. return (this.cacheNTH[argument] = parsed);
  760. };
  761.  
  762. local.createNTHPseudo = function(child, sibling, positions, ofType){
  763. return function(node, argument){
  764. var uid = this.getUID(node);
  765. if (!this[positions][uid]){
  766. var parent = node.parentNode;
  767. if (!parent) return false;
  768. var el = parent[child], count = 1;
  769. if (ofType){
  770. var nodeName = node.nodeName;
  771. do {
  772. if (el.nodeName != nodeName) continue;
  773. this[positions][this.getUID(el)] = count++;
  774. } while ((el = el[sibling]));
  775. } else {
  776. do {
  777. if (el.nodeType != 1) continue;
  778. this[positions][this.getUID(el)] = count++;
  779. } while ((el = el[sibling]));
  780. }
  781. }
  782. argument = argument || 'n';
  783. var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
  784. if (!parsed) return false;
  785. var a = parsed.a, b = parsed.b, pos = this[positions][uid];
  786. if (a == 0) return b == pos;
  787. if (a > 0){
  788. if (pos < b) return false;
  789. } else {
  790. if (b < pos) return false;
  791. }
  792. return ((pos - b) % a) == 0;
  793. };
  794. };
  795.  
  796. /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
  797.  
  798. local.pushArray = function(node, tag, id, classes, attributes, pseudos){
  799. if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
  800. };
  801.  
  802. local.pushUID = function(node, tag, id, classes, attributes, pseudos){
  803. var uid = this.getUID(node);
  804. if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
  805. this.uniques[uid] = true;
  806. this.found.push(node);
  807. }
  808. };
  809.  
  810. local.matchNode = function(node, selector){
  811. if (this.isHTMLDocument && this.nativeMatchesSelector){
  812. try {
  813. return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
  814. } catch(matchError) {}
  815. }
  816.  
  817. var parsed = this.Slick.parse(selector);
  818. if (!parsed) return true;
  819.  
  820. // simple (single) selectors
  821. var expressions = parsed.expressions, simpleExpCounter = 0, i;
  822. for (i = 0; (currentExpression = expressions[i]); i++){
  823. if (currentExpression.length == 1){
  824. var exp = currentExpression[0];
  825. if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
  826. simpleExpCounter++;
  827. }
  828. }
  829.  
  830. if (simpleExpCounter == parsed.length) return false;
  831.  
  832. var nodes = this.search(this.document, parsed), item;
  833. for (i = 0; item = nodes[i++];){
  834. if (item === node) return true;
  835. }
  836. return false;
  837. };
  838.  
  839. local.matchPseudo = function(node, name, argument){
  840. var pseudoName = 'pseudo:' + name;
  841. if (this[pseudoName]) {
  842. // saves a this.found reference so if the pseudo-selector
  843. // uses Slick it wont mess with the current reference
  844. var found = this.found,
  845. result = this[pseudoName](node, argument);
  846. this.found = found;
  847. return result;
  848. }
  849. var attribute = this.getAttribute(node, name);
  850. return (argument) ? argument == attribute : !!attribute;
  851. };
  852.  
  853. local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
  854. if (tag){
  855. var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
  856. if (tag == '*'){
  857. if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
  858. } else {
  859. if (nodeName != tag) return false;
  860. }
  861. }
  862.  
  863. if (id && node.getAttribute('id') != id) return false;
  864.  
  865. var i, part, cls;
  866. if (classes) for (i = classes.length; i--;){
  867. cls = this.getAttribute(node, 'class');
  868. if (!(cls && classes[i].regexp.test(cls))) return false;
  869. }
  870. if (attributes) for (i = attributes.length; i--;){
  871. part = attributes[i];
  872. if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
  873. }
  874. if (pseudos) for (i = pseudos.length; i--;){
  875. part = pseudos[i];
  876. if (!this.matchPseudo(node, part.key, part.value)) return false;
  877. }
  878. return true;
  879. };
  880.  
  881. var combinators = {
  882.  
  883. ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
  884.  
  885. var i, item, children;
  886.  
  887. if (this.isHTMLDocument){
  888. getById: if (id){
  889. item = this.document.getElementById(id);
  890. if ((!item && node.all) || (this.idGetsName && item && this.getAttribute(item, 'id') != id)){
  891. // all[id] returns all the elements with that name or id inside node
  892. // if theres just one it will return the element, else it will be a collection
  893. children = node.all[id];
  894. if (!children) return;
  895. if (!children[0]) children = [children];
  896. for (i = 0; item = children[i++];){
  897. if (this.getAttribute(item, 'id') == id){
  898. this.push(item, tag, null, classes, attributes, pseudos);
  899. break;
  900. }
  901. }
  902. return;
  903. }
  904. if (!item){
  905. // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
  906. if (this.contains(this.root, node)) return;
  907. else break getById;
  908. } else if (this.document !== node && !this.contains(node, item)) return;
  909. this.push(item, tag, null, classes, attributes, pseudos);
  910. return;
  911. }
  912. getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
  913. children = node.getElementsByClassName(classList.join(' '));
  914. if (!(children && children.length)) break getByClass;
  915. for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
  916. return;
  917. }
  918. }
  919. getByTag: {
  920. children = node.getElementsByTagName(tag);
  921. if (!(children && children.length)) break getByTag;
  922. if (!this.brokenStarGEBTN) tag = null;
  923. for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
  924. }
  925. },
  926.  
  927. '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
  928. if ((node = node.firstChild)) do {
  929. if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
  930. } while ((node = node.nextSibling));
  931. },
  932.  
  933. '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
  934. while ((node = node.nextSibling)) if (node.nodeType == 1){
  935. this.push(node, tag, id, classes, attributes, pseudos);
  936. break;
  937. }
  938. },
  939.  
  940. '^': function(node, tag, id, classes, attributes, pseudos){ // first child
  941. node = node.firstChild;
  942. if (node){
  943. if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
  944. else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
  945. }
  946. },
  947.  
  948. '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
  949. while ((node = node.nextSibling)){
  950. if (node.nodeType != 1) continue;
  951. var uid = this.getUID(node);
  952. if (this.bitUniques[uid]) break;
  953. this.bitUniques[uid] = true;
  954. this.push(node, tag, id, classes, attributes, pseudos);
  955. }
  956. },
  957.  
  958. '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
  959. this['combinator:+'](node, tag, id, classes, attributes, pseudos);
  960. this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
  961. },
  962.  
  963. '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
  964. this['combinator:~'](node, tag, id, classes, attributes, pseudos);
  965. this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
  966. },
  967.  
  968. '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
  969. while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
  970. },
  971.  
  972. '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
  973. node = node.parentNode;
  974. if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
  975. },
  976.  
  977. '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
  978. while ((node = node.previousSibling)) if (node.nodeType == 1){
  979. this.push(node, tag, id, classes, attributes, pseudos);
  980. break;
  981. }
  982. },
  983.  
  984. '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
  985. node = node.lastChild;
  986. if (node){
  987. if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
  988. else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
  989. }
  990. },
  991.  
  992. '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
  993. while ((node = node.previousSibling)){
  994. if (node.nodeType != 1) continue;
  995. var uid = this.getUID(node);
  996. if (this.bitUniques[uid]) break;
  997. this.bitUniques[uid] = true;
  998. this.push(node, tag, id, classes, attributes, pseudos);
  999. }
  1000. }
  1001.  
  1002. };
  1003.  
  1004. for (var c in combinators) local['combinator:' + c] = combinators[c];
  1005.  
  1006. var pseudos = {
  1007.  
  1008. /*<pseudo-selectors>*/
  1009.  
  1010. 'empty': function(node){
  1011. var child = node.firstChild;
  1012. return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
  1013. },
  1014.  
  1015. 'not': function(node, expression){
  1016. return !this.matchNode(node, expression);
  1017. },
  1018.  
  1019. 'contains': function(node, text){
  1020. return (node.innerText || node.textContent || '').indexOf(text) > -1;
  1021. },
  1022.  
  1023. 'first-child': function(node){
  1024. while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
  1025. return true;
  1026. },
  1027.  
  1028. 'last-child': function(node){
  1029. while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
  1030. return true;
  1031. },
  1032.  
  1033. 'only-child': function(node){
  1034. var prev = node;
  1035. while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
  1036. var next = node;
  1037. while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
  1038. return true;
  1039. },
  1040.  
  1041. /*<nth-pseudo-selectors>*/
  1042.  
  1043. 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
  1044.  
  1045. 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
  1046.  
  1047. 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
  1048.  
  1049. 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
  1050.  
  1051. 'index': function(node, index){
  1052. return this['pseudo:nth-child'](node, '' + ((+index) + 1));
  1053. },
  1054.  
  1055. 'even': function(node){
  1056. return this['pseudo:nth-child'](node, '2n');
  1057. },
  1058.  
  1059. 'odd': function(node){
  1060. return this['pseudo:nth-child'](node, '2n+1');
  1061. },
  1062.  
  1063. /*</nth-pseudo-selectors>*/
  1064.  
  1065. /*<of-type-pseudo-selectors>*/
  1066.  
  1067. 'first-of-type': function(node){
  1068. var nodeName = node.nodeName;
  1069. while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
  1070. return true;
  1071. },
  1072.  
  1073. 'last-of-type': function(node){
  1074. var nodeName = node.nodeName;
  1075. while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
  1076. return true;
  1077. },
  1078.  
  1079. 'only-of-type': function(node){
  1080. var prev = node, nodeName = node.nodeName;
  1081. while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
  1082. var next = node;
  1083. while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
  1084. return true;
  1085. },
  1086.  
  1087. /*</of-type-pseudo-selectors>*/
  1088.  
  1089. // custom pseudos
  1090.  
  1091. 'enabled': function(node){
  1092. return !node.disabled;
  1093. },
  1094.  
  1095. 'disabled': function(node){
  1096. return node.disabled;
  1097. },
  1098.  
  1099. 'checked': function(node){
  1100. return node.checked || node.selected;
  1101. },
  1102.  
  1103. 'focus': function(node){
  1104. return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
  1105. },
  1106.  
  1107. 'root': function(node){
  1108. return (node === this.root);
  1109. },
  1110.  
  1111. 'selected': function(node){
  1112. return node.selected;
  1113. }
  1114.  
  1115. /*</pseudo-selectors>*/
  1116. };
  1117.  
  1118. for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
  1119.  
  1120. // attributes methods
  1121.  
  1122. var attributeGetters = local.attributeGetters = {
  1123.  
  1124. 'for': function(){
  1125. return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
  1126. },
  1127.  
  1128. 'href': function(){
  1129. return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
  1130. },
  1131.  
  1132. 'style': function(){
  1133. return (this.style) ? this.style.cssText : this.getAttribute('style');
  1134. },
  1135.  
  1136. 'type': function(){
  1137. return this.getAttribute('type');
  1138. },
  1139.  
  1140. 'tabindex': function(){
  1141. var attributeNode = this.getAttributeNode('tabindex');
  1142. return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
  1143. },
  1144.  
  1145. 'maxlength': function(){
  1146. var attributeNode = this.getAttributeNode('maxLength');
  1147. return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
  1148. }
  1149.  
  1150. };
  1151.  
  1152. attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
  1153.  
  1154. // Slick
  1155.  
  1156. var Slick = local.Slick = (this.Slick || {});
  1157.  
  1158. Slick.version = '1.1.7';
  1159.  
  1160. // Slick finder
  1161.  
  1162. Slick.search = function(context, expression, append){
  1163. return local.search(context, expression, append);
  1164. };
  1165.  
  1166. Slick.find = function(context, expression){
  1167. return local.search(context, expression, null, true);
  1168. };
  1169.  
  1170. // Slick containment checker
  1171.  
  1172. Slick.contains = function(container, node){
  1173. local.setDocument(container);
  1174. return local.contains(container, node);
  1175. };
  1176.  
  1177. // Slick attribute getter
  1178.  
  1179. Slick.getAttribute = function(node, name){
  1180. local.setDocument(node);
  1181. return local.getAttribute(node, name);
  1182. };
  1183.  
  1184. Slick.hasAttribute = function(node, name){
  1185. local.setDocument(node);
  1186. return local.hasAttribute(node, name);
  1187. };
  1188.  
  1189. // Slick matcher
  1190.  
  1191. Slick.match = function(node, selector){
  1192. if (!(node && selector)) return false;
  1193. if (!selector || selector === node) return true;
  1194. local.setDocument(node);
  1195. return local.matchNode(node, selector);
  1196. };
  1197.  
  1198. // Slick attribute accessor
  1199.  
  1200. Slick.defineAttributeGetter = function(name, fn){
  1201. local.attributeGetters[name] = fn;
  1202. return this;
  1203. };
  1204.  
  1205. Slick.lookupAttributeGetter = function(name){
  1206. return local.attributeGetters[name];
  1207. };
  1208.  
  1209. // Slick pseudo accessor
  1210.  
  1211. Slick.definePseudo = function(name, fn){
  1212. local['pseudo:' + name] = function(node, argument){
  1213. return fn.call(node, argument);
  1214. };
  1215. return this;
  1216. };
  1217.  
  1218. Slick.lookupPseudo = function(name){
  1219. var pseudo = local['pseudo:' + name];
  1220. if (pseudo) return function(argument){
  1221. return pseudo.call(this, argument);
  1222. };
  1223. return null;
  1224. };
  1225.  
  1226. // Slick overrides accessor
  1227.  
  1228. Slick.override = function(regexp, fn){
  1229. local.override(regexp, fn);
  1230. return this;
  1231. };
  1232.  
  1233. Slick.isXML = local.isXML;
  1234.  
  1235. Slick.uidOf = function(node){
  1236. return local.getUIDHTML(node);
  1237. };
  1238.  
  1239. if (!this.Slick) this.Slick = Slick;
  1240.  
  1241. }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);