Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('yui2-selector', function(Y) {
2
    var YAHOO    = Y.YUI2;
3
    /*
4
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
5
Code licensed under the BSD License:
6
http://developer.yahoo.com/yui/license.html
7
version: 2.9.0
8
*/
9
var Y = YAHOO,
10
    Y_DOM = YAHOO.util.Dom,
11
    EMPTY_ARRAY = [],
12
    Y_UA = Y.env.ua,
13
    Y_Lang = Y.lang,
14
    Y_DOC = document,
15
    Y_DOCUMENT_ELEMENT = Y_DOC.documentElement,
16
 
17
    Y_DOM_inDoc = Y_DOM.inDocument,
18
    Y_mix = Y_Lang.augmentObject,
19
    Y_guid = Y_DOM.generateId,
20
 
21
    Y_getDoc = function(element) {
22
        var doc = Y_DOC;
23
        if (element) {
24
            doc = (element.nodeType === 9) ? element : // element === document
25
                element.ownerDocument || // element === DOM node
26
                element.document || // element === window
27
                Y_DOC; // default
28
        }
29
 
30
        return doc;
31
    },
32
 
33
    Y_Array = function(o, startIdx) {
34
        var l, a, start = startIdx || 0;
35
 
36
        // IE errors when trying to slice HTMLElement collections
37
        try {
38
            return Array.prototype.slice.call(o, start);
39
        } catch (e) {
40
            a = [];
41
            l = o.length;
42
            for (; start < l; start++) {
43
                a.push(o[start]);
44
            }
45
            return a;
46
        }
47
    },
48
 
49
    Y_DOM_allById = function(id, root) {
50
        root = root || Y_DOC;
51
        var nodes = [],
52
            ret = [],
53
            i,
54
            node;
55
 
56
        if (root.querySelectorAll) {
57
            ret = root.querySelectorAll('[id="' + id + '"]');
58
        } else if (root.all) {
59
            nodes = root.all(id);
60
 
61
            if (nodes) {
62
                // root.all may return HTMLElement or HTMLCollection.
63
                // some elements are also HTMLCollection (FORM, SELECT).
64
                if (nodes.nodeName) {
65
                    if (nodes.id === id) { // avoid false positive on name
66
                        ret.push(nodes);
67
                        nodes = EMPTY_ARRAY; // done, no need to filter
68
                    } else { //  prep for filtering
69
                        nodes = [nodes];
70
                    }
71
                }
72
 
73
                if (nodes.length) {
74
                    // filter out matches on node.name
75
                    // and element.id as reference to element with id === 'id'
76
                    for (i = 0; node = nodes[i++];) {
77
                        if (node.id === id  ||
78
                                (node.attributes && node.attributes.id &&
79
                                node.attributes.id.value === id)) {
80
                            ret.push(node);
81
                        }
82
                    }
83
                }
84
            }
85
        } else {
86
            ret = [Y_getDoc(root).getElementById(id)];
87
        }
88
 
89
        return ret;
90
    };
91
 
92
/**
93
 * The selector-native module provides support for native querySelector
94
 * @module dom
95
 * @submodule selector-native
96
 * @for Selector
97
 */
98
 
99
/**
100
 * Provides support for using CSS selectors to query the DOM
101
 * @class Selector
102
 * @static
103
 * @for Selector
104
 */
105
 
106
var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
107
    OWNER_DOCUMENT = 'ownerDocument',
108
 
109
Selector = {
110
    _foundCache: [],
111
 
112
    useNative: true,
113
 
114
    _compare: ('sourceIndex' in Y_DOCUMENT_ELEMENT) ?
115
        function(nodeA, nodeB) {
116
            var a = nodeA.sourceIndex,
117
                b = nodeB.sourceIndex;
118
 
119
            if (a === b) {
120
                return 0;
121
            } else if (a > b) {
122
                return 1;
123
            }
124
 
125
            return -1;
126
 
127
        } : (Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION] ?
128
        function(nodeA, nodeB) {
129
            if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
130
                return -1;
131
            } else {
132
                return 1;
133
            }
134
        } :
135
        function(nodeA, nodeB) {
136
            var rangeA, rangeB, compare;
137
            if (nodeA && nodeB) {
138
                rangeA = nodeA[OWNER_DOCUMENT].createRange();
139
                rangeA.setStart(nodeA, 0);
140
                rangeB = nodeB[OWNER_DOCUMENT].createRange();
141
                rangeB.setStart(nodeB, 0);
142
                compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
143
            }
144
 
145
            return compare;
146
 
147
    }),
148
 
149
    _sort: function(nodes) {
150
        if (nodes) {
151
            nodes = Y_Array(nodes, 0, true);
152
            if (nodes.sort) {
153
                nodes.sort(Selector._compare);
154
            }
155
        }
156
 
157
        return nodes;
158
    },
159
 
160
    _deDupe: function(nodes) {
161
        var ret = [],
162
            i, node;
163
 
164
        for (i = 0; (node = nodes[i++]);) {
165
            if (!node._found) {
166
                ret[ret.length] = node;
167
                node._found = true;
168
            }
169
        }
170
 
171
        for (i = 0; (node = ret[i++]);) {
172
            node._found = null;
173
            node.removeAttribute('_found');
174
        }
175
 
176
        return ret;
177
    },
178
 
179
    /**
180
     * Retrieves a set of nodes based on a given CSS selector.
181
     * @method query
182
     *
183
     * @param {string} selector The CSS Selector to test the node against.
184
     * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
185
     * @param {Boolean} firstOnly optional Whether or not to return only the first match.
186
     * @return {Array} An array of nodes that match the given selector.
187
     * @static
188
     */
189
    query: function(selector, root, firstOnly, skipNative) {
190
        if (root && typeof root == 'string') {
191
            root = Y_DOM.get(root);
192
            if (!root) {
193
                return (firstOnly) ? null : [];
194
            }
195
        } else {
196
            root = root || Y_DOC;
197
        }
198
 
199
        var ret = [],
200
            useNative = (Selector.useNative && Y_DOC.querySelector && !skipNative),
201
            queries = [[selector, root]],
202
            query,
203
            result,
204
            i,
205
            fn = (useNative) ? Selector._nativeQuery : Selector._bruteQuery;
206
 
207
        if (selector && fn) {
208
            // split group into seperate queries
209
            if (!skipNative && // already done if skipping
210
                    (!useNative || root.tagName)) { // split native when element scoping is needed
211
                queries = Selector._splitQueries(selector, root);
212
            }
213
 
214
            for (i = 0; (query = queries[i++]);) {
215
                result = fn(query[0], query[1], firstOnly);
216
                if (!firstOnly) { // coerce DOM Collection to Array
217
                    result = Y_Array(result, 0, true);
218
                }
219
                if (result) {
220
                    ret = ret.concat(result);
221
                }
222
            }
223
 
224
            if (queries.length > 1) { // remove dupes and sort by doc order
225
                ret = Selector._sort(Selector._deDupe(ret));
226
            }
227
        }
228
 
229
        YAHOO.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
230
        return (firstOnly) ? (ret[0] || null) : ret;
231
 
232
    },
233
 
234
    // allows element scoped queries to begin with combinator
235
    // e.g. query('> p', document.body) === query('body > p')
236
    _splitQueries: function(selector, node) {
237
        var groups = selector.split(','),
238
            queries = [],
239
            prefix = '',
240
            i, len;
241
 
242
        if (node) {
243
            // enforce for element scoping
244
            if (node.tagName) {
245
                node.id = node.id || Y_guid();
246
                prefix = '[id="' + node.id + '"] ';
247
            }
248
 
249
            for (i = 0, len = groups.length; i < len; ++i) {
250
                selector =  prefix + groups[i];
251
                queries.push([selector, node]);
252
            }
253
        }
254
 
255
        return queries;
256
    },
257
 
258
    _nativeQuery: function(selector, root, one) {
259
        if (Y_UA.webkit && selector.indexOf(':checked') > -1 &&
260
                (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
261
            return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
262
        }
263
        try {
264
            //YAHOO.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
            //YAHOO.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
268
            return Selector.query(selector, root, one, true); // redo with skipNative true
269
        }
270
    },
271
 
272
    filter: function(nodes, selector) {
273
        var ret = [],
274
            i, node;
275
 
276
        if (nodes && selector) {
277
            for (i = 0; (node = nodes[i++]);) {
278
                if (Selector.test(node, selector)) {
279
                    ret[ret.length] = node;
280
                }
281
            }
282
        } else {
283
            YAHOO.log('invalid filter input (nodes: ' + nodes +
284
                    ', selector: ' + selector + ')', 'warn', 'Selector');
285
        }
286
 
287
        return ret;
288
    },
289
 
290
    test: function(node, selector, root) {
291
        var ret = false,
292
            groups = selector.split(','),
293
            useFrag = false,
294
            parent,
295
            item,
296
            items,
297
            frag,
298
            i, j, group;
299
 
300
        if (node && node.tagName) { // only test HTMLElements
301
 
302
            // we need a root if off-doc
303
            if (!root && !Y_DOM_inDoc(node)) {
304
                parent = node.parentNode;
305
                if (parent) {
306
                    root = parent;
307
                } else { // only use frag when no parent to query
308
                    frag = node[OWNER_DOCUMENT].createDocumentFragment();
309
                    frag.appendChild(node);
310
                    root = frag;
311
                    useFrag = true;
312
                }
313
            }
314
            root = root || node[OWNER_DOCUMENT];
315
 
316
            if (!node.id) {
317
                node.id = Y_guid();
318
            }
319
            for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
320
                group += '[id="' + node.id + '"]';
321
                items = Selector.query(group, root);
322
 
323
                for (j = 0; item = items[j++];) {
324
                    if (item === node) {
325
                        ret = true;
326
                        break;
327
                    }
328
                }
329
                if (ret) {
330
                    break;
331
                }
332
            }
333
 
334
            if (useFrag) { // cleanup
335
                frag.removeChild(node);
336
            }
337
        }
338
 
339
        return ret;
340
    }
341
 
342
};
343
 
