]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/dom/selector-native.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / dom / selector-native.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('selector-native', function(Y) {
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     _foundCache: [],
32
33     useNative: true,
34
35     _compare: ('sourceIndex' in Y.config.doc.documentElement) ?
36         function(nodeA, nodeB) {
37             var a = nodeA.sourceIndex,
38                 b = nodeB.sourceIndex;
39
40             if (a === b) {
41                 return 0;
42             } else if (a > b) {
43                 return 1;
44             }
45
46             return -1;
47
48         } : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ?
49         function(nodeA, nodeB) {
50             if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
51                 return -1;
52             } else {
53                 return 1;
54             }
55         } :
56         function(nodeA, nodeB) {
57             var rangeA, rangeB, compare;
58             if (nodeA && nodeB) {
59                 rangeA = nodeA[OWNER_DOCUMENT].createRange();
60                 rangeA.setStart(nodeA, 0);
61                 rangeB = nodeB[OWNER_DOCUMENT].createRange();
62                 rangeB.setStart(nodeB, 0);
63                 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
64             }
65
66             return compare;
67         
68     }),
69
70     _sort: function(nodes) {
71         if (nodes) {
72             nodes = Y.Array(nodes, 0, true);
73             if (nodes.sort) {
74                 nodes.sort(Selector._compare);
75             }
76         }
77
78         return nodes;
79     },
80
81     _deDupe: function(nodes) {
82         var ret = [],
83             i, node;
84
85         for (i = 0; (node = nodes[i++]);) {
86             if (!node._found) {
87                 ret[ret.length] = node;
88                 node._found = true;
89             }
90         }
91
92         for (i = 0; (node = ret[i++]);) {
93             node._found = null;
94             node.removeAttribute('_found');
95         }
96
97         return ret;
98     },
99
100     /**
101      * Retrieves a set of nodes based on a given CSS selector. 
102      * @method query
103      *
104      * @param {string} selector The CSS Selector to test the node against.
105      * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
106      * @param {Boolean} firstOnly optional Whether or not to return only the first match.
107      * @return {Array} An array of nodes that match the given selector.
108      * @static
109      */
110     query: function(selector, root, firstOnly, skipNative) {
111         root = root || Y.config.doc;
112         var ret = [],
113             useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative),
114             queries = [[selector, root]],
115             query,
116             result,
117             i,
118             fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
119
120         if (selector && fn) {
121             // split group into seperate queries
122             if (!skipNative && // already done if skipping
123                     (!useNative || root.tagName)) { // split native when element scoping is needed
124                 queries = Selector._splitQueries(selector, root);
125             }
126
127             for (i = 0; (query = queries[i++]);) {
128                 result = fn(query[0], query[1], firstOnly);
129                 if (!firstOnly) { // coerce DOM Collection to Array
130                     result = Y.Array(result, 0, true);
131                 }
132                 if (result) {
133                     ret = ret.concat(result);
134                 }
135             }
136
137             if (queries.length > 1) { // remove dupes and sort by doc order 
138                 ret = Selector._sort(Selector._deDupe(ret));
139             }
140         }
141
142         return (firstOnly) ? (ret[0] || null) : ret;
143
144     },
145
146     // allows element scoped queries to begin with combinator
147     // e.g. query('> p', document.body) === query('body > p')
148     _splitQueries: function(selector, node) {
149         var groups = selector.split(','),
150             queries = [],
151             prefix = '',
152             i, len;
153
154         if (node) {
155             // enforce for element scoping
156             if (node.tagName) {
157                 node.id = node.id || Y.guid();
158                 prefix = '[id="' + node.id + '"] ';
159             }
160
161             for (i = 0, len = groups.length; i < len; ++i) {
162                 selector =  prefix + groups[i];
163                 queries.push([selector, node]);
164             }
165         }
166
167         return queries;
168     },
169
170     _nativeQuery: function(selector, root, one) {
171         if (Y.UA.webkit && selector.indexOf(':checked') > -1 &&
172                 (Y.Selector.pseudos && Y.Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
173             return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
174         }
175         try {
176             return root['querySelector' + (one ? '' : 'All')](selector);
177         } catch(e) { // fallback to brute if available
178             return Y.Selector.query(selector, root, one, true); // redo with skipNative true
179         }
180     },
181
182     filter: function(nodes, selector) {
183         var ret = [],
184             i, node;
185
186         if (nodes && selector) {
187             for (i = 0; (node = nodes[i++]);) {
188                 if (Y.Selector.test(node, selector)) {
189                     ret[ret.length] = node;
190                 }
191             }
192         } else {
193         }
194
195         return ret;
196     },
197
198     test: function(node, selector, root) {
199         var ret = false,
200             groups = selector.split(','),
201             useFrag = false,
202             parent,
203             item,
204             items,
205             frag,
206             i, j, group;
207
208         if (node && node.tagName) { // only test HTMLElements
209
210             // we need a root if off-doc
211             if (!root && !Y.DOM.inDoc(node)) {
212                 parent = node.parentNode;
213                 if (parent) { 
214                     root = parent;
215                 } else { // only use frag when no parent to query
216                     frag = node[OWNER_DOCUMENT].createDocumentFragment();
217                     frag.appendChild(node);
218                     root = frag;
219                     useFrag = true;
220                 }
221             }
222             root = root || node[OWNER_DOCUMENT];
223
224             if (!node.id) {
225                 node.id = Y.guid();
226             }
227             for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
228                 group += '[id="' + node.id + '"]';
229                 items = Y.Selector.query(group, root);
230
231                 for (j = 0; item = items[j++];) {
232                     if (item === node) {
233                         ret = true;
234                         break;
235                     }
236                 }
237                 if (ret) {
238                     break;
239                 }
240             }
241
242             if (useFrag) { // cleanup
243                 frag.removeChild(node);
244             }
245         }
246
247         return ret;
248     },
249
250     /**
251      * A convenience function to emulate Y.Node's aNode.ancestor(selector).
252      * @param {HTMLElement} element An HTMLElement to start the query from.
253      * @param {String} selector The CSS selector to test the node against.
254      * @return {HTMLElement} The ancestor node matching the selector, or null.
255      * @param {Boolean} testSelf optional Whether or not to include the element in the scan 
256      * @static
257      * @method ancestor
258      */
259     ancestor: function (element, selector, testSelf) {
260         return Y.DOM.ancestor(element, function(n) {
261             return Y.Selector.test(n, selector);
262         }, testSelf);
263     }
264 };
265
266 Y.mix(Y.Selector, Selector, true);
267
268 })(Y);
269
270
271 }, '3.3.0' ,{requires:['dom-base']});