~andreserl/maas/packaging_precise_rebase

« back to all changes in this revision

Viewing changes to debian/extras/jslibs/yui/selector-native/selector-native.js

  • Committer: Andres Rodriguez
  • Date: 2013-03-20 18:12:30 UTC
  • mfrom: (145.2.22 precise.sru)
  • Revision ID: andreserl@ubuntu.com-20130320181230-6l5guc0nhlv2z4p7
Re-base againts latest quantal released branch towards SRU

Show diffs side-by-side

added added

removed removed

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