344
YAHOO.util.Selector = Selector;
345
/**
346
 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
347
 * @module dom
348
 * @submodule selector-css2
349
 * @for Selector
350
 */
351
 
352
/**
353
 * Provides helper methods for collecting and filtering DOM elements.
354
 */
355
 
356
var PARENT_NODE = 'parentNode',
357
    TAG_NAME = 'tagName',
358
    ATTRIBUTES = 'attributes',
359
    COMBINATOR = 'combinator',
360
    PSEUDOS = 'pseudos',
361
 
362
    SelectorCSS2 = {
363
        _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
364
        SORT_RESULTS: true,
365
        _children: function(node, tag) {
366
            var ret = node.children,
367
                i,
368
                children = [],
369
                childNodes,
370
                child;
371
 
372
            if (node.children && tag && node.children.tags) {
373
                children = node.children.tags(tag);
374
            } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
375
                childNodes = ret || node.childNodes;
376
                ret = [];
377
                for (i = 0; (child = childNodes[i++]);) {
378
                    if (child.tagName) {
379
                        if (!tag || tag === child.tagName) {
380
                            ret.push(child);
381
                        }
382
                    }
383
                }
384
            }
385
 
386
            return ret || [];
387
        },
388
 
389
        _re: {
390
            //attr: /(\[.*\])/g,
391
            attr: /(\[[^\]]*\])/g,
