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
        return (firstOnly) ? (ret[0] || null) : ret;
230
 
231
    },
232
 
233
    // allows element scoped queries to begin with combinator
234
    // e.g. query('> p', document.body) === query('body > p')
235
    _splitQueries: function(selector, node) {
236
        var groups = selector.split(','),
237
            queries = [],
238
            prefix = '',
239
            i, len;
240
 
241
        if (node) {
242
            // enforce for element scoping
243
            if (node.tagName) {
244
                node.id = node.id || Y_guid();
245
                prefix = '[id="' + node.id + '"] ';
246
            }
247
 
248
            for (i = 0, len = groups.length; i < len; ++i) {
249
                selector =  prefix + groups[i];
250
                queries.push([selector, node]);
251
            }
252
        }
253
 
254
        return queries;
255
    },
256
 
257
    _nativeQuery: function(selector, root, one) {
258
        if (Y_UA.webkit && selector.indexOf(':checked') > -1 &&
259
                (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
260
            return 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 Selector.query(selector, root, one, true); // redo with skipNative true
266
        }
267
    },
268
 
269
    filter: function(nodes, selector) {
270
        var ret = [],
271
            i, node;
272
 
273
        if (nodes && selector) {
274
            for (i = 0; (node = nodes[i++]);) {
275
                if (Selector.test(node, selector)) {
276
                    ret[ret.length] = node;
277
                }
278
            }
279
        } else {
280
        }
281
 
282
        return ret;
283
    },
284
 
285
    test: function(node, selector, root) {
286
        var ret = false,
287
            groups = selector.split(','),
288
            useFrag = false,
289
            parent,
290
            item,
291
            items,
292
            frag,
293
            i, j, group;
294
 
295
        if (node && node.tagName) { // only test HTMLElements
296
 
297
            // we need a root if off-doc
298
            if (!root && !Y_DOM_inDoc(node)) {
299
                parent = node.parentNode;
300
                if (parent) {
301
                    root = parent;
302
                } else { // only use frag when no parent to query
303
                    frag = node[OWNER_DOCUMENT].createDocumentFragment();
304
                    frag.appendChild(node);
305
                    root = frag;
306
                    useFrag = true;
307
                }
308
            }
309
            root = root || node[OWNER_DOCUMENT];
310
 
311
            if (!node.id) {
312
                node.id = Y_guid();
313
            }
314
            for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
315
                group += '[id="' + node.id + '"]';
316
                items = Selector.query(group, root);
317
 
318
                for (j = 0; item = items[j++];) {
319
                    if (item === node) {
320
                        ret = true;
321
                        break;
322
                    }
323
                }
324
                if (ret) {
325
                    break;
326
                }
327
            }
328
 
329
            if (useFrag) { // cleanup
330
                frag.removeChild(node);
331
            }
332
        }
333
 
334
        return ret;
335
    }
336
 
337
};
338
 
339
YAHOO.util.Selector = Selector;
340
/**
341
 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
342
 * @module dom
343
 * @submodule selector-css2
344
 * @for Selector
345
 */
346
 
347
/**
348
 * Provides helper methods for collecting and filtering DOM elements.
349
 */
350
 
351
var PARENT_NODE = 'parentNode',
352
    TAG_NAME = 'tagName',
353
    ATTRIBUTES = 'attributes',
354
    COMBINATOR = 'combinator',
355
    PSEUDOS = 'pseudos',
356
 
357
    SelectorCSS2 = {
358
        _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
359
        SORT_RESULTS: true,
360
        _children: function(node, tag) {
361
            var ret = node.children,
362
                i,
363
                children = [],
364
                childNodes,
365
                child;
366
 
367
            if (node.children && tag && node.children.tags) {
368
                children = node.children.tags(tag);
369
            } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
370
                childNodes = ret || node.childNodes;
371
                ret = [];
372
                for (i = 0; (child = childNodes[i++]);) {
373
                    if (child.tagName) {
374
                        if (!tag || tag === child.tagName) {
375
                            ret.push(child);
376
                        }
377
                    }
378
                }
379
            }
380
 
381
            return ret || [];
382
        },
383
 
384
        _re: {
385
            //attr: /(\[.*\])/g,
386
            attr: /(\[[^\]]*\])/g,
387
            //esc: /\\[:\[][\w\d\]]*/gi,
388
            esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
389
            //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i
