[ 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 YAHOO.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector'); 230 return (firstOnly) ? (ret[0] || null) : ret; 231 232 }, 233 234 // allows element scoped queries to begin with combinator 235 // e.g. query('> p', document.body) === query('body > p') 236 _splitQueries: function(selector, node) { 237 var groups = selector.split(','), 238 queries = [], 239 prefix = '', 240 i, len; 241 242 if (node) { 243 // enforce for element scoping 244 if (node.tagName) { 245 node.id = node.id || Y_guid(); 246 prefix = '[id="' + node.id + '"] '; 247 } 248 249 for (i = 0, len = groups.length; i < len; ++i) { 250 selector = prefix + groups[i]; 251 queries.push([selector, node]); 252 } 253 } 254 255 return queries; 256 }, 257 258 _nativeQuery: function(selector, root, one) { 259 if (Y_UA.webkit && selector.indexOf(':checked') > -1 && 260 (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected" 261 return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query 262 } 263 try { 264 //YAHOO.log('trying native query with: ' + selector, 'info', 'selector-native'); 265 return root['querySelector' + (one ? '' : 'All')](selector); 266 } catch(e) { // fallback to brute if available 267 //YAHOO.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native'); 268 return Selector.query(selector, root, one, true); // redo with skipNative true 269 } 270 }, 271 272 filter: function(nodes, selector) { 273 var ret = [], 274 i, node; 275 276 if (nodes && selector) { 277 for (i = 0; (node = nodes[i++]);) { 278 if (Selector.test(node, selector)) { 279 ret[ret.length] = node; 280 } 281 } 282 } else { 283 YAHOO.log('invalid filter input (nodes: ' + nodes + 284 ', selector: ' + selector + ')', 'warn', 'Selector'); 285 } 286 287 return ret; 288 }, 289 290 test: function(node, selector, root) { 291 var ret = false, 292 groups = selector.split(','), 293 useFrag = false, 294 parent, 295 item, 296 items, 297 frag, 298 i, j, group; 299 300 if (node && node.tagName) { // only test HTMLElements 301 302 // we need a root if off-doc 303 if (!root && !Y_DOM_inDoc(node)) { 304 parent = node.parentNode; 305 if (parent) { 306 root = parent; 307 } else { // only use frag when no parent to query 308 frag = node[OWNER_DOCUMENT].createDocumentFragment(); 309 frag.appendChild(node); 310 root = frag; 311 useFrag = true; 312 } 313 } 314 root = root || node[OWNER_DOCUMENT]; 315 316 if (!node.id) { 317 node.id = Y_guid(); 318 } 319 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test 320 group += '[id="' + node.id + '"]'; 321 items = Selector.query(group, root); 322 323 for (j = 0; item = items[j++];) { 324 if (item === node) { 325 ret = true; 326 break; 327 } 328 } 329 if (ret) { 330 break; 331 } 332 } 333 334 if (useFrag) { // cleanup 335 frag.removeChild(node); 336 } 337 } 338 339 return ret; 340 } 341 342 }; 343 344 YAHOO.util.Selector = Selector; 345 /** 346 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. 347 * @module dom 348 * @submodule selector-css2 349 * @for Selector 350 */ 351 352 /** 353 * Provides helper methods for collecting and filtering DOM elements. 354 */ 355 356 var PARENT_NODE = 'parentNode', 357 TAG_NAME = 'tagName', 358 ATTRIBUTES = 'attributes', 359 COMBINATOR = 'combinator', 360 PSEUDOS = 'pseudos', 361 362 SelectorCSS2 = { 363 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move? 364 SORT_RESULTS: true, 365 _children: function(node, tag) { 366 var ret = node.children, 367 i, 368 children = [], 369 childNodes, 370 child; 371 372 if (node.children && tag && node.children.tags) { 373 children = node.children.tags(tag); 374 } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children 375 childNodes = ret || node.childNodes; 376 ret = []; 377 for (i = 0; (child = childNodes[i++]);) { 378 if (child.tagName) { 379 if (!tag || tag === child.tagName) { 380 ret.push(child); 381 } 382 } 383 } 384 } 385 386 return ret || []; 387 }, 388 389 _re: { 390 //attr: /(\[.*\])/g, 391 attr: /(\[[^\]]*\])/g, 392 //esc: /\\[:\[][\w\d\]]*/gi, 393 esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi, 394 //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i 395 pseudos: /(\([^\)]*\))/g 396 }, 397 398 /** 399 * Mapping of shorthand tokens to corresponding attribute selector 400 * @property shorthand 401 * @type object 402 */ 403 shorthand: { 404 //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]', 405 '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]', 406 //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]', 407 //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]' 408 '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]' 409 }, 410 411 /** 412 * List of operators and corresponding boolean functions. 413 * These functions are passed the attribute and the current node's value of the attribute. 414 * @property operators 415 * @type object 416 */ 417 operators: { 418 '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute 419 //'': '.+', 420 //'=': '^{val}$', // equality 421 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited 422 '|=': '^{val}(?:-|$)' // optional hyphen-delimited 423 }, 424 425 pseudos: { 426 'first-child': function(node) { 427 return Selector._children(node[PARENT_NODE])[0] === node; 428 } 429 }, 430 431 _bruteQuery: function(selector, root, firstOnly) { 432 var ret = [], 433 nodes = [], 434 tokens = Selector._tokenize(selector), 435 token = tokens[tokens.length - 1], 436 rootDoc = Y_getDoc(root), 437 child, 438 id, 439 className, 440 tagName; 441 442 443 // if we have an initial ID, set to root when in document 444 /* 445 if (tokens[0] && rootDoc === root && 446 (id = tokens[0].id) && 447 rootDoc.getElementById(id)) { 448 root = rootDoc.getElementById(id); 449 } 450 */ 451 452 if (token) { 453 // prefilter nodes 454 id = token.id; 455 className = token.className; 456 tagName = token.tagName || '*'; 457 458 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags 459 // try ID first, unless no root.all && root not in document 460 // (root.all works off document, but not getElementById) 461 // TODO: move to allById? 462 if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) { 463 nodes = Y_DOM_allById(id, root); 464 // try className 465 } else if (className) { 466 nodes = root.getElementsByClassName(className); 467 } else { // default to tagName 468 nodes = root.getElementsByTagName(tagName); 469 } 470 471 } else { // brute getElementsByTagName('*') 472 child = root.firstChild; 473 while (child) { 474 if (child.tagName) { // only collect HTMLElements 475 nodes.push(child); 476 } 477 child = child.nextSilbing || child.firstChild; 478 } 479 } 480 if (nodes.length) { 481 ret = Selector._filterNodes(nodes, tokens, firstOnly); 482 } 483 } 484 485 return ret; 486 }, 487 488 _filterNodes: function(nodes, tokens, firstOnly) { 489 var i = 0, 490 j, 491 len = tokens.length, 492 n = len - 1, 493 result = [], 494 node = nodes[0], 495 tmpNode = node, 496 getters = Selector.getters, 497 operator, 498 combinator, 499 token, 500 path, 501 pass, 502 //FUNCTION = 'function', 503 value, 504 tests, 505 test; 506 507 //do { 508 for (i = 0; (tmpNode = node = nodes[i++]);) { 509 n = len - 1; 510 path = null; 511 512 testLoop: 513 while (tmpNode && tmpNode.tagName) { 514 token = tokens[n]; 515 tests = token.tests; 516 j = tests.length; 517 if (j && !pass) { 518 while ((test = tests[--j])) { 519 operator = test[1]; 520 if (getters[test[0]]) { 521 value = getters[test[0]](tmpNode, test[0]); 522 } else { 523 value = tmpNode[test[0]]; 524 // use getAttribute for non-standard attributes 525 if (value === undefined && tmpNode.getAttribute) { 526 value = tmpNode.getAttribute(test[0]); 527 } 528 } 529 530 if ((operator === '=' && value !== test[2]) || // fast path for equality 531 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo) 532 operator.test && !operator.test(value)) || // regex test 533 (!operator.test && // protect against RegExp as function (webkit) 534 typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test 535 536 // skip non element nodes or non-matching tags 537 if ((tmpNode = tmpNode[path])) { 538 while (tmpNode && 539 (!tmpNode.tagName || 540 (token.tagName && token.tagName !== tmpNode.tagName)) 541 ) { 542 tmpNode = tmpNode[path]; 543 } 544 } 545 continue testLoop; 546 } 547 } 548 } 549 550 n--; // move to next token 551 // now that we've passed the test, move up the tree by combinator 552 if (!pass && (combinator = token.combinator)) { 553 path = combinator.axis; 554 tmpNode = tmpNode[path]; 555 556 // skip non element nodes 557 while (tmpNode && !tmpNode.tagName) { 558 tmpNode = tmpNode[path]; 559 } 560 561 if (combinator.direct) { // one pass only 562 path = null; 563 } 564 565 } else { // success if we made it this far 566 result.push(node); 567 if (firstOnly) { 568 return result; 569 } 570 break; 571 } 572 } 573 }// while (tmpNode = node = nodes[++i]); 574 node = tmpNode = null; 575 return result; 576 }, 577 578 combinators: { 579 ' ': { 580 axis: 'parentNode' 581 }, 582 583 '>': { 584 axis: 'parentNode', 585 direct: true 586 }, 587 588 589 '+': { 590 axis: 'previousSibling', 591 direct: true 592 } 593 }, 594 595 _parsers: [ 596 { 597 name: ATTRIBUTES, 598 //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i, 599 re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i, 600 fn: function(match, token) { 601 var operator = match[2] || '', 602 operators = Selector.operators, 603 escVal = (match[3]) ? match[3].replace(/\\/g, '') : '', 604 test; 605 606 // add prefiltering for ID and CLASS 607 if ((match[1] === 'id' && operator === '=') || 608 (match[1] === 'className' && 609 Y_DOCUMENT_ELEMENT.getElementsByClassName && 610 (operator === '~=' || operator === '='))) { 611 token.prefilter = match[1]; 612 613 614 match[3] = escVal; 615 616 // escape all but ID for prefilter, which may run through QSA (via Dom.allById) 617 token[match[1]] = (match[1] === 'id') ? match[3] : escVal; 618 619 } 620 621 // add tests 622 if (operator in operators) { 623 test = operators[operator]; 624 if (typeof test === 'string') { 625 match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1'); 626 test = new RegExp(test.replace('{val}', match[3])); 627 } 628 match[2] = test; 629 } 630 if (!token.last || token.prefilter !== match[1]) { 631 return match.slice(1); 632 } 633 } 634 635 }, 636 { 637 name: TAG_NAME, 638 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, 639 fn: function(match, token) { 640 var tag = match[1].toUpperCase(); 641 token.tagName = tag; 642 643 if (tag !== '*' && (!token.last || token.prefilter)) { 644 return [TAG_NAME, '=', tag]; 645 } 646 if (!token.prefilter) { 647 token.prefilter = 'tagName'; 648 } 649 } 650 }, 651 { 652 name: COMBINATOR, 653 re: /^\s*([>+~]|\s)\s*/, 654 fn: function(match, token) { 655 } 656 }, 657 { 658 name: PSEUDOS, 659 re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i, 660 fn: function(match, token) { 661 var test = Selector[PSEUDOS][match[1]]; 662 if (test) { // reorder match array and unescape special chars for tests 663 if (match[2]) { 664 match[2] = match[2].replace(/\\/g, ''); 665 } 666 return [match[2], test]; 667 } else { // selector token not supported (possibly missing CSS3 module) 668 return false; 669 } 670 } 671 } 672 ], 673 674 _getToken: function(token) { 675 return { 676 tagName: null, 677 id: null, 678 className: null, 679 attributes: {}, 680 combinator: null, 681 tests: [] 682 }; 683 }, 684 685 /** 686 Break selector into token units per simple selector. 687 Combinator is attached to the previous token. 688 */ 689 _tokenize: function(selector) { 690 selector = selector || ''; 691 selector = Selector._replaceShorthand(Y_Lang.trim(selector)); 692 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) 693 query = selector, // original query for debug report 694 tokens = [], // array of tokens 695 found = false, // whether or not any matches were found this pass 696 match, // the regex match 697 test, 698 i, parser; 699 700 /* 701 Search for selector patterns, store, and strip them from the selector string 702 until no patterns match (invalid selector) or we run out of chars. 703 704 Multiple attributes and pseudos are allowed, in any order. 705 for example: 706 'form:first-child[type=button]:not(button)[lang|=en]' 707 */ 708 709 outer: 710 do { 711 found = false; // reset after full pass 712 713 for (i = 0; (parser = Selector._parsers[i++]);) { 714 if ( (match = parser.re.exec(selector)) ) { // note assignment 715 if (parser.name !== COMBINATOR ) { 716 token.selector = selector; 717 } 718 selector = selector.replace(match[0], ''); // strip current match from selector 719 if (!selector.length) { 720 token.last = true; 721 } 722 723 if (Selector._attrFilters[match[1]]) { // convert class to className, etc. 724 match[1] = Selector._attrFilters[match[1]]; 725 } 726 727 test = parser.fn(match, token); 728 if (test === false) { // selector not supported 729 found = false; 730 break outer; 731 } else if (test) { 732 token.tests.push(test); 733 } 734 735 if (!selector.length || parser.name === COMBINATOR) { 736 tokens.push(token); 737 token = Selector._getToken(token); 738 if (parser.name === COMBINATOR) { 739 token.combinator = Selector.combinators[match[1]]; 740 } 741 } 742 found = true; 743 744 745 } 746 } 747 } while (found && selector.length); 748 749 if (!found || selector.length) { // not fully parsed 750 YAHOO.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector'); 751 tokens = []; 752 } 753 return tokens; 754 }, 755 756 _replaceShorthand: function(selector) { 757 var shorthand = Selector.shorthand, 758 esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc. 759 attrs, 760 pseudos, 761 re, i, len; 762 763 if (esc) { 764 selector = selector.replace(Selector._re.esc, '\uE000'); 765 } 766 767 attrs = selector.match(Selector._re.attr); 768 pseudos = selector.match(Selector._re.pseudos); 769 770 if (attrs) { 771 selector = selector.replace(Selector._re.attr, '\uE001'); 772 } 773 774 if (pseudos) { 775 selector = selector.replace(Selector._re.pseudos, '\uE002'); 776 } 777 778 779 for (re in shorthand) { 780 if (shorthand.hasOwnProperty(re)) { 781 selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]); 782 } 783 } 784 785 if (attrs) { 786 for (i = 0, len = attrs.length; i < len; ++i) { 787 selector = selector.replace(/\uE001/, attrs[i]); 788 } 789 } 790 791 if (pseudos) { 792 for (i = 0, len = pseudos.length; i < len; ++i) { 793 selector = selector.replace(/\uE002/, pseudos[i]); 794 } 795 } 796 797 selector = selector.replace(/\[/g, '\uE003'); 798 selector = selector.replace(/\]/g, '\uE004'); 799 800 selector = selector.replace(/\(/g, '\uE005'); 801 selector = selector.replace(/\)/g, '\uE006'); 802 803 if (esc) { 804 for (i = 0, len = esc.length; i < len; ++i) { 805 selector = selector.replace('\uE000', esc[i]); 806 } 807 } 808 809 return selector; 810 }, 811 812 _attrFilters: { 813 'class': 'className', 814 'for': 'htmlFor' 815 }, 816 817 getters: { 818 href: function(node, attr) { 819 return Y_DOM.getAttribute(node, attr); 820 } 821 } 822 }; 823 824 Y_mix(Selector, SelectorCSS2, true); 825 Selector.getters.src = Selector.getters.rel = Selector.getters.href; 826 827 // IE wants class with native queries 828 if (Selector.useNative && Y_DOC.querySelector) { 829 Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]'; 830 } 831 832 /** 833 * The selector css3 module provides support for css3 selectors. 834 * @module dom 835 * @submodule selector-css3 836 * @for Selector 837 */ 838 839 /* 840 an+b = get every _a_th node starting at the _b_th 841 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 842 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n") 843 an+0 = get every _a_th element, "0" may be omitted 844 */ 845 846 Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/; 847 848 Selector._getNth = function(node, expr, tag, reverse) { 849 Selector._reNth.test(expr); 850 var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_) 851 n = RegExp.$2, // "n" 852 oddeven = RegExp.$3, // "odd" or "even" 853 b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_ 854 result = [], 855 siblings = Selector._children(node.parentNode, tag), 856 op; 857 858 if (oddeven) { 859 a = 2; // always every other 860 op = '+'; 861 n = 'n'; 862 b = (oddeven === 'odd') ? 1 : 0; 863 } else if ( isNaN(a) ) { 864 a = (n) ? 1 : 0; // start from the first or no repeat 865 } 866 867 if (a === 0) { // just the first 868 if (reverse) { 869 b = siblings.length - b + 1; 870 } 871 872 if (siblings[b - 1] === node) { 873 return true; 874 } else { 875 return false; 876 } 877 878 } else if (a < 0) { 879 reverse = !!reverse; 880 a = Math.abs(a); 881 } 882 883 if (!reverse) { 884 for (var i = b - 1, len = siblings.length; i < len; i += a) { 885 if ( i >= 0 && siblings[i] === node ) { 886 return true; 887 } 888 } 889 } else { 890 for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) { 891 if ( i < len && siblings[i] === node ) { 892 return true; 893 } 894 } 895 } 896 return false; 897 }; 898 899 Y_mix(Selector.pseudos, { 900 'root': function(node) { 901 return node === node.ownerDocument.documentElement; 902 }, 903 904 'nth-child': function(node, expr) { 905 return Selector._getNth(node, expr); 906 }, 907 908 'nth-last-child': function(node, expr) { 909 return Selector._getNth(node, expr, null, true); 910 }, 911 912 'nth-of-type': function(node, expr) { 913 return Selector._getNth(node, expr, node.tagName); 914 }, 915 916 'nth-last-of-type': function(node, expr) { 917 return Selector._getNth(node, expr, node.tagName, true); 918 }, 919 920 'last-child': function(node) { 921 var children = Selector._children(node.parentNode); 922 return children[children.length - 1] === node; 923 }, 924 925 'first-of-type': function(node) { 926 return Selector._children(node.parentNode, node.tagName)[0] === node; 927 }, 928 929 'last-of-type': function(node) { 930 var children = Selector._children(node.parentNode, node.tagName); 931 return children[children.length - 1] === node; 932 }, 933 934 'only-child': function(node) { 935 var children = Selector._children(node.parentNode); 936 return children.length === 1 && children[0] === node; 937 }, 938 939 'only-of-type': function(node) { 940 var children = Selector._children(node.parentNode, node.tagName); 941 return children.length === 1 && children[0] === node; 942 }, 943 944 'empty': function(node) { 945 return node.childNodes.length === 0; 946 }, 947 948 'not': function(node, expr) { 949 return !Selector.test(node, expr); 950 }, 951 952 'contains': function(node, expr) { 953 var text = node.innerText || node.textContent || ''; 954 return text.indexOf(expr) > -1; 955 }, 956 957 'checked': function(node) { 958 return (node.checked === true || node.selected === true); 959 }, 960 961 enabled: function(node) { 962 return (node.disabled !== undefined && !node.disabled); 963 }, 964 965 disabled: function(node) { 966 return (node.disabled); 967 } 968 }); 969 970 Y_mix(Selector.operators, { 971 '^=': '^{val}', // Match starts with value 972 '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value 973 '$=': '{val}$', // Match ends with value 974 '*=': '{val}' // Match contains value as substring 975 }); 976 977 Selector.combinators['~'] = { 978 axis: 'previousSibling' 979 }; 980 YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"}); 981 982 }, '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 |