392
            //esc: /\\[:\[][\w\d\]]*/gi,
393
            esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
394
            //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i
395
            pseudos: /(\([^\)]*\))/g
396
        },
397
 
398
        /**
399
         * Mapping of shorthand tokens to corresponding attribute selector
400
         * @property shorthand
401
         * @type object
402
         */
403
        shorthand: {
404
            //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]',
405
            '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]',
406
            //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]',
407
            //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]'
408
            '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
409
        },
410
 
411
        /**
412
         * List of operators and corresponding boolean functions.
413
         * These functions are passed the attribute and the current node's value of the attribute.
414
         * @property operators
415
         * @type object
416
         */
417
        operators: {
418
            '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute
419
            //'': '.+',
420
            //'=': '^{val}$', // equality
421
            '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
422
            '|=': '^{val}(?:-|$)' // optional hyphen-delimited
423
        },
424
 
425
        pseudos: {
426
           'first-child': function(node) {
427
                return Selector._children(node[PARENT_NODE])[0] === node;
428
            }
429
        },
430
 
431
        _bruteQuery: function(selector, root, firstOnly) {
432
            var ret = [],
433
                nodes = [],
434
                tokens = Selector._tokenize(selector),
435
                token = tokens[tokens.length - 1],
436
                rootDoc = Y_getDoc(root),
437
                child,
438
                id,
439
                className,
440
                tagName;
441
 
442
 
443
            // if we have an initial ID, set to root when in document
444
            /*
445
            if (tokens[0] && rootDoc === root &&
446
                    (id = tokens[0].id) &&
447
                    rootDoc.getElementById(id)) {
448
                root = rootDoc.getElementById(id);
449
            }
450
            */
451
 
452
            if (token) {
453
                // prefilter nodes
454
                id = token.id;
455
                className = token.className;
456
                tagName = token.tagName || '*';
457
 
458
                if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
459
                    // try ID first, unless no root.all && root not in document
460
                    // (root.all works off document, but not getElementById)
461
                    // TODO: move to allById?
462
                    if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) {
463
                        nodes = Y_DOM_allById(id, root);
464
                    // try className
465
                    } else if (className) {
466
                        nodes = root.getElementsByClassName(className);
467
                    } else { // default to tagName
468
                        nodes = root.getElementsByTagName(tagName);
469
                    }
470
 
471
                } else { // brute getElementsByTagName('*')
472
                    child = root.firstChild;
473
                    while (child) {
474
                        if (child.tagName) { // only collect HTMLElements
475
                            nodes.push(child);
476
                        }
477
                        child = child.nextSilbing || child.firstChild;
478
                    }
479
                }