390
            pseudos: /(\([^\)]*\))/g
391
        },
392
 
393
        /**
394
         * Mapping of shorthand tokens to corresponding attribute selector
395
         * @property shorthand
396
         * @type object
397
         */
398
        shorthand: {
399
            //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]',
400
            '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]',
401
            //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]',
402
            //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]'
403
            '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
404
        },
405
 
406
        /**
407
         * List of operators and corresponding boolean functions.
408
         * These functions are passed the attribute and the current node's value of the attribute.
409
         * @property operators
410
         * @type object
411
         */
412
        operators: {
413
            '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute
414
            //'': '.+',
415
            //'=': '^{val}$', // equality
416
            '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
417
            '|=': '^{val}(?:-|$)' // optional hyphen-delimited
418
        },
419
 
420
        pseudos: {
421
           'first-child': function(node) {
422
                return Selector._children(node[PARENT_NODE])[0] === node;
423
            }
424
        },
425
 
426
        _bruteQuery: function(selector, root, firstOnly) {
427
            var ret = [],
428
                nodes = [],
429
                tokens = Selector._tokenize(selector),
430
                token = tokens[tokens.length - 1],
431
                rootDoc = Y_getDoc(root),
432
                child,
433
                id,
434
                className,
435
                tagName;
436
 
437
 
438
            // if we have an initial ID, set to root when in document
439
            /*
440
            if (tokens[0] && rootDoc === root &&
441
                    (id = tokens[0].id) &&
442
                    rootDoc.getElementById(id)) {
443
                root = rootDoc.getElementById(id);
444
            }
445
            */
446
 
447
            if (token) {
448
                // prefilter nodes
449
                id = token.id;
450
                className = token.className;
451
                tagName = token.tagName || '*';
452
 
453
                if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
454
                    // try ID first, unless no root.all && root not in document
455
                    // (root.all works off document, but not getElementById)
456
                    // TODO: move to allById?
457
                    if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) {
458
                        nodes = Y_DOM_allById(id, root);
459
                    // try className
460
                    } else if (className) {
461
                        nodes = root.getElementsByClassName(className);
462
                    } else { // default to tagName
463
                        nodes = root.getElementsByTagName(tagName);
464
                    }
465
 
466
                } else { // brute getElementsByTagName('*')
467
                    child = root.firstChild;
468
                    while (child) {
469
                        if (child.tagName) { // only collect HTMLElements
470
                            nodes.push(child);
471
                        }
472
                        child = child.nextSilbing || child.firstChild;
473
                    }
474
                }
475
                if (nodes.length) {
476
                    ret = Selector._filterNodes(nodes, tokens, firstOnly);
477
                }
478
            }
479
 
480
            return ret;
481
        },
482
 
