AutorÃa | Ultima modificación | Ver Log |
YUI.add('selector-native', function (Y, NAME) {(function(Y) {/*** The selector-native module provides support for native querySelector* @module dom* @submodule selector-native* @for Selector*//*** Provides support for using CSS selectors to query the DOM* @class Selector* @static* @for Selector*/Y.namespace('Selector'); // allow native module to standalonevar COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',OWNER_DOCUMENT = 'ownerDocument';var Selector = {_types: {esc: {token: '\uE000',re: /\\[:\[\]\(\)#\.\'\>+~"]/gi},attr: {token: '\uE001',re: /(\[[^\]]*\])/g},pseudo: {token: '\uE002',re: /(\([^\)]*\))/g}},/*** Use the native version of `querySelectorAll`, if it exists.** @property useNative* @default true* @static*/useNative: true,_escapeId: function(id) {if (id) {id = id.replace(/([:\[\]\(\)#\.'<>+~"])/g,'\\$1');}return id;},_compare: ('sourceIndex' in Y.config.doc.documentElement) ?function(nodeA, nodeB) {var a = nodeA.sourceIndex,b = nodeB.sourceIndex;if (a === b) {return 0;} else if (a > b) {return 1;}return -1;} : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ?function(nodeA, nodeB) {if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {return -1;} else {return 1;}} :function(nodeA, nodeB) {var rangeA, rangeB, compare;if (nodeA && nodeB) {rangeA = nodeA[OWNER_DOCUMENT].createRange();rangeA.setStart(nodeA, 0);rangeB = nodeB[OWNER_DOCUMENT].createRange();rangeB.setStart(nodeB, 0);compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END}return compare;}),_sort: function(nodes) {if (nodes) {nodes = Y.Array(nodes, 0, true);if (nodes.sort) {nodes.sort(Selector._compare);}}return nodes;},_deDupe: function(nodes) {var ret = [],i, node;for (i = 0; (node = nodes[i++]);) {if (!node._found) {ret[ret.length] = node;node._found = true;}}for (i = 0; (node = ret[i++]);) {node._found = null;node.removeAttribute('_found');}return ret;},/*** Retrieves a set of nodes based on a given CSS selector.* @method query** @param {String} selector A CSS selector.* @param {HTMLElement} root optional A node to start the query from. Defaults to `Y.config.doc`.* @param {Boolean} firstOnly optional Whether or not to return only the first match.* @return {HTMLElement[]} The array of nodes that matched the given selector.* @static*/query: function(selector, root, firstOnly, skipNative) {root = root || Y.config.doc;var ret = [],useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative),queries = [[selector, root]],query,result,i,fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;if (selector && fn) {// split group into seperate queriesif (!skipNative && // already done if skipping(!useNative || root.tagName)) { // split native when element scoping is neededqueries = Selector._splitQueries(selector, root);}for (i = 0; (query = queries[i++]);) {result = fn(query[0], query[1], firstOnly);if (!firstOnly) { // coerce DOM Collection to Arrayresult = Y.Array(result, 0, true);}if (result) {ret = ret.concat(result);}}if (queries.length > 1) { // remove dupes and sort by doc orderret = Selector._sort(Selector._deDupe(ret));}}Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');return (firstOnly) ? (ret[0] || null) : ret;},_replaceSelector: function(selector) {var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc.attrs,pseudos;// first replace escaped chars, which could be present in attrs or pseudosselector = Y.Selector._replace('esc', selector);// then replace pseudos before attrs to avoid replacing :not([foo])pseudos = Y.Selector._parse('pseudo', selector);selector = Selector._replace('pseudo', selector);attrs = Y.Selector._parse('attr', selector);selector = Y.Selector._replace('attr', selector);return {esc: esc,attrs: attrs,pseudos: pseudos,selector: selector};},_restoreSelector: function(replaced) {var selector = replaced.selector;selector = Y.Selector._restore('attr', selector, replaced.attrs);selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);selector = Y.Selector._restore('esc', selector, replaced.esc);return selector;},_replaceCommas: function(selector) {var replaced = Y.Selector._replaceSelector(selector),selector = replaced.selector;if (selector) {selector = selector.replace(/,/g, '\uE007');replaced.selector = selector;selector = Y.Selector._restoreSelector(replaced);}return selector;},// allows element scoped queries to begin with combinator// e.g. query('> p', document.body) === query('body > p')_splitQueries: function(selector, node) {if (selector.indexOf(',') > -1) {selector = Y.Selector._replaceCommas(selector);}var groups = selector.split('\uE007'), // split on replaced comma tokenqueries = [],prefix = '',id,i,len;if (node) {// enforce for element scopingif (node.nodeType === 1) { // Elements onlyid = Y.Selector._escapeId(Y.DOM.getId(node));if (!id) {id = Y.guid();Y.DOM.setId(node, id);}prefix = '[id="' + id + '"] ';}for (i = 0, len = groups.length; i < len; ++i) {selector = prefix + groups[i];queries.push([selector, node]);}}return queries;},_nativeQuery: function(selector, root, one) {if ((Y.UA.webkit || Y.UA.opera) && // webkit (chrome, safari) and Operaselector.indexOf(':checked') > -1 && // fail to pick up "selected" with ":checked"(Y.Selector.pseudos && Y.Selector.pseudos.checked)) {return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query}try {//Y.log('trying native query with: ' + selector, 'info', 'selector-native');return root['querySelector' + (one ? '' : 'All')](selector);} catch(e) { // fallback to brute if available//Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');return Y.Selector.query(selector, root, one, true); // redo with skipNative true}},/*** Filters out nodes that do not match the given CSS selector.* @method filter** @param {HTMLElement[]} nodes An array of nodes.* @param {String} selector A CSS selector to test each node against.* @return {HTMLElement[]} The nodes that matched the given CSS selector.* @static*/filter: function(nodes, selector) {var ret = [],i, node;if (nodes && selector) {for (i = 0; (node = nodes[i++]);) {if (Y.Selector.test(node, selector)) {ret[ret.length] = node;}}} else {Y.log('invalid filter input (nodes: ' + nodes +', selector: ' + selector + ')', 'warn', 'Selector');}return ret;},/*** Determines whether or not the given node matches the given CSS selector.* @method test** @param {HTMLElement} node A node to test.* @param {String} selector A CSS selector to test the node against.* @param {HTMLElement} root optional A node to start the query from. Defaults to the parent document of the node.* @return {Boolean} Whether or not the given node matched the given CSS selector.* @static*/test: function(node, selector, root) {var ret = false,useFrag = false,groups,parent,item,items,frag,id,i, j, group;if (node && node.tagName) { // only test HTMLElementsif (typeof selector == 'function') { // test with functionret = selector.call(node, node);} else { // test with query// we need a root if off-docgroups = selector.split(',');if (!root && !Y.DOM.inDoc(node)) {parent = node.parentNode;if (parent) {root = parent;} else { // only use frag when no parent to queryfrag = node[OWNER_DOCUMENT].createDocumentFragment();frag.appendChild(node);root = frag;useFrag = true;}}root = root || node[OWNER_DOCUMENT];id = Y.Selector._escapeId(Y.DOM.getId(node));if (!id) {id = Y.guid();Y.DOM.setId(node, id);}for (i = 0; (group = groups[i++]);) { // TODO: off-dom testgroup += '[id="' + id + '"]';items = Y.Selector.query(group, root);for (j = 0; item = items[j++];) {if (item === node) {ret = true;break;}}if (ret) {break;}}if (useFrag) { // cleanupfrag.removeChild(node);}};}return ret;},/*** A convenience method to emulate Y.Node's aNode.ancestor(selector).* @method ancestor** @param {HTMLElement} node A node to start the query from.* @param {String} selector A CSS selector to test the node against.* @param {Boolean} testSelf optional Whether or not to include the node in the scan.* @return {HTMLElement} The ancestor node matching the selector, or null.* @static*/ancestor: function (node, selector, testSelf) {return Y.DOM.ancestor(node, function(n) {return Y.Selector.test(n, selector);}, testSelf);},_parse: function(name, selector) {return selector.match(Y.Selector._types[name].re);},_replace: function(name, selector) {var o = Y.Selector._types[name];return selector.replace(o.re, o.token);},_restore: function(name, selector, items) {if (items) {var token = Y.Selector._types[name].token,i, len;for (i = 0, len = items.length; i < len; ++i) {selector = selector.replace(token, items[i]);}}return selector;}};Y.mix(Y.Selector, Selector, true);})(Y);}, '3.18.1', {"requires": ["dom-base"]});