480
                if (nodes.length) {
481
                    ret = Selector._filterNodes(nodes, tokens, firstOnly);
482
                }
483
            }
484
 
485
            return ret;
486
        },
487
 
488
        _filterNodes: function(nodes, tokens, firstOnly) {
489
            var i = 0,
490
                j,
491
                len = tokens.length,
492
                n = len - 1,
493
                result = [],
494
                node = nodes[0],
495
                tmpNode = node,
496
                getters = Selector.getters,
497
                operator,
498
                combinator,
499
                token,
500
                path,
501
                pass,
502
                //FUNCTION = 'function',
503
                value,
504
                tests,
505
                test;
506
 
507
            //do {
508
            for (i = 0; (tmpNode = node = nodes[i++]);) {
509
                n = len - 1;
510
                path = null;
511
 
512
                testLoop:
513
                while (tmpNode && tmpNode.tagName) {
514
                    token = tokens[n];
515
                    tests = token.tests;
516
                    j = tests.length;
517
                    if (j && !pass) {
518
                        while ((test = tests[--j])) {
519
                            operator = test[1];
520
                            if (getters[test[0]]) {
521
                                value = getters[test[0]](tmpNode, test[0]);
522
                            } else {
523
                                value = tmpNode[test[0]];
524
                                // use getAttribute for non-standard attributes
525
                                if (value === undefined && tmpNode.getAttribute) {
526
                                    value = tmpNode.getAttribute(test[0]);
527
                                }
528
                            }
529
 
530
                            if ((operator === '=' && value !== test[2]) ||  // fast path for equality
531
                                (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
532
                                operator.test && !operator.test(value)) ||  // regex test
533
                                (!operator.test && // protect against RegExp as function (webkit)
534
                                        typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
535
 
536
                                // skip non element nodes or non-matching tags
537
                                if ((tmpNode = tmpNode[path])) {
538
                                    while (tmpNode &&
539
                                        (!tmpNode.tagName ||
540
                                            (token.tagName && token.tagName !== tmpNode.tagName))
541
                                    ) {
542
                                        tmpNode = tmpNode[path];
543
                                    }
544
                                }
545
                                continue testLoop;
546
                            }
547
                        }
548
                    }
549
 
550
                    n--; // move to next token
551
                    // now that we've passed the test, move up the tree by combinator
552
                    if (!pass && (combinator = token.combinator)) {
553
                        path = combinator.axis;
554
                        tmpNode = tmpNode[path];
555
 
556
                        // skip non element nodes
557
                        while (tmpNode && !tmpNode.tagName) {
558
                            tmpNode = tmpNode[path];
559
                        }
560
 
561
                        if (combinator.direct) { // one pass only
562
                            path = null;
563
                        }
564
 
565
                    } else { // success if we made it this far
566
                        result.push(node);
567
                        if (firstOnly) {
568
                            return result;
569
                        }
570
                        break;
571
                    }
572
                }
573
            }// while (tmpNode = node = nodes[++i]);
574
            node = tmpNode = null;
575
            return result;
576
        },
577
 
578
        combinators: {
579
            ' ': {
580
                axis: 'parentNode'
581
            },
582
 
583
            '>': {
584
                axis: 'parentNode',
585
                direct: true
586
            },
587
 
588
 
589
            '+': {
590
                axis: 'previousSibling',
591
                direct: true
592
            }
593
        },
594
 
595
        _parsers: [
596
            {
597
                name: ATTRIBUTES,
598
                //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
599
                re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
600
                fn: function(match, token) {
601
                    var operator = match[2] || '',
602
                        operators = Selector.operators,
603
                        escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
604
                        test;
605
 
606
                    // add prefiltering for ID and CLASS
607
                    if ((match[1] === 'id' && operator === '=') ||
608
                            (match[1] === 'className' &&
609
                            Y_DOCUMENT_ELEMENT.getElementsByClassName &&
610
                            (operator === '~=' || operator === '='))) {
611
                        token.prefilter = match[1];
612
 
613
 
614
                        match[3] = escVal;
615
 
616
                        // escape all but ID for prefilter, which may run through QSA (via Dom.allById)
617
                        token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
618
 
619
                    }
620
 
621
                    // add tests
622
                    if (operator in operators) {
623
                        test = operators[operator];
624
                        if (typeof test === 'string') {
625
                            match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
626
                            test = new RegExp(test.replace('{val}', match[3]));
627
                        }
628
                        match[2] = test;
629
                    }
630
                    if (!token.last || token.prefilter !== match[1]) {
631
                        return match.slice(1);
632
                    }
633
                }
634
 
635
            },
636
            {
637
                name: TAG_NAME,
638
                re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
639
                fn: function(match, token) {
640
                    var tag = match[1].toUpperCase();
641
                    token.tagName = tag;
642
 
643
                    if (tag !== '*' && (!token.last || token.prefilter)) {
644
                        return [TAG_NAME, '=', tag];
645
                    }
646
                    if (!token.prefilter) {
647
                        token.prefilter = 'tagName';
648
                    }
649
                }
650
            },
651
            {
652
                name: COMBINATOR,
653
                re: /^\s*([>+~]|\s)\s*/,
654
                fn: function(match, token) {
655
                }
656
            },
657
            {
658
                name: PSEUDOS,
659
                re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
660
                fn: function(match, token) {
661
                    var test = Selector[PSEUDOS][match[1]];
662
                    if (test) { // reorder match array and unescape special chars for tests
663
                        if (match[2]) {
664
                            match[2] = match[2].replace(/\\/g, '');
665
                        }
666
                        return [match[2], test];
667
                    } else { // selector token not supported (possibly missing CSS3 module)
668
                        return false;
669
                    }
670
                }
671
            }
672
            ],
673
 
674
        _getToken: function(token) {
675
            return {
676
                tagName: null,
677
                id: null,
678
                className: null,
679
                attributes: {},
680
                combinator: null,
681
                tests: []
682
            };
683
        },
684
 
685
        /**
686
            Break selector into token units per simple selector.
687
            Combinator is attached to the previous token.
688
         */
689
        _tokenize: function(selector) {
690
            selector = selector || '';
691
            selector = Selector._replaceShorthand(Y_Lang.trim(selector));
692
            var token = Selector._getToken(),     // one token per simple selector (left selector holds combinator)
693
                query = selector, // original query for debug report
694
                tokens = [],    // array of tokens
695
                found = false,  // whether or not any matches were found this pass
696
                match,         // the regex match
697
                test,
698
                i, parser;
699
 
700
            /*
701
                Search for selector patterns, store, and strip them from the selector string
702
                until no patterns match (invalid selector) or we run out of chars.
703
 
704
                Multiple attributes and pseudos are allowed, in any order.
705
                for example:
706
                    'form:first-child[type=button]:not(button)[lang|=en]'
707
            */
708
 
709
            outer:
710
            do {
711
                found = false; // reset after full pass
712
 
713
                for (i = 0; (parser = Selector._parsers[i++]);) {
714
                    if ( (match = parser.re.exec(selector)) ) { // note assignment
715
                        if (parser.name !== COMBINATOR ) {
716
                            token.selector = selector;
717
                        }
718
                        selector = selector.replace(match[0], ''); // strip current match from selector
719
                        if (!selector.length) {
720
                            token.last = true;
721
                        }
722
 
723
                        if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
724
                            match[1] = Selector._attrFilters[match[1]];
725
                        }
726
 
727
                        test = parser.fn(match, token);
728
                        if (test === false) { // selector not supported
729
                            found = false;
730
                            break outer;
731
                        } else if (test) {
732
                            token.tests.push(test);
733
                        }
734
 
735
                        if (!selector.length || parser.name === COMBINATOR) {
736
                            tokens.push(token);
737
                            token = Selector._getToken(token);
738
                            if (parser.name === COMBINATOR) {
739
                                token.combinator = Selector.combinators[match[1]];
740
                            }
741
                        }
742
                        found = true;
743
 
744
 
745
                    }
746
                }
747
            } while (found && selector.length);
748
 
749
            if (!found || selector.length) { // not fully parsed
750
                YAHOO.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
751
                tokens = [];
752
            }
753
            return tokens;
754
        },