483
        _filterNodes: function(nodes, tokens, firstOnly) {
484
            var i = 0,
485
                j,
486
                len = tokens.length,
487
                n = len - 1,
488
                result = [],
489
                node = nodes[0],
490
                tmpNode = node,
491
                getters = Selector.getters,
492
                operator,
493
                combinator,
494
                token,
495
                path,
496
                pass,
497
                //FUNCTION = 'function',
498
                value,
499
                tests,
500
                test;
501
 
502
            //do {
503
            for (i = 0; (tmpNode = node = nodes[i++]);) {
504
                n = len - 1;
505
                path = null;
506
 
507
                testLoop:
508
                while (tmpNode && tmpNode.tagName) {
509
                    token = tokens[n];
510
                    tests = token.tests;
511
                    j = tests.length;
512
                    if (j && !pass) {
513
                        while ((test = tests[--j])) {
514
                            operator = test[1];
515
                            if (getters[test[0]]) {
516
                                value = getters[test[0]](tmpNode, test[0]);
517
                            } else {
518
                                value = tmpNode[test[0]];
519
                                // use getAttribute for non-standard attributes
520
                                if (value === undefined && tmpNode.getAttribute) {
521
                                    value = tmpNode.getAttribute(test[0]);
522
                                }
523
                            }
524
 
525
                            if ((operator === '=' && value !== test[2]) ||  // fast path for equality
526
                                (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
527
                                operator.test && !operator.test(value)) ||  // regex test
528
                                (!operator.test && // protect against RegExp as function (webkit)
529
                                        typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
530
 
531
                                // skip non element nodes or non-matching tags
532
                                if ((tmpNode = tmpNode[path])) {
533
                                    while (tmpNode &&
534
                                        (!tmpNode.tagName ||
535
                                            (token.tagName && token.tagName !== tmpNode.tagName))
536
                                    ) {
537
                                        tmpNode = tmpNode[path];
538
                                    }
539
                                }
540
                                continue testLoop;
541
                            }
542
                        }
543
                    }
544
 
545
                    n--; // move to next token
546
                    // now that we've passed the test, move up the tree by combinator
547
                    if (!pass && (combinator = token.combinator)) {
548
                        path = combinator.axis;
549
                        tmpNode = tmpNode[path];
550
 
551
                        // skip non element nodes
552
                        while (tmpNode && !tmpNode.tagName) {
553
                            tmpNode = tmpNode[path];
554
                        }
555
 
556
                        if (combinator.direct) { // one pass only
557
                            path = null;
558
                        }
559
 
560
                    } else { // success if we made it this far
561
                        result.push(node);
562
                        if (firstOnly) {
563
                            return result;
564
                        }
565
                        break;
566
                    }
567
                }
568
            }// while (tmpNode = node = nodes[++i]);
569
            node = tmpNode = null;
570
            return result;
571
        },
572
 
573
        combinators: {
574
            ' ': {
575
                axis: 'parentNode'
576
            },
577
 
578
            '>': {
579
                axis: 'parentNode',
580
                direct: true
581
            },
582
 
583
 
584
            '+': {
585
                axis: 'previousSibling',
586
                direct: true
587
            }
588
        },
589
 
590
        _parsers: [
591
            {
592
                name: ATTRIBUTES,
593
                //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
594
                re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
595
                fn: function(match, token) {
596
                    var operator = match[2] || '',
597
                        operators = Selector.operators,
598
                        escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
599
                        test;
600
 
601
                    // add prefiltering for ID and CLASS
602
                    if ((match[1] === 'id' && operator === '=') ||
603
                            (match[1] === 'className' &&
604
                            Y_DOCUMENT_ELEMENT.getElementsByClassName &&
605
                            (operator === '~=' || operator === '='))) {
606
                        token.prefilter = match[1];
607
 
608
 
609
                        match[3] = escVal;
610
 
611
                        // escape all but ID for prefilter, which may run through QSA (via Dom.allById)
612
                        token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
613
 
614
                    }
615
 
616
                    // add tests
617
                    if (operator in operators) {
618
                        test = operators[operator];
619
                        if (typeof test === 'string') {
620
                            match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
621
                            test = new RegExp(test.replace('{val}', match[3]));
622
                        }
623
                        match[2] = test;
624
                    }
625
                    if (!token.last || token.prefilter !== match[1]) {
626
                        return match.slice(1);
627
                    }
628
                }
629
 
630
            },
631
            {
632
                name: TAG_NAME,
633
                re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
634
                fn: function(match, token) {
635
                    var tag = match[1].toUpperCase();
636
                    token.tagName = tag;
637
 
638
                    if (tag !== '*' && (!token.last || token.prefilter)) {
639
                        return [TAG_NAME, '=', tag];
640
                    }
641
                    if (!token.prefilter) {
642
                        token.prefilter = 'tagName';
643
                    }
644
                }
645
            },
646
            {
647
                name: COMBINATOR,
648
                re: /^\s*([>+~]|\s)\s*/,
649
                fn: function(match, token) {
650
                }
651
            },
652
            {
653
                name: PSEUDOS,
654
                re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
655
                fn: function(match, token) {
656
                    var test = Selector[PSEUDOS][match[1]];
657
                    if (test) { // reorder match array and unescape special chars for tests
658
                        if (match[2]) {
659
                            match[2] = match[2].replace(/\\/g, '');
660
                        }
661
                        return [match[2], test];
662
                    } else { // selector token not supported (possibly missing CSS3 module)
663
                        return false;
664
                    }
665
                }
666
            }
667
            ],
668
 
669
        _getToken: function(token) {
670
            return {
671
                tagName: null,
672
                id: null,
673
                className: null,
674
                attributes: {},
675
                combinator: null,
676
                tests: []
677
            };
678
        },
679
 
680
        /**
681
            Break selector into token units per simple selector.
682
            Combinator is attached to the previous token.
683
         */
684
        _tokenize: function(selector) {
685
            selector = selector || '';
686
            selector = Selector._replaceShorthand(Y_Lang.trim(selector));
687
            var token = Selector._getToken(),     // one token per simple selector (left selector holds combinator)
688
                query = selector, // original query for debug report
689
                tokens = [],    // array of tokens
690
                found = false,  // whether or not any matches were found this pass
691
                match,         // the regex match
692
                test,
693
                i, parser;
694
 
695
            /*
696
                Search for selector patterns, store, and strip them from the selector string
697
                until no patterns match (invalid selector) or we run out of chars.
698
 
699
                Multiple attributes and pseudos are allowed, in any order.
700
                for example:
701
                    'form:first-child[type=button]:not(button)[lang|=en]'
702
            */
703
 
704
            outer:
705
            do {
706
                found = false; // reset after full pass
707
 
708
                for (i = 0; (parser = Selector._parsers[i++]);) {
709
                    if ( (match = parser.re.exec(selector)) ) { // note assignment
710
                        if (parser.name !== COMBINATOR ) {
711
                            token.selector = selector;
712
                        }
713
                        selector = selector.replace(match[0], ''); // strip current match from selector
714
                        if (!selector.length) {
715
                            token.last = true;
716
                        }
717
 
718
                        if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
719
                            match[1] = Selector._attrFilters[match[1]];
720
                        }
721
 
722
                        test = parser.fn(match, token);
723
                        if (test === false) { // selector not supported
724
                            found = false;
725
                            break outer;
726
                        } else if (test) {
727
                            token.tests.push(test);
728
                        }
729
 
730
                        if (!selector.length || parser.name === COMBINATOR) {
731
                            tokens.push(token);
732
                            token = Selector._getToken(token);
733
                            if (parser.name === COMBINATOR) {
734
                                token.combinator = Selector.combinators[match[1]];
735
                            }
736
                        }
737
                        found = true;
738
 
739
 
740
                    }
741
                }
742
            } while (found && selector.length);
743
 
744
            if (!found || selector.length) { // not fully parsed
745
                tokens = [];
746
            }
747
            return tokens;
748
        },
