[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 YUI.add('yui2-selector', function(Y) { 2 var YAHOO = Y.YUI2; 3 /* 4 Copyright (c) 2011, Yahoo! Inc. All rights reserved. 5 Code licensed under the BSD License: 6 http://developer.yahoo.com/yui/license.html 7 version: 2.9.0 8 */ 9 var Y = YAHOO, 10 Y_DOM = YAHOO.util.Dom, 11 EMPTY_ARRAY = [], 12 Y_UA = Y.env.ua, 13 Y_Lang = Y.lang, 14 Y_DOC = document, 15 Y_DOCUMENT_ELEMENT = Y_DOC.documentElement, 16 17 Y_DOM_inDoc = Y_DOM.inDocument, 18 Y_mix = Y_Lang.augmentObject, 19 Y_guid = Y_DOM.generateId, 20 21 Y_getDoc = function(element) { 22 var doc = Y_DOC; 23 if (element) { 24 doc = (element.nodeType === 9) ? element : // element === document 25 element.ownerDocument || // element === DOM node 26 element.document || // element === window 27 Y_DOC; // default 28 } 29 30 return doc; 31 }, 32 33 Y_Array = function(o, startIdx) { 34 var l, a, start = startIdx || 0; 35 36 // IE errors when trying to slice HTMLElement collections 37 try { 38 return Array.prototype.slice.call(o, start); 39 } catch (e) { 40 a = []; 41 l = o.length; 42 for (; start < l; start++) { 43 a.push(o[start]); 44 } 45 return a; 46 } 47 }, 48 49 Y_DOM_allById = function(id, root) { 50 root = root || Y_DOC; 51 var nodes = [], 52 ret = [], 53 i, 54 node; 55 56 if (root.querySelectorAll) { 57 ret = root.querySelectorAll('[id="' + id + '"]'); 58 } else if (root.all) { 59 nodes = root.all(id); 60 61 if (nodes) { 62 // root.all may return HTMLElement or HTMLCollection. 63 // some elements are also HTMLCollection (FORM, SELECT). 64 if (nodes.nodeName) { 65 if (nodes.id === id) { // avoid false positive on name 66 ret.push(nodes); 67 nodes = EMPTY_ARRAY; // done, no need to filter 68 } else { // prep for filtering 69 nodes = [nodes]; 70 } 71 } 72 73 if (nodes.length) { 74 // filter out matches on node.name 75 // and element.id as reference to element with id === 'id' 76 for (i = 0; node = nodes[i++];) { 77 if (node.id === id || 78 (node.attributes && node.attributes.id && 79 node.attributes.id.value === id)) { 80 ret.push(node); 81 } 82 } 83 } 84 } 85 } else { 86 ret = [Y_getDoc(root).getElementById(id)]; 87 } 88 89 return ret; 90 }; 91 92 /** 93 * The selector-native module provides support for native querySelector 94 * @module dom 95 * @submodule selector-native 96 * @for Selector 97 */ 98 99 /** 100 * Provides support for using CSS selectors to query the DOM 101 * @class Selector 102 * @static 103 * @for Selector 104 */ 105 106 var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', 107 OWNER_DOCUMENT = 'ownerDocument', 108 109 Selector = { 110 _foundCache: [], 111 112 useNative: true, 113 114 _compare: ('sourceIndex' in Y_DOCUMENT_ELEMENT) ? 115 function(nodeA, nodeB) { 116 var a = nodeA.sourceIndex, 117 b = nodeB.sourceIndex; 118 119 if (a === b) { 120 return 0; 121 } else if (a > b) { 122 return 1; 123 } 124 125 return -1; 126 127 } : (Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION] ? 128 function(nodeA, nodeB) { 129 if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) { 130 return -1; 131 } else { 132 return 1; 133 } 134 } : 135 function(nodeA, nodeB) { 136 var rangeA, rangeB, compare; 137 if (nodeA && nodeB) { 138 rangeA = nodeA[OWNER_DOCUMENT].createRange(); 139 rangeA.setStart(nodeA, 0); 140 rangeB = nodeB[OWNER_DOCUMENT].createRange(); 141 rangeB.setStart(nodeB, 0); 142 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END 143 } 144 145 return compare; 146 147 }), 148 149 _sort: function(nodes) { 150 if (nodes) { 151 nodes = Y_Array(nodes, 0, true); 152 if (nodes.sort) { 153 nodes.sort(Selector._compare); 154 } 155 } 156 157 return nodes; 158 }, 159 160 _deDupe: function(nodes) { 161 var ret = [], 162 i, node; 163 164 for (i = 0; (node = nodes[i++]);) { 165 if (!node._found) { 166 ret[ret.length] = node; 167 node._found = true; 168 } 169 } 170 171 for (i = 0; (node = ret[i++]);) { 172 node._found = null; 173 node.removeAttribute('_found'); 174 } 175 176 return ret; 177 }, 178 179 /** 180 * Retrieves a set of nodes based on a given CSS selector. 181 * @method query 182 * 183 * @param {string} selector The CSS Selector to test the node against. 184 * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc 185 * @param {Boolean} firstOnly optional Whether or not to return only the first match. 186 * @return {Array} An array of nodes that match the given selector. 187 * @static 188 */ 189 query: function(selector, root, firstOnly, skipNative) { 190 if (root && typeof root == 'string') { 191 root = Y_DOM.get(root); 192 if (!root) { 193 return (firstOnly) ? null : []; 194 } 195 } else { 196 root = root || Y_DOC; 197 } 198 199 var ret = [], 200 useNative = (Selector.useNative && Y_DOC.querySelector && !skipNative), 201 queries = [[selector, root]], 202 query, 203 result, 204 i, 205 fn = (useNative) ? Selector._nativeQuery : Selector._bruteQuery; 206 207 if (selector && fn) { 208 // split group into seperate queries 209 if (!skipNative && // already done if skipping 210 (!useNative || root.tagName)) { // split native when element scoping is needed 211 queries = Selector._splitQueries(selector, root); 212 } 213 214 for (i = 0; (query = queries[i++]);) { 215 result = fn(query[0], query[1], firstOnly); 216 if (!firstOnly) { // coerce DOM Collection to Array 217 result = Y_Array(result, 0, true); 218 } 219 if (result) { 220 ret = ret.concat(result); 221 } 222 } 223 224 if (queries.length > 1) { // remove dupes and sort by doc order 225 ret = Selector._sort(Selector._deDupe(ret)); 226 } 227 } 228 229 return (firstOnly) ? (ret[0] || null) : ret; 230 231 }, 232 233 // allows element scoped queries to begin with combinator 234 // e.g. query('> p', document.body) === query('body > p') 235 _splitQueries: function(selector, node) { 236 var groups = selector.split(','), 237 queries = [], 238 prefix = '', 239 i, len; 240 241 if (node) { 242 // enforce for element scoping 243 if (node.tagName) { 244 node.id = node.id || Y_guid(); 245 prefix = '[id="' + node.id + '"] '; 246 } 247 248 for (i = 0, len = groups.length; i < len; ++i) { 249 selector = prefix + groups[i]; 250 queries.push([selector, node]); 251 } 252 } 253 254 return queries; 255 }, 256 257 _nativeQuery: function(selector, root, one) { 258 if (Y_UA.webkit && selector.indexOf(':checked') > -1 && 259 (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected" 260 return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query 261 } 262 try { 263 return root['querySelector' + (one ? '' : 'All')](selector); 264 } catch(e) { // fallback to brute if available 265 return Selector.query(selector, root, one, true); // redo with skipNative true 266 } 267 }, 268 269 filter: function(nodes, selector) { 270 var ret = [], 271 i, node; 272 273 if (nodes && selector) { 274 for (i = 0; (node = nodes[i++]);) { 275 if (Selector.test(node, selector)) { 276 ret[ret.length] = node; 277 } 278 } 279 } else { 280 } 281 282 return ret; 283 }, 284 285 test: function(node, selector, root) { 286 var ret = false, 287 groups = selector.split(','), 288 useFrag = false, 289 parent, 290 item, 291 items, 292 frag, 293 i, j, group; 294 295 if (node && node.tagName) { // only test HTMLElements 296 297 // we need a root if off-doc 298 if (!root && !Y_DOM_inDoc(node)) { 299 parent = node.parentNode; 300 if (parent) { 301 root = parent; 302 } else { // only use frag when no parent to query 303 frag = node[OWNER_DOCUMENT].createDocumentFragment(); 304 frag.appendChild(node); 305 root = frag; 306 useFrag = true; 307 } 308 } 309 root = root || node[OWNER_DOCUMENT]; 310 311 if (!node.id) { 312 node.id = Y_guid(); 313 } 314 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test 315 group += '[id="' + node.id + '"]'; 316 items = Selector.query(group, root); 317 318 for (j = 0; item = items[j++];) { 319 if (item === node) { 320 ret = true; 321 break; 322 } 323 } 324 if (ret) { 325 break; 326 } 327 } 328 329 if (useFrag) { // cleanup 330 frag.removeChild(node); 331 } 332 } 333 334 return ret; 335 } 336 337 }; 338 339 YAHOO.util.Selector = Selector; 340 /** 341 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. 342 * @module dom 343 * @submodule selector-css2 344 * @for Selector 345 */ 346 347 /** 348 * Provides helper methods for collecting and filtering DOM elements. 349 */ 350 351 var PARENT_NODE = 'parentNode', 352 TAG_NAME = 'tagName', 353 ATTRIBUTES = 'attributes', 354 COMBINATOR = 'combinator', 355 PSEUDOS = 'pseudos', 356 357 SelectorCSS2 = { 358 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move? 359 SORT_RESULTS: true, 360 _children: function(node, tag) { 361 var ret = node.children, 362 i, 363 children = [], 364 childNodes, 365 child; 366 367 if (node.children && tag && node.children.tags) { 368 children = node.children.tags(tag); 369 } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children 370 childNodes = ret || node.childNodes; 371 ret = []; 372 for (i = 0; (child = childNodes[i++]);) { 373 if (child.tagName) { 374 if (!tag || tag === child.tagName) { 375 ret.push(child); 376 } 377 } 378 } 379 } 380 381 return ret || []; 382 }, 383 384 _re: { 385 //attr: /(\[.*\])/g, 386 attr: /(\[[^\]]*\])/g, 387 //esc: /\\[:\[][\w\d\]]*/gi, 388 esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi, 389 //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i 390 pseudos: /(\([^\)]*\))/g 391 }, 392 393 /** 394 * Mapping of shorthand tokens to corresponding attribute selector 395 * @property shorthand 396 * @type object 397 */ 398 shorthand: { 399 //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]', 400 '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]', 401 //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]', 402 //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]' 403 '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]' 404 }, 405 406 /** 407 * List of operators and corresponding boolean functions. 408 * These functions are passed the attribute and the current node's value of the attribute. 409 * @property operators 410 * @type object 411 */ 412 operators: { 413 '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute 414 //'': '.+', 415 //'=': '^{val}$', // equality 416 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited 417 '|=': '^{val}(?:-|$)' // optional hyphen-delimited 418 }, 419 420 pseudos: { 421 'first-child': function(node) { 422 return Selector._children(node[PARENT_NODE])[0] === node; 423 } 424 }, 425 426 _bruteQuery: function(selector, root, firstOnly) { 427 var ret = [], 428 nodes = [], 429 tokens = Selector._tokenize(selector), 430 token = tokens[tokens.length - 1], 431 rootDoc = Y_getDoc(root), 432 child, 433 id, 434 className, 435 tagName; 436 437 438 // if we have an initial ID, set to root when in document 439 /* 440 if (tokens[0] && rootDoc === root && 441 (id = tokens[0].id) && 442 rootDoc.getElementById(id)) { 443 root = rootDoc.getElementById(id); 444 } 445 */ 446 447 if (token) { 448 // prefilter nodes 449 id = token.id; 450 className = token.className; 451 tagName = token.tagName || '*'; 452 453 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags 454 // try ID first, unless no root.all && root not in document 455 // (root.all works off document, but not getElementById) 456 // TODO: move to allById? 457 if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) { 458 nodes = Y_DOM_allById(id, root); 459 // try className 460 } else if (className) { 461 nodes = root.getElementsByClassName(className); 462 } else { // default to tagName 463 nodes = root.getElementsByTagName(tagName); 464 } 465 466 } else { // brute getElementsByTagName('*') 467 child = root.firstChild; 468 while (child) { 469 if (child.tagName) { // only collect HTMLElements 470 nodes.push(child); 471 } 472 child = child.nextSilbing || child.firstChild; 473 } 474 } 475 if (nodes.length) { 476 ret = Selector._filterNodes(nodes, tokens, firstOnly); 477 } 478 } 479 480 return ret; 481 }, 482 483 _filterNodes: function(nodes, tokens, firstOnly) { 484 var i = 0, 485 j, 486 len = tokens.length, 487 n = len - 1, 488 result = [], 489 node = nodes[0], 490 tmpNode = node, 491 getters = Selector.getters, 492 operator, 493 combinator, 494 token, 495 path, 496 pass, 497 //FUNCTION = 'function', 498 value, 499 tests, 500 test; 501 502 //do { 503 for (i = 0; (tmpNode = node = nodes[i++]);) { 504 n = len - 1; 505 path = null; 506 507 testLoop: 508 while (tmpNode && tmpNode.tagName) { 509 token = tokens[n]; 510 tests = token.tests; 511 j = tests.length; 512 if (j && !pass) { 513 while ((test = tests[--j])) { 514 operator = test[1]; 515 if (getters[test[0]]) { 516 value = getters[test[0]](tmpNode, test[0]); 517 } else { 518 value = tmpNode[test[0]]; 519 // use getAttribute for non-standard attributes 520 if (value === undefined && tmpNode.getAttribute) { 521 value = tmpNode.getAttribute(test[0]); 522 } 523 } 524 525 if ((operator === '=' && value !== test[2]) || // fast path for equality 526 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo) 527 operator.test && !operator.test(value)) || // regex test 528 (!operator.test && // protect against RegExp as function (webkit) 529 typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test 530 531 // skip non element nodes or non-matching tags 532 if ((tmpNode = tmpNode[path])) { 533 while (tmpNode && 534 (!tmpNode.tagName || 535 (token.tagName && token.tagName !== tmpNode.tagName)) 536 ) { 537 tmpNode = tmpNode[path]; 538 } 539 } 540 continue testLoop; 541 } 542 } 543 } 544 545 n--; // move to next token 546 // now that we've passed the test, move up the tree by combinator 547 if (!pass && (combinator = token.combinator)) { 548 path = combinator.axis; 549 tmpNode = tmpNode[path]; 550 551 // skip non element nodes 552 while (tmpNode && !tmpNode.tagName) { 553 tmpNode = tmpNode[path]; 554 } 555 556 if (combinator.direct) { // one pass only 557 path = null; 558 } 559 560 } else { // success if we made it this far 561 result.push(node); 562 if (firstOnly) { 563 return result; 564 } 565 break; 566 } 567 } 568 }// while (tmpNode = node = nodes[++i]); 569 node = tmpNode = null; 570 return result; 571 }, 572 573 combinators: { 574 ' ': { 575 axis: 'parentNode' 576 }, 577 578 '>': { 579 axis: 'parentNode', 580 direct: true 581 }, 582 583 584 '+': { 585 axis: 'previousSibling', 586 direct: true 587 } 588 }, 589 590 _parsers: [ 591 { 592 name: ATTRIBUTES, 593 //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i, 594 re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i, 595 fn: function(match, token) { 596 var operator = match[2] || '', 597 operators = Selector.operators, 598 escVal = (match[3]) ? match[3].replace(/\\/g, '') : '', 599 test; 600 601 // add prefiltering for ID and CLASS 602 if ((match[1] === 'id' && operator === '=') || 603 (match[1] === 'className' && 604 Y_DOCUMENT_ELEMENT.getElementsByClassName && 605 (operator === '~=' || operator === '='))) { 606 token.prefilter = match[1]; 607 608 609 match[3] = escVal; 610 611 // escape all but ID for prefilter, which may run through QSA (via Dom.allById) 612 token[match[1]] = (match[1] === 'id') ? match[3] : escVal; 613 614 } 615 616 // add tests 617 if (operator in operators) { 618 test = operators[operator]; 619 if (typeof test === 'string') { 620 match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1'); 621 test = new RegExp(test.replace('{val}', match[3])); 622 } 623 match[2] = test; 624 } 625 if (!token.last || token.prefilter !== match[1]) { 626 return match.slice(1); 627 } 628 } 629 630 }, 631 { 632 name: TAG_NAME, 633 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, 634 fn: function(match, token) { 635 var tag = match[1].toUpperCase(); 636 token.tagName = tag; 637 638 if (tag !== '*' && (!token.last || token.prefilter)) { 639 return [TAG_NAME, '=', tag]; 640 } 641 if (!token.prefilter) { 642 token.prefilter = 'tagName'; 643 } 644 } 645 }, 646 { 647 name: COMBINATOR, 648 re: /^\s*([>+~]|\s)\s*/, 649 fn: function(match, token) { 650 } 651 }, 652 { 653 name: PSEUDOS, 654 re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i, 655 fn: function(match, token) { 656 var test = Selector[PSEUDOS][match[1]]; 657 if (test) { // reorder match array and unescape special chars for tests 658 if (match[2]) { 659 match[2] = match[2].replace(/\\/g, ''); 660 } 661 return [match[2], test]; 662 } else { // selector token not supported (possibly missing CSS3 module) 663 return false; 664 } 665 } 666 } 667 ], 668 669 _getToken: function(token) { 670 return { 671 tagName: null, 672 id: null, 673 className: null, 674 attributes: {}, 675 combinator: null, 676 tests: [] 677 }; 678 }, 679 680 /** 681 Break selector into token units per simple selector. 682 Combinator is attached to the previous token. 683 */ 684 _tokenize: function(selector) { 685 selector = selector || ''; 686 selector = Selector._replaceShorthand(Y_Lang.trim(selector)); 687 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) 688 query = selector, // original query for debug report 689 tokens = [], // array of tokens 690 found = false, // whether or not any matches were found this pass 691 match, // the regex match 692 test, 693 i, parser; 694 695 /* 696 Search for selector patterns, store, and strip them from the selector string 697 until no patterns match (invalid selector) or we run out of chars. 698 699 Multiple attributes and pseudos are allowed, in any order. 700 for example: 701 'form:first-child[type=button]:not(button)[lang|=en]' 702 */ 703 704 outer: 705 do { 706 found = false; // reset after full pass 707 708 for (i = 0; (parser = Selector._parsers[i++]);) { 709 if ( (match = parser.re.exec(selector)) ) { // note assignment 710 if (parser.name !== COMBINATOR ) { 711 token.selector = selector; 712 } 713 selector = selector.replace(match[0], ''); // strip current match from selector 714 if (!selector.length) { 715 token.last = true; 716 } 717 718 if (Selector._attrFilters[match[1]]) { // convert class to className, etc. 719 match[1] = Selector._attrFilters[match[1]]; 720 } 721 722 test = parser.fn(match, token); 723 if (test === false) { // selector not supported 724 found = false; 725 break outer; 726 } else if (test) { 727 token.tests.push(test); 728 } 729 730 if (!selector.length || parser.name === COMBINATOR) { 731 tokens.push(token); 732 token = Selector._getToken(token); 733 if (parser.name === COMBINATOR) { 734 token.combinator = Selector.combinators[match[1]]; 735 } 736 } 737 found = true; 738 739 740 } 741 } 742 } while (found && selector.length); 743 744 if (!found || selector.length) { // not fully parsed 745 tokens = []; 746 } 747 return tokens; 748 }, 749 750 _replaceShorthand: function(selector) { 751 var shorthand = Selector.shorthand, 752 esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc. 753 attrs, 754 pseudos, 755 re, i, len; 756 757 if (esc) { 758 selector = selector.replace(Selector._re.esc, '\uE000'); 759 } 760 761 attrs = selector.match(Selector._re.attr); 762 pseudos = selector.match(Selector._re.pseudos); 763 764 if (attrs) { 765 selector = selector.replace(Selector._re.attr, '\uE001'); 766 } 767 768 if (pseudos) { 769 selector = selector.replace(Selector._re.pseudos, '\uE002'); 770 } 771 772 773 for (re in shorthand) { 774 if (shorthand.hasOwnProperty(re)) { 775 selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]); 776 } 777 } 778 779 if (attrs) { 780 for (i = 0, len = attrs.length; i < len; ++i) { 781 selector = selector.replace(/\uE001/, attrs[i]); 782 } 783 } 784 785 if (pseudos) { 786 for (i = 0, len = pseudos.length; i < len; ++i) { 787 selector = selector.replace(/\uE002/, pseudos[i]); 788 } 789 } 790 791 selector = selector.replace(/\[/g, '\uE003'); 792 selector = selector.replace(/\]/g, '\uE004'); 793 794 selector = selector.replace(/\(/g, '\uE005'); 795 selector = selector.replace(/\)/g, '\uE006'); 796 797 if (esc) { 798 for (i = 0, len = esc.length; i < len; ++i) { 799 selector = selector.replace('\uE000', esc[i]); 800 } 801 } 802 803 return selector; 804 }, 805 806 _attrFilters: { 807 'class': 'className', 808 'for': 'htmlFor' 809 }, 810 811 getters: { 812 href: function(node, attr) { 813 return Y_DOM.getAttribute(node, attr); 814 } 815 } 816 }; 817 818 Y_mix(Selector, SelectorCSS2, true); 819 Selector.getters.src = Selector.getters.rel = Selector.getters.href; 820 821 // IE wants class with native queries 822 if (Selector.useNative && Y_DOC.querySelector) { 823 Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]'; 824 } 825 826 /** 827 * The selector css3 module provides support for css3 selectors. 828 * @module dom 829 * @submodule selector-css3 830 * @for Selector 831 */ 832 833 /* 834 an+b = get every _a_th node starting at the _b_th 835 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element 836 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n") 837 an+0 = get every _a_th element, "0" may be omitted 838 */ 839 840 Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/; 841 842 Selector._getNth = function(node, expr, tag, reverse) { 843 Selector._reNth.test(expr); 844 var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_) 845 n = RegExp.$2, // "n" 846 oddeven = RegExp.$3, // "odd" or "even" 847 b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_ 848 result = [], 849 siblings = Selector._children(node.parentNode, tag), 850 op; 851 852 if (oddeven) { 853 a = 2; // always every other 854 op = '+'; 855 n = 'n'; 856 b = (oddeven === 'odd') ? 1 : 0; 857 } else if ( isNaN(a) ) { 858 a = (n) ? 1 : 0; // start from the first or no repeat 859 } 860 861 if (a === 0) { // just the first 862 if (reverse) { 863 b = siblings.length - b + 1; 864 } 865 866 if (siblings[b - 1] === node) { 867 return true; 868 } else { 869 return false; 870 } 871 872 } else if (a < 0) { 873 reverse = !!reverse; 874 a = Math.abs(a); 875 } 876 877 if (!reverse) { 878 for (var i = b - 1, len = siblings.length; i < len; i += a) { 879 if ( i >= 0 && siblings[i] === node ) { 880 return true; 881 } 882 } 883 } else { 884 for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) { 885 if ( i < len && siblings[i] === node ) { 886 return true; 887 } 888 } 889 } 890 return false; 891 }; 892 893 Y_mix(Selector.pseudos, { 894 'root': function(node) { 895 return node === node.ownerDocument.documentElement; 896 }, 897 898 'nth-child': function(node, expr) { 899 return Selector._getNth(node, expr); 900 }, 901 902 'nth-last-child': function(node, expr) { 903 return Selector._getNth(node, expr, null, true); 904 }, 905 906 'nth-of-type': function(node, expr) { 907 return Selector._getNth(node, expr, node.tagName); 908 }, 909 910 'nth-last-of-type': function(node, expr) { 911 return Selector._getNth(node, expr, node.tagName, true); 912 }, 913 914 'last-child': function(node) { 915 var children = Selector._children(node.parentNode); 916 return children[children.length - 1] === node; 917 }, 918 919 'first-of-type': function(node) { 920 return Selector._children(node.parentNode, node.tagName)[0] === node; 921 }, 922 923 'last-of-type': function(node) { 924 var children = Selector._children(node.parentNode, node.tagName); 925 return children[children.length - 1] === node; 926 }, 927 928 'only-child': function(node) { 929 var children = Selector._children(node.parentNode); 930 return children.length === 1 && children[0] === node; 931 }, 932 933 'only-of-type': function(node) { 934 var children = Selector._children(node.parentNode, node.tagName); 935 return children.length === 1 && children[0] === node; 936 }, 937 938 'empty': function(node) { 939 return node.childNodes.length === 0; 940 }, 941 942 'not': function(node, expr) { 943 return !Selector.test(node, expr); 944 }, 945 946 'contains': function(node, expr) { 947 var text = node.innerText || node.textContent || ''; 948 return text.indexOf(expr) > -1; 949 }, 950 951 'checked': function(node) { 952 return (node.checked === true || node.selected === true); 953 }, 954 955 enabled: function(node) { 956 return (node.disabled !== undefined && !node.disabled); 957 }, 958 959 disabled: function(node) { 960 return (node.disabled); 961 } 962 }); 963 964 Y_mix(Selector.operators, { 965 '^=': '^{val}', // Match starts with value 966 '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value 967 '$=': '{val}$', // Match ends with value 968 '*=': '{val}' // Match contains value as substring 969 }); 970 971 Selector.combinators['~'] = { 972 axis: 'previousSibling' 973 }; 974 YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"}); 975 976 }, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |