[ 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-native', function (Y, NAME) { 9 10 (function(Y) { 11 /** 12 * The selector-native module provides support for native querySelector 13 * @module dom 14 * @submodule selector-native 15 * @for Selector 16 */ 17 18 /** 19 * Provides support for using CSS selectors to query the DOM 20 * @class Selector 21 * @static 22 * @for Selector 23 */ 24 25 Y.namespace('Selector'); // allow native module to standalone 26 27 var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', 28 OWNER_DOCUMENT = 'ownerDocument'; 29 30 var Selector = { 31 _types: { 32 esc: { 33 token: '\uE000', 34 re: /\\[:\[\]\(\)#\.\'\>+~"]/gi 35 }, 36 37 attr: { 38 token: '\uE001', 39 re: /(\[[^\]]*\])/g 40 }, 41 42 pseudo: { 43 token: '\uE002', 44 re: /(\([^\)]*\))/g 45 } 46 }, 47 48 /** 49 * Use the native version of `querySelectorAll`, if it exists. 50 * 51 * @property useNative 52 * @default true 53 * @static 54 */ 55 useNative: true, 56 57 _escapeId: function(id) { 58 if (id) { 59 id = id.replace(/([:\[\]\(\)#\.'<>+~"])/g,'\\$1'); 60 } 61 return id; 62 }, 63 64 _compare: ('sourceIndex' in Y.config.doc.documentElement) ? 65 function(nodeA, nodeB) { 66 var a = nodeA.sourceIndex, 67 b = nodeB.sourceIndex; 68 69 if (a === b) { 70 return 0; 71 } else if (a > b) { 72 return 1; 73 } 74 75 return -1; 76 77 } : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ? 78 function(nodeA, nodeB) { 79 if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) { 80 return -1; 81 } else { 82 return 1; 83 } 84 } : 85 function(nodeA, nodeB) { 86 var rangeA, rangeB, compare; 87 if (nodeA && nodeB) { 88 rangeA = nodeA[OWNER_DOCUMENT].createRange(); 89 rangeA.setStart(nodeA, 0); 90 rangeB = nodeB[OWNER_DOCUMENT].createRange(); 91 rangeB.setStart(nodeB, 0); 92 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END 93 } 94 95 return compare; 96 97 }), 98 99 _sort: function(nodes) { 100 if (nodes) { 101 nodes = Y.Array(nodes, 0, true); 102 if (nodes.sort) { 103 nodes.sort(Selector._compare); 104 } 105 } 106 107 return nodes; 108 }, 109 110 _deDupe: function(nodes) { 111 var ret = [], 112 i, node; 113 114 for (i = 0; (node = nodes[i++]);) { 115 if (!node._found) { 116 ret[ret.length] = node; 117 node._found = true; 118 } 119 } 120 121 for (i = 0; (node = ret[i++]);) { 122 node._found = null; 123 node.removeAttribute('_found'); 124 } 125 126 return ret; 127 }, 128 129 /** 130 * Retrieves a set of nodes based on a given CSS selector. 131 * @method query 132 * 133 * @param {String} selector A CSS selector. 134 * @param {HTMLElement} root optional A node to start the query from. Defaults to `Y.config.doc`. 135 * @param {Boolean} firstOnly optional Whether or not to return only the first match. 136 * @return {HTMLElement[]} The array of nodes that matched the given selector. 137 * @static 138 */ 139 query: function(selector, root, firstOnly, skipNative) { 140 root = root || Y.config.doc; 141 var ret = [], 142 useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative), 143 queries = [[selector, root]], 144 query, 145 result, 146 i, 147 fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery; 148 149 if (selector && fn) { 150 // split group into seperate queries 151 if (!skipNative && // already done if skipping 152 (!useNative || root.tagName)) { // split native when element scoping is needed 153 queries = Selector._splitQueries(selector, root); 154 } 155 156 for (i = 0; (query = queries[i++]);) { 157 result = fn(query[0], query[1], firstOnly); 158 if (!firstOnly) { // coerce DOM Collection to Array 159 result = Y.Array(result, 0, true); 160 } 161 if (result) { 162 ret = ret.concat(result); 163 } 164 } 165 166 if (queries.length > 1) { // remove dupes and sort by doc order 167 ret = Selector._sort(Selector._deDupe(ret)); 168 } 169 } 170 171 Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector'); 172 return (firstOnly) ? (ret[0] || null) : ret; 173 174 }, 175 176 _replaceSelector: function(selector) { 177 var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc. 178 attrs, 179 pseudos; 180 181 // first replace escaped chars, which could be present in attrs or pseudos 182 selector = Y.Selector._replace('esc', selector); 183 184 // then replace pseudos before attrs to avoid replacing :not([foo]) 185 pseudos = Y.Selector._parse('pseudo', selector); 186 selector = Selector._replace('pseudo', selector); 187 188 attrs = Y.Selector._parse('attr', selector); 189 selector = Y.Selector._replace('attr', selector); 190 191 return { 192 esc: esc, 193 attrs: attrs, 194 pseudos: pseudos, 195 selector: selector 196 }; 197 }, 198 199 _restoreSelector: function(replaced) { 200 var selector = replaced.selector; 201 selector = Y.Selector._restore('attr', selector, replaced.attrs); 202 selector = Y.Selector._restore('pseudo', selector, replaced.pseudos); 203 selector = Y.Selector._restore('esc', selector, replaced.esc); 204 return selector; 205 }, 206 207 _replaceCommas: function(selector) { 208 var replaced = Y.Selector._replaceSelector(selector), 209 selector = replaced.selector; 210 211 if (selector) { 212 selector = selector.replace(/,/g, '\uE007'); 213 replaced.selector = selector; 214 selector = Y.Selector._restoreSelector(replaced); 215 } 216 return selector; 217 }, 218 219 // allows element scoped queries to begin with combinator 220 // e.g. query('> p', document.body) === query('body > p') 221 _splitQueries: function(selector, node) { 222 if (selector.indexOf(',') > -1) { 223 selector = Y.Selector._replaceCommas(selector); 224 } 225 226 var groups = selector.split('\uE007'), // split on replaced comma token 227 queries = [], 228 prefix = '', 229 id, 230 i, 231 len; 232 233 if (node) { 234 // enforce for element scoping 235 if (node.nodeType === 1) { // Elements only 236 id = Y.Selector._escapeId(Y.DOM.getId(node)); 237 238 if (!id) { 239 id = Y.guid(); 240 Y.DOM.setId(node, id); 241 } 242 243 prefix = '[id="' + id + '"] '; 244 } 245 246 for (i = 0, len = groups.length; i < len; ++i) { 247 selector = prefix + groups[i]; 248 queries.push([selector, node]); 249 } 250 } 251 252 return queries; 253 }, 254 255 _nativeQuery: function(selector, root, one) { 256 if ( 257 (Y.UA.webkit || Y.UA.opera) && // webkit (chrome, safari) and Opera 258 selector.indexOf(':checked') > -1 && // fail to pick up "selected" with ":checked" 259 (Y.Selector.pseudos && Y.Selector.pseudos.checked) 260 ) { 261 return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query 262 } 263 try { 264 //Y.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 //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native'); 268 return Y.Selector.query(selector, root, one, true); // redo with skipNative true 269 } 270 }, 271 272 /** 273 * Filters out nodes that do not match the given CSS selector. 274 * @method filter 275 * 276 * @param {HTMLElement[]} nodes An array of nodes. 277 * @param {String} selector A CSS selector to test each node against. 278 * @return {HTMLElement[]} The nodes that matched the given CSS selector. 279 * @static 280 */ 281 filter: function(nodes, selector) { 282 var ret = [], 283 i, node; 284 285 if (nodes && selector) { 286 for (i = 0; (node = nodes[i++]);) { 287 if (Y.Selector.test(node, selector)) { 288 ret[ret.length] = node; 289 } 290 } 291 } else { 292 Y.log('invalid filter input (nodes: ' + nodes + 293 ', selector: ' + selector + ')', 'warn', 'Selector'); 294 } 295 296 return ret; 297 }, 298 299 /** 300 * Determines whether or not the given node matches the given CSS selector. 301 * @method test 302 * 303 * @param {HTMLElement} node A node to test. 304 * @param {String} selector A CSS selector to test the node against. 305 * @param {HTMLElement} root optional A node to start the query from. Defaults to the parent document of the node. 306 * @return {Boolean} Whether or not the given node matched the given CSS selector. 307 * @static 308 */ 309 test: function(node, selector, root) { 310 var ret = false, 311 useFrag = false, 312 groups, 313 parent, 314 item, 315 items, 316 frag, 317 id, 318 i, j, group; 319 320 if (node && node.tagName) { // only test HTMLElements 321 322 if (typeof selector == 'function') { // test with function 323 ret = selector.call(node, node); 324 } else { // test with query 325 // we need a root if off-doc 326 groups = selector.split(','); 327 if (!root && !Y.DOM.inDoc(node)) { 328 parent = node.parentNode; 329 if (parent) { 330 root = parent; 331 } else { // only use frag when no parent to query 332 frag = node[OWNER_DOCUMENT].createDocumentFragment(); 333 frag.appendChild(node); 334 root = frag; 335 useFrag = true; 336 } 337 } 338 root = root || node[OWNER_DOCUMENT]; 339 340 id = Y.Selector._escapeId(Y.DOM.getId(node)); 341 if (!id) { 342 id = Y.guid(); 343 Y.DOM.setId(node, id); 344 } 345 346 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test 347 group += '[id="' + id + '"]'; 348 items = Y.Selector.query(group, root); 349 350 for (j = 0; item = items[j++];) { 351 if (item === node) { 352 ret = true; 353 break; 354 } 355 } 356 if (ret) { 357 break; 358 } 359 } 360 361 if (useFrag) { // cleanup 362 frag.removeChild(node); 363 } 364 }; 365 } 366 367 return ret; 368 }, 369 370 /** 371 * A convenience method to emulate Y.Node's aNode.ancestor(selector). 372 * @method ancestor 373 * 374 * @param {HTMLElement} node A node to start the query from. 375 * @param {String} selector A CSS selector to test the node against. 376 * @param {Boolean} testSelf optional Whether or not to include the node in the scan. 377 * @return {HTMLElement} The ancestor node matching the selector, or null. 378 * @static 379 */ 380 ancestor: function (node, selector, testSelf) { 381 return Y.DOM.ancestor(node, function(n) { 382 return Y.Selector.test(n, selector); 383 }, testSelf); 384 }, 385 386 _parse: function(name, selector) { 387 return selector.match(Y.Selector._types[name].re); 388 }, 389 390 _replace: function(name, selector) { 391 var o = Y.Selector._types[name]; 392 return selector.replace(o.re, o.token); 393 }, 394 395 _restore: function(name, selector, items) { 396 if (items) { 397 var token = Y.Selector._types[name].token, 398 i, len; 399 for (i = 0, len = items.length; i < len; ++i) { 400 selector = selector.replace(token, items[i]); 401 } 402 } 403 return selector; 404 } 405 }; 406 407 Y.mix(Y.Selector, Selector, true); 408 409 })(Y); 410 411 412 }, '3.17.2', {"requires": ["dom-base"]});
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 |