749
 
750
        _replaceShorthand: function(selector) {
751
            var shorthand = Selector.shorthand,
752
                esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
753
                attrs,
754
                pseudos,
755
                re, i, len;
756
 
757
            if (esc) {
758
                selector = selector.replace(Selector._re.esc, '\uE000');
759
            }
760
 
761
            attrs = selector.match(Selector._re.attr);
762
            pseudos = selector.match(Selector._re.pseudos);
763
 
764
            if (attrs) {
765
                selector = selector.replace(Selector._re.attr, '\uE001');
766
            }
767
 
768
            if (pseudos) {
769
                selector = selector.replace(Selector._re.pseudos, '\uE002');
770
            }
771
 
772
 
773
            for (re in shorthand) {
774
                if (shorthand.hasOwnProperty(re)) {
775
                    selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
776
                }
777
            }
778
 
779
            if (attrs) {
780
                for (i = 0, len = attrs.length; i < len; ++i) {
781
                    selector = selector.replace(/\uE001/, attrs[i]);
782
                }
783
            }
784
 
785
            if (pseudos) {
786
                for (i = 0, len = pseudos.length; i < len; ++i) {
787
                    selector = selector.replace(/\uE002/, pseudos[i]);
788
                }
789
            }
790
 
791
            selector = selector.replace(/\[/g, '\uE003');
792
            selector = selector.replace(/\]/g, '\uE004');
793
 
794
            selector = selector.replace(/\(/g, '\uE005');
795
            selector = selector.replace(/\)/g, '\uE006');
796
 
797
            if (esc) {
798
                for (i = 0, len = esc.length; i < len; ++i) {
799
                    selector = selector.replace('\uE000', esc[i]);
800
                }
801
            }
802
 
803
            return selector;
804
        },
805
 
806
        _attrFilters: {
807
            'class': 'className',
808
            'for': 'htmlFor'
809
        },
810
 
811
        getters: {
812
            href: function(node, attr) {
813
                return Y_DOM.getAttribute(node, attr);
814
            }
815
        }
816
    };
817
 
818
Y_mix(Selector, SelectorCSS2, true);
819
Selector.getters.src = Selector.getters.rel = Selector.getters.href;
820
 
821
// IE wants class with native queries
822
if (Selector.useNative && Y_DOC.querySelector) {
823
    Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]';
824
}
825
 