755
 
756
        _replaceShorthand: function(selector) {
757
            var shorthand = Selector.shorthand,
758
                esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
759
                attrs,
760
                pseudos,
761
                re, i, len;
762
 
763
            if (esc) {
764
                selector = selector.replace(Selector._re.esc, '\uE000');
765
            }
766
 
767
            attrs = selector.match(Selector._re.attr);
768
            pseudos = selector.match(Selector._re.pseudos);
769
 
770
            if (attrs) {
771
                selector = selector.replace(Selector._re.attr, '\uE001');
772
            }
773
 
774
            if (pseudos) {
775
                selector = selector.replace(Selector._re.pseudos, '\uE002');
776
            }
777
 
778
 
779
            for (re in shorthand) {
780
                if (shorthand.hasOwnProperty(re)) {
781
                    selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
782
                }
783
            }
784
 
785
            if (attrs) {
786
                for (i = 0, len = attrs.length; i < len; ++i) {
787
                    selector = selector.replace(/\uE001/, attrs[i]);
788
                }
789
            }
790
 
791
            if (pseudos) {
792
                for (i = 0, len = pseudos.length; i < len; ++i) {
793
                    selector = selector.replace(/\uE002/, pseudos[i]);
794
                }
795
            }
796
 
797
            selector = selector.replace(/\[/g, '\uE003');
798
            selector = selector.replace(/\]/g, '\uE004');
799
 
800
            selector = selector.replace(/\(/g, '\uE005');
801
            selector = selector.replace(/\)/g, '\uE006');
802
 
803
            if (esc) {
804
                for (i = 0, len = esc.length; i < len; ++i) {
805
                    selector = selector.replace('\uE000', esc[i]);
806
                }
807
            }
808
 
809
            return selector;
810
        },
