[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('selector-css2', function (Y, NAME) { 9 10 /** 11 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. 12 * @module dom 13 * @submodule selector-css2 14 * @for Selector 15 */ 16 17 /* 18 * Provides helper methods for collecting and filtering DOM elements. 19 */ 20 21 var PARENT_NODE = 'parentNode', 22 TAG_NAME = 'tagName', 23 ATTRIBUTES = 'attributes', 24 COMBINATOR = 'combinator', 25 PSEUDOS = 'pseudos', 26 27 Selector = Y.Selector, 28 29 SelectorCSS2 = { 30 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, 31 SORT_RESULTS: true, 32 33 // TODO: better detection, document specific 34 _isXML: (function() { 35 var isXML = (Y.config.doc.createElement('div').tagName !== 'DIV'); 36 return isXML; 37 }()), 38 39 /** 40 * Mapping of shorthand tokens to corresponding attribute selector 41 * @property shorthand 42 * @type object 43 */ 44 shorthand: { 45 '\\#(-?[_a-z0-9]+[-\\w\\uE000]*)': '[id=$1]', 46 '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]' 47 }, 48 49 /** 50 * List of operators and corresponding boolean functions. 51 * These functions are passed the attribute and the current node's value of the attribute. 52 * @property operators 53 * @type object 54 */ 55 operators: { 56 '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute 57 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited 58 '|=': '^{val}-?' // optional hyphen-delimited 59 }, 60 61 pseudos: { 62 'first-child': function(node) { 63 return Y.DOM._children(node[PARENT_NODE])[0] === node; 64 } 65 }, 66 67 _bruteQuery: function(selector, root, firstOnly) { 68 var ret = [], 69 nodes = [], 70 visited, 71 tokens = Selector._tokenize(selector), 72 token = tokens[tokens.length - 1], 73 rootDoc = Y.DOM._getDoc(root), 74 child, 75 id, 76 className, 77 tagName, 78 isUniversal; 79 80 if (token) { 81 // prefilter nodes 82 id = token.id; 83 className = token.className; 84 tagName = token.tagName || '*'; 85 86 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags 87 // try ID first, unless no root.all && root not in document 88 // (root.all works off document, but not getElementById) 89 if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) { 90 nodes = Y.DOM.allById(id, root); 91 // try className 92 } else if (className) { 93 nodes = root.getElementsByClassName(className); 94 } else { // default to tagName 95 nodes = root.getElementsByTagName(tagName); 96 } 97 98 } else { // brute getElementsByTagName() 99 visited = []; 100 child = root.firstChild; 101 isUniversal = tagName === "*"; 102 while (child) { 103 while (child) { 104 // IE 6-7 considers comment nodes as element nodes, and gives them the tagName "!". 105 // We can filter them out by checking if its tagName is > "@". 106 // This also avoids a superflous nodeType === 1 check. 107 if (child.tagName > "@" && (isUniversal || child.tagName === tagName)) { 108 nodes.push(child); 109 } 110 111 // We may need to traverse back up the tree to find more unvisited subtrees. 112 visited.push(child); 113 child = child.firstChild; 114 } 115 116 // Find the most recently visited node who has a next sibling. 117 while (visited.length > 0 && !child) { 118 child = visited.pop().nextSibling; 119 } 120 } 121 } 122 123 if (nodes.length) { 124 ret = Selector._filterNodes(nodes, tokens, firstOnly); 125 } 126 } 127 128 return ret; 129 }, 130 131 _filterNodes: function(nodes, tokens, firstOnly) { 132 var i = 0, 133 j, 134 len = tokens.length, 135 n = len - 1, 136 result = [], 137 node = nodes[0], 138 tmpNode = node, 139 getters = Y.Selector.getters, 140 operator, 141 combinator, 142 token, 143 path, 144 pass, 145 value, 146 tests, 147 test; 148 149 for (i = 0; (tmpNode = node = nodes[i++]);) { 150 n = len - 1; 151 path = null; 152 153 testLoop: 154 while (tmpNode && tmpNode.tagName) { 155 token = tokens[n]; 156 tests = token.tests; 157 j = tests.length; 158 if (j && !pass) { 159 while ((test = tests[--j])) { 160 operator = test[1]; 161 if (getters[test[0]]) { 162 value = getters[test[0]](tmpNode, test[0]); 163 } else { 164 value = tmpNode[test[0]]; 165 if (test[0] === 'tagName' && !Selector._isXML) { 166 value = value.toUpperCase(); 167 } 168 if (typeof value != 'string' && value !== undefined && value.toString) { 169 value = value.toString(); // coerce for comparison 170 } else if (value === undefined && tmpNode.getAttribute) { 171 // use getAttribute for non-standard attributes 172 value = tmpNode.getAttribute(test[0], 2); // 2 === force string for IE 173 } 174 } 175 176 if ((operator === '=' && value !== test[2]) || // fast path for equality 177 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo) 178 operator.test && !operator.test(value)) || // regex test 179 (!operator.test && // protect against RegExp as function (webkit) 180 typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test 181 182 // skip non element nodes or non-matching tags 183 if ((tmpNode = tmpNode[path])) { 184 while (tmpNode && 185 (!tmpNode.tagName || 186 (token.tagName && token.tagName !== tmpNode.tagName)) 187 ) { 188 tmpNode = tmpNode[path]; 189 } 190 } 191 continue testLoop; 192 } 193 } 194 } 195 196 n--; // move to next token 197 // now that we've passed the test, move up the tree by combinator 198 if (!pass && (combinator = token.combinator)) { 199 path = combinator.axis; 200 tmpNode = tmpNode[path]; 201 202 // skip non element nodes 203 while (tmpNode && !tmpNode.tagName) { 204 tmpNode = tmpNode[path]; 205 } 206 207 if (combinator.direct) { // one pass only 208 path = null; 209 } 210 211 } else { // success if we made it this far 212 result.push(node); 213 if (firstOnly) { 214 return result; 215 } 216 break; 217 } 218 } 219 } 220 node = tmpNode = null; 221 return result; 222 }, 223 224 combinators: { 225 ' ': { 226 axis: 'parentNode' 227 }, 228 229 '>': { 230 axis: 'parentNode', 231 direct: true 232 }, 233 234 235 '+': { 236 axis: 'previousSibling', 237 direct: true 238 } 239 }, 240 241 _parsers: [ 242 { 243 name: ATTRIBUTES, 244 re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i, 245 fn: function(match, token) { 246 var operator = match[2] || '', 247 operators = Selector.operators, 248 escVal = (match[3]) ? match[3].replace(/\\/g, '') : '', 249 test; 250 251 // add prefiltering for ID and CLASS 252 if ((match[1] === 'id' && operator === '=') || 253 (match[1] === 'className' && 254 Y.config.doc.documentElement.getElementsByClassName && 255 (operator === '~=' || operator === '='))) { 256 token.prefilter = match[1]; 257 258 259 match[3] = escVal; 260 261 // escape all but ID for prefilter, which may run through QSA (via Dom.allById) 262 token[match[1]] = (match[1] === 'id') ? match[3] : escVal; 263 264 } 265 266 // add tests 267 if (operator in operators) { 268 test = operators[operator]; 269 if (typeof test === 'string') { 270 match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1'); 271 test = new RegExp(test.replace('{val}', match[3])); 272 } 273 match[2] = test; 274 } 275 if (!token.last || token.prefilter !== match[1]) { 276 return match.slice(1); 277 } 278 } 279 }, 280 { 281 name: TAG_NAME, 282 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, 283 fn: function(match, token) { 284 var tag = match[1]; 285 286 if (!Selector._isXML) { 287 tag = tag.toUpperCase(); 288 } 289 290 token.tagName = tag; 291 292 if (tag !== '*' && (!token.last || token.prefilter)) { 293 return [TAG_NAME, '=', tag]; 294 } 295 if (!token.prefilter) { 296 token.prefilter = 'tagName'; 297 } 298 } 299 }, 300 { 301 name: COMBINATOR, 302 re: /^\s*([>+~]|\s)\s*/, 303 fn: function(match, token) { 304 } 305 }, 306 { 307 name: PSEUDOS, 308 re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i, 309 fn: function(match, token) { 310 var test = Selector[PSEUDOS][match[1]]; 311 if (test) { // reorder match array and unescape special chars for tests 312 if (match[2]) { 313 match[2] = match[2].replace(/\\/g, ''); 314 } 315 return [match[2], test]; 316 } else { // selector token not supported (possibly missing CSS3 module) 317 return false; 318 } 319 } 320 } 321 ], 322 323 _getToken: function(token) { 324 return { 325 tagName: null, 326 id: null, 327 className: null, 328 attributes: {}, 329 combinator: null, 330 tests: [] 331 }; 332 }, 333 334 /* 335 Break selector into token units per simple selector. 336 Combinator is attached to the previous token. 337 */ 338 _tokenize: function(selector) { 339 selector = selector || ''; 340 selector = Selector._parseSelector(Y.Lang.trim(selector)); 341 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) 342 query = selector, // original query for debug report 343 tokens = [], // array of tokens 344 found = false, // whether or not any matches were found this pass 345 match, // the regex match 346 test, 347 i, parser; 348 349 /* 350 Search for selector patterns, store, and strip them from the selector string 351 until no patterns match (invalid selector) or we run out of chars. 352 353 Multiple attributes and pseudos are allowed, in any order. 354 for example: 355 'form:first-child[type=button]:not(button)[lang|=en]' 356 */ 357 outer: 358 do { 359 found = false; // reset after full pass 360 for (i = 0; (parser = Selector._parsers[i++]);) { 361 if ( (match = parser.re.exec(selector)) ) { // note assignment 362 if (parser.name !== COMBINATOR ) { 363 token.selector = selector; 364 } 365 selector = selector.replace(match[0], ''); // strip current match from selector 366 if (!selector.length) { 367 token.last = true; 368 } 369 370 if (Selector._attrFilters[match[1]]) { // convert class to className, etc. 371 match[1] = Selector._attrFilters[match[1]]; 372 } 373 374 test = parser.fn(match, token); 375 if (test === false) { // selector not supported 376 found = false; 377 break outer; 378 } else if (test) { 379 token.tests.push(test); 380 } 381 382 if (!selector.length || parser.name === COMBINATOR) { 383 tokens.push(token); 384 token = Selector._getToken(token); 385 if (parser.name === COMBINATOR) { 386 token.combinator = Y.Selector.combinators[match[1]]; 387 } 388 } 389 found = true; 390 } 391 } 392 } while (found && selector.length); 393 394 if (!found || selector.length) { // not fully parsed 395 tokens = []; 396 } 397 return tokens; 398 }, 399 400 _replaceMarkers: function(selector) { 401 selector = selector.replace(/\[/g, '\uE003'); 402 selector = selector.replace(/\]/g, '\uE004'); 403 404 selector = selector.replace(/\(/g, '\uE005'); 405 selector = selector.replace(/\)/g, '\uE006'); 406 return selector; 407 }, 408 409 _replaceShorthand: function(selector) { 410 var shorthand = Y.Selector.shorthand, 411 re; 412 413 for (re in shorthand) { 414 if (shorthand.hasOwnProperty(re)) { 415 selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]); 416 } 417 } 418 419 return selector; 420 }, 421 422 _parseSelector: function(selector) { 423 var replaced = Y.Selector._replaceSelector(selector), 424 selector = replaced.selector; 425 426 // replace shorthand (".foo, #bar") after pseudos and attrs 427 // to avoid replacing unescaped chars 428 selector = Y.Selector._replaceShorthand(selector); 429 430 selector = Y.Selector._restore('attr', selector, replaced.attrs); 431 selector = Y.Selector._restore('pseudo', selector, replaced.pseudos); 432 433 // replace braces and parens before restoring escaped chars 434 // to avoid replacing ecaped markers 435 selector = Y.Selector._replaceMarkers(selector); 436 selector = Y.Selector._restore('esc', selector, replaced.esc); 437 438 return selector; 439 }, 440 441 _attrFilters: { 442 'class': 'className', 443 'for': 'htmlFor' 444 }, 445 446 getters: { 447 href: function(node, attr) { 448 return Y.DOM.getAttribute(node, attr); 449 }, 450 451 id: function(node, attr) { 452 return Y.DOM.getId(node); 453 } 454 } 455 }; 456 457 Y.mix(Y.Selector, SelectorCSS2, true); 458 Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href; 459 460 // IE wants class with native queries 461 if (Y.Selector.useNative && Y.config.doc.querySelector) { 462 Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]'; 463 } 464 465 466 }, '3.17.2', {"requires": ["selector-native"]});
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 |