826
/**
827
 * The selector css3 module provides support for css3 selectors.
828
 * @module dom
829
 * @submodule selector-css3
830
 * @for Selector
831
 */
832
 
833
/*
834
    an+b = get every _a_th node starting at the _b_th
835
    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
836
    1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
837
    an+0 = get every _a_th element, "0" may be omitted
838
*/
839
 
840
Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
841
 
842
Selector._getNth = function(node, expr, tag, reverse) {
843
    Selector._reNth.test(expr);
844
    var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
845
        n = RegExp.$2, // "n"
846
        oddeven = RegExp.$3, // "odd" or "even"
847
        b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
848
        result = [],
849
        siblings = Selector._children(node.parentNode, tag),
850
        op;
851
 
852
    if (oddeven) {
853
        a = 2; // always every other
854
        op = '+';
855
        n = 'n';
856
        b = (oddeven === 'odd') ? 1 : 0;
857
    } else if ( isNaN(a) ) {
858
        a = (n) ? 1 : 0; // start from the first or no repeat
859
    }
860
 
861
    if (a === 0) { // just the first
862
        if (reverse) {
863
            b = siblings.length - b + 1;
864
        }
865
 
866
        if (siblings[b - 1] === node) {
867
            return true;
868
        } else {
869
            return false;
870
        }
871
 
872
    } else if (a < 0) {
873
        reverse = !!reverse;
874
        a = Math.abs(a);
875
    }
876
 
877
    if (!reverse) {
878
        for (var i = b - 1, len = siblings.length; i < len; i += a) {
879
            if ( i >= 0 && siblings[i] === node ) {
880
                return true;
881
            }
882
        }
883
    } else {
884
        for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
885
            if ( i < len && siblings[i] === node ) {
886
                return true;
887
            }
888
        }
889
    }
890
    return false;
891
};
892
 
893
Y_mix(Selector.pseudos, {
894
    'root': function(node) {
895
        return node === node.ownerDocument.documentElement;
896
    },
897
 
898
    'nth-child': function(node, expr) {
899
        return Selector._getNth(node, expr);
900
    },
901
 
902
    'nth-last-child': function(node, expr) {
903
        return Selector._getNth(node, expr, null, true);
904
    },
905
 
906
    'nth-of-type': function(node, expr) {
907
        return Selector._getNth(node, expr, node.tagName);
908
    },
909
 
910
    'nth-last-of-type': function(node, expr) {
911
        return Selector._getNth(node, expr, node.tagName, true);
912
    },
913
 
914
    'last-child': function(node) {
915
        var children = Selector._children(node.parentNode);
916
        return children[children.length - 1] === node;
917
    },
918
 
919
    'first-of-type': function(node) {
920
        return Selector._children(node.parentNode, node.tagName)[0] === node;
921
    },
922
 
923
    'last-of-type': function(node) {
924
        var children = Selector._children(node.parentNode, node.tagName);
925
        return children[children.length - 1] === node;
926
    },
927
 
928
    'only-child': function(node) {
929
        var children = Selector._children(node.parentNode);
930
        return children.length === 1 && children[0] === node;
931
    },
932
 
933
    'only-of-type': function(node) {
934
        var children = Selector._children(node.parentNode, node.tagName);
935
        return children.length === 1 && children[0] === node;
936
    },
937
 
938
    'empty': function(node) {
939
        return node.childNodes.length === 0;
940
    },
941
 
942
    'not': function(node, expr) {
943
        return !Selector.test(node, expr);
944
    },
945
 
946
    'contains': function(node, expr) {
947
        var text = node.innerText || node.textContent || '';
948
        return text.indexOf(expr) > -1;
949
    },
950
 
951
    'checked': function(node) {
952
        return (node.checked === true || node.selected === true);
953
    },
954
 
955
    enabled: function(node) {
956
        return (node.disabled !== undefined && !node.disabled);
957
    },
958
 
959
    disabled: function(node) {
960
        return (node.disabled);
961
    }
962
});
963
 
964
Y_mix(Selector.operators, {
965
    '^=': '^{val}', // Match starts with value
966
    '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value
967
    '$=': '{val}$', // Match ends with value
968
    '*=': '{val}' // Match contains value as substring
969
});
970
 
971
Selector.combinators['~'] = {
972
    axis: 'previousSibling'
973
};
974
YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"});
975
 
976
}, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom"]});