811
 
812
        _attrFilters: {
813
            'class': 'className',
814
            'for': 'htmlFor'
815
        },
816
 
817
        getters: {
818
            href: function(node, attr) {
819
                return Y_DOM.getAttribute(node, attr);
820
            }
821
        }
822
    };
823
 
824
Y_mix(Selector, SelectorCSS2, true);
825
Selector.getters.src = Selector.getters.rel = Selector.getters.href;
826
 
827
// IE wants class with native queries
828
if (Selector.useNative && Y_DOC.querySelector) {
829
    Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]';
830
}
831
 
832
/**
833
 * The selector css3 module provides support for css3 selectors.
834
 * @module dom
835
 * @submodule selector-css3
836
 * @for Selector
837
 */
838
 
839
/*
840
    an+b = get every _a_th node starting at the _b_th
841
    0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
842
    1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
843
    an+0 = get every _a_th element, "0" may be omitted
844
*/
845
 
846
Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
847
 
848
Selector._getNth = function(node, expr, tag, reverse) {
849
    Selector._reNth.test(expr);
850
    var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
851
        n = RegExp.$2, // "n"
852
        oddeven = RegExp.$3, // "odd" or "even"
853
        b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
854
        result = [],
855
        siblings = Selector._children(node.parentNode, tag),
856
        op;
857
 
858
    if (oddeven) {
859
        a = 2; // always every other
860
        op = '+';
861
        n = 'n';
862
        b = (oddeven === 'odd') ? 1 : 0;
863
    } else if ( isNaN(a) ) {
864
        a = (n) ? 1 : 0; // start from the first or no repeat
865
    }
866
 
867
    if (a === 0) { // just the first
868
        if (reverse) {
869
            b = siblings.length - b + 1;
870
        }
871
 
872
        if (siblings[b - 1] === node) {
873
            return true;
874
        } else {
875
            return false;
876
        }
877
 
878
    } else if (a < 0) {
879
        reverse = !!reverse;
880
        a = Math.abs(a);
881
    }
882
 
883
    if (!reverse) {
884
        for (var i = b - 1, len = siblings.length; i < len; i += a) {
885
            if ( i >= 0 && siblings[i] === node ) {
886
                return true;
887
            }
888
        }
889
    } else {
890
        for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
891
            if ( i < len && siblings[i] === node ) {
892
                return true;
893
            }
894
        }
895
    }
