[ 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 return (firstOnly) ? (ret[0] || null) : ret; 172 173 }, 174 175 _replaceSelector: function(selector) { 176 var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc. 177 attrs, 178 pseudos; 179 180 // first replace escaped chars, which could be present in attrs or pseudos 181 selector = Y.Selector._replace('esc', selector); 182 183 // then replace pseudos before attrs to avoid replacing :not([foo]) 184 pseudos = Y.Selector._parse('pseudo', selector); 185 selector = Selector._replace('pseudo', selector); 186 187 attrs = Y.Selector._parse('attr', selector); 188 selector = Y.Selector._replace('attr', selector); 189 190 return { 191 esc: esc, 192 attrs: attrs, 193 pseudos: pseudos, 194 selector: selector 195 }; 196 }, 197 198 _restoreSelector: function(replaced) { 199 var selector = replaced.selector; 200 selector = Y.Selector._restore('attr', selector, replaced.attrs); 201 selector = Y.Selector._restore('pseudo', selector, replaced.pseudos); 202 selector = Y.Selector._restore('esc', selector, replaced.esc); 203 return selector; 204 }, 205 206 _replaceCommas: function(selector) { 207 var replaced = Y.Selector._replaceSelector(selector), 208 selector = replaced.selector; 209 210 if (selector) { 211 selector = selector.replace(/,/g, '\uE007'); 212 replaced.selector = selector; 213 selector = Y.Selector._restoreSelector(replaced); 214 } 215 return selector; 216 }, 217 218 // allows element scoped queries to begin with combinator 219 // e.g. query('> p', document.body) === query('body > p') 220 _splitQueries: function(selector, node) { 221 if (selector.indexOf(',') > -1) { 222 selector = Y.Selector._replaceCommas(selector); 223 } 224 225 var groups = selector.split('\uE007'), // split on replaced comma token 226 queries = [], 227 prefix = '', 228 id, 229 i, 230 len; 231 232 if (node) { 233 // enforce for element scoping 234 if (node.nodeType === 1) { // Elements only 235 id = Y.Selector._escapeId(Y.DOM.getId(node)); 236 237 if (!id) { 238 id = Y.guid(); 239 Y.DOM.setId(node, id); 240 } 241 242 prefix = '[id="' + id + '"] '; 243 } 244 245 for (i = 0, len = groups.length; i < len; ++i) { 246 selector = prefix + groups[i]; 247 queries.push([selector, node]); 248 } 249 } 250 251 return queries; 252 }, 253 254 _nativeQuery: function(selector, root, one) { 255 if ( 256 (Y.UA.webkit || Y.UA.opera) && // webkit (chrome, safari) and Opera 257 selector.indexOf(':checked') > -1 && // fail to pick up "selected" with ":checked" 258 (Y.Selector.pseudos && Y.Selector.pseudos.checked) 259 ) { 260 return Y.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 Y.Selector.query(selector, root, one, true); // redo with skipNative true 266 } 267 }, 268 269 /** 270 * Filters out nodes that do not match the given CSS selector. 271 * @method filter 272 * 273 * @param {HTMLElement[]} nodes An array of nodes. 274 * @param {String} selector A CSS selector to test each node against. 275 * @return {HTMLElement[]} The nodes that matched the given CSS selector. 276 * @static 277 */ 278 filter: function(nodes, selector) { 279 var ret = [], 280 i, node; 281 282 if (nodes && selector) { 283 for (i = 0; (node = nodes[i++]);) { 284 if (Y.Selector.test(node, selector)) { 285 ret[ret.length] = node; 286 } 287 } 288 } else { 289 } 290 291 return ret; 292 }, 293 294 /** 295 * Determines whether or not the given node matches the given CSS selector. 296 * @method test 297 * 298 * @param {HTMLElement} node A node to test. 299 * @param {String} selector A CSS selector to test the node against. 300 * @param {HTMLElement} root optional A node to start the query from. Defaults to the parent document of the node. 301 * @return {Boolean} Whether or not the given node matched the given CSS selector. 302 * @static 303 */ 304 test: function(node, selector, root) { 305 var ret = false, 306 useFrag = false, 307 groups, 308 parent, 309 item, 310 items, 311 frag, 312 id, 313 i, j, group; 314 315 if (node && node.tagName) { // only test HTMLElements 316 317 if (typeof selector == 'function') { // test with function 318 ret = selector.call(node, node); 319 } else { // test with query 320 // we need a root if off-doc 321 groups = selector.split(','); 322 if (!root && !Y.DOM.inDoc(node)) { 323 parent = node.parentNode; 324 if (parent) { 325 root = parent; 326 } else { // only use frag when no parent to query 327 frag = node[OWNER_DOCUMENT].createDocumentFragment(); 328 frag.appendChild(node); 329 root = frag; 330 useFrag = true; 331 } 332 } 333 root = root || node[OWNER_DOCUMENT]; 334 335 id = Y.Selector._escapeId(Y.DOM.getId(node)); 336 if (!id) { 337 id = Y.guid(); 338 Y.DOM.setId(node, id); 339 } 340 341 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test 342 group += '[id="' + id + '"]'; 343 items = Y.Selector.query(group, root); 344 345 for (j = 0; item = items[j++];) { 346 if (item === node) { 347 ret = true; 348 break; 349 } 350 } 351 if (ret) { 352 break; 353 } 354 } 355 356 if (useFrag) { // cleanup 357 frag.removeChild(node); 358 } 359 }; 360 } 361 362 return ret; 363 }, 364 365 /** 366 * A convenience method to emulate Y.Node's aNode.ancestor(selector). 367 * @method ancestor 368 * 369 * @param {HTMLElement} node A node to start the query from. 370 * @param {String} selector A CSS selector to test the node against. 371 * @param {Boolean} testSelf optional Whether or not to include the node in the scan. 372 * @return {HTMLElement} The ancestor node matching the selector, or null. 373 * @static 374 */ 375 ancestor: function (node, selector, testSelf) { 376 return Y.DOM.ancestor(node, function(n) { 377 return Y.Selector.test(n, selector); 378 }, testSelf); 379 }, 380 381 _parse: function(name, selector) { 382 return selector.match(Y.Selector._types[name].re); 383 }, 384 385 _replace: function(name, selector) { 386 var o = Y.Selector._types[name]; 387 return selector.replace(o.re, o.token); 388 }, 389 390 _restore: function(name, selector, items) { 391 if (items) { 392 var token = Y.Selector._types[name].token, 393 i, len; 394 for (i = 0, len = items.length; i < len; ++i) { 395 selector = selector.replace(token, items[i]); 396 } 397 } 398 return selector; 399 } 400 }; 401 402 Y.mix(Y.Selector, Selector, true); 403 404 })(Y); 405 406 407 }, '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 |