896
    return false;
897
};
898
 
899
Y_mix(Selector.pseudos, {
900
    'root': function(node) {
901
        return node === node.ownerDocument.documentElement;
902
    },
903
 
904
    'nth-child': function(node, expr) {
905
        return Selector._getNth(node, expr);
906
    },
907
 
908
    'nth-last-child': function(node, expr) {
909
        return Selector._getNth(node, expr, null, true);
910
    },
911
 
912
    'nth-of-type': function(node, expr) {
913
        return Selector._getNth(node, expr, node.tagName);
914
    },
915
 
916
    'nth-last-of-type': function(node, expr) {
917
        return Selector._getNth(node, expr, node.tagName, true);
918
    },
919
 
920
    'last-child': function(node) {
921
        var children = Selector._children(node.parentNode);
922
        return children[children.length - 1] === node;
923
    },
924
 
925
    'first-of-type': function(node) {
926
        return Selector._children(node.parentNode, node.tagName)[0] === node;
927
    },
928
 
929
    'last-of-type': function(node) {
930
        var children = Selector._children(node.parentNode, node.tagName);
931
        return children[children.length - 1] === node;
932
    },
933
 
934
    'only-child': function(node) {
935
        var children = Selector._children(node.parentNode);
936
        return children.length === 1 && children[0] === node;
937
    },
938
 
939
    'only-of-type': function(node) {
940
        var children = Selector._children(node.parentNode, node.tagName);
941
        return children.length === 1 && children[0] === node;
942
    },
943
 
944
    'empty': function(node) {
945
        return node.childNodes.length === 0;
946
    },
947
 
948
    'not': function(node, expr) {
949
        return !Selector.test(node, expr);
950
    },
951
 
952
    'contains': function(node, expr) {
953
        var text = node.innerText || node.textContent || '';
954
        return text.indexOf(expr) > -1;
955
    },
956
 
957
    'checked': function(node) {
958
        return (node.checked === true || node.selected === true);
959
    },
960
 
961
    enabled: function(node) {
962
        return (node.disabled !== undefined && !node.disabled);
963
    },
964
 
965
    disabled: function(node) {
966
        return (node.disabled);
967
    }
968
});
969
 
970
Y_mix(Selector.operators, {
971
    '^=': '^{val}', // Match starts with value
972
    '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value
973
    '$=': '{val}$', // Match ends with value
974
    '*=': '{val}' // Match contains value as substring
975
});
976
 
977
Selector.combinators['~'] = {
978
    axis: 'previousSibling'
979
};
980
YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"});
981
 
982
}, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom"]});