/loggerhead/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/loggerhead/trunk

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/yui/build/dom/dom-debug.js

  • Committer: Matt Nordhoff
  • Date: 2010-02-26 04:37:13 UTC
  • mfrom: (400 trunk)
  • mto: This revision was merged to the branch mainline in revision 401.
  • Revision ID: mnordhoff@mattnordhoff.com-20100226043713-7mw3r6dr9qowutmi
Merge trunk for NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.net/yui/license.txt
 
5
version: 3.0.0pr2
 
6
*/
 
7
YUI.add('dom', function(Y) {
 
8
 
 
9
/** 
 
10
 * The DOM utility provides a cross-browser abtraction layer
 
11
 * normalizing DOM tasks, and adds extra helper functionality
 
12
 * for other common tasks. 
 
13
 * @module dom
 
14
 * @submodule dom-base
 
15
 *
 
16
 */
 
17
 
 
18
/** 
 
19
 * Provides DOM helper methods.
 
20
 * @class DOM
 
21
 *
 
22
 */
 
23
var NODE_TYPE = 'nodeType',
 
24
    OWNER_DOCUMENT = 'ownerDocument',
 
25
    DOCUMENT_ELEMENT = 'documentElement',
 
26
    DEFAULT_VIEW = 'defaultView',
 
27
    PARENT_WINDOW = 'parentWindow',
 
28
    TAG_NAME = 'tagName',
 
29
    PARENT_NODE = 'parentNode',
 
30
    FIRST_CHILD = 'firstChild',
 
31
    LAST_CHILD = 'lastChild',
 
32
    PREVIOUS_SIBLING = 'previousSibling',
 
33
    NEXT_SIBLING = 'nextSibling',
 
34
    CONTAINS = 'contains',
 
35
    COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
 
36
    INNER_TEXT = 'innerText',
 
37
    TEXT_CONTENT = 'textContent',
 
38
    LENGTH = 'length',
 
39
 
 
40
    UNDEFINED = undefined;
 
41
 
 
42
var re_tag = /<([a-z]+)/i;
 
43
 
 
44
var templateCache = {};
 
45
 
 
46
Y.DOM = {
 
47
    /**
 
48
     * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
 
49
     * @method byId         
 
50
     * @param {String} id the id attribute 
 
51
     * @param {Object} doc optional The document to search. Defaults to current document 
 
52
     * @return {HTMLElement | null} The HTMLElement with the id, or null if none found. 
 
53
     */
 
54
    byId: function(id, doc) {
 
55
        return Y.DOM._getDoc(doc).getElementById(id);
 
56
    },
 
57
 
 
58
    /**
 
59
     * Returns the text content of the HTMLElement. 
 
60
     * @method getText         
 
61
     * @param {HTMLElement} element The html element. 
 
62
     * @return {String} The text content of the element (includes text of any descending elements).
 
63
     */
 
64
    getText: function(element) {
 
65
        var text = element ? element[TEXT_CONTENT] : '';
 
66
        if (text === UNDEFINED && INNER_TEXT in element) {
 
67
            text = element[INNER_TEXT];
 
68
        } 
 
69
        return text || '';
 
70
    },
 
71
 
 
72
    /**
 
73
     * Finds the firstChild of the given HTMLElement. 
 
74
     * @method firstChild
 
75
     * @param {HTMLElement} element The html element. 
 
76
     * @param {Function} fn optional An optional boolean test to apply.
 
77
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
78
     * If no function is given, the first found is returned.
 
79
     * @return {HTMLElement | null} The first matching child html element.
 
80
     */
 
81
    firstChild: function(element, fn) {
 
82
        return Y.DOM._childBy(element, null, fn);
 
83
    },
 
84
 
 
85
    firstChildByTag: function(element, tag, fn) {
 
86
        return Y.DOM._childBy(element, tag, fn);
 
87
    },
 
88
 
 
89
    /**
 
90
     * Finds the lastChild of the given HTMLElement.
 
91
     * @method lastChild
 
92
     * @param {HTMLElement} element The html element.
 
93
     * @param {String} tag The tag to search for.
 
94
     * @param {Function} fn optional An optional boolean test to apply.
 
95
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
96
     * If no function is given, the first found is returned.
 
97
     * @return {HTMLElement | null} The first matching child html element.
 
98
     */
 
99
    lastChild: function(element, fn) {
 
100
        return Y.DOM._childBy(element, null, fn, true);
 
101
    },
 
102
 
 
103
    lastChildByTag: function(element, tag, fn) {
 
104
        return Y.DOM._childBy(element, tag, fn, true);
 
105
    },
 
106
 
 
107
    /**
 
108
     * Finds all HTMLElement childNodes matching the given tag.
 
109
     * @method childrenByTag
 
110
     * @param {HTMLElement} element The html element.
 
111
     * @param {String} tag The tag to search for.
 
112
     * @param {Function} fn optional An optional boolean test to apply.
 
113
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
114
     * If no function is given, all children with the given tag are collected.
 
115
     * @return {Array} The collection of child elements.
 
116
     */
 
117
    childrenByTag: function() {
 
118
        if (document[DOCUMENT_ELEMENT].children) {
 
119
            return function(element, tag, fn, toArray) { // TODO: keep toArray option?
 
120
                tag = (tag && tag !== '*') ? tag : null;
 
121
                var elements = [];
 
122
                if (element) {
 
123
                    elements = (tag) ? element.children.tags(tag) : element.children; 
 
124
 
 
125
                    if (fn || toArray) {
 
126
                        elements = Y.DOM.filterElementsBy(elements, fn);
 
127
                    }
 
128
                }
 
129
 
 
130
                return elements;
 
131
            };
 
132
        } else {
 
133
            return function(element, tag, fn) {
 
134
                tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
 
135
                var elements = [],
 
136
                    wrapFn = fn;
 
137
 
 
138
                if (element) {
 
139
                    elements = element.childNodes; 
 
140
                    if (tag) { // wrap fn and add tag test TODO: allow tag in filterElementsBy?
 
141
                        wrapFn = function(el) {
 
142
                            return el[TAG_NAME].toUpperCase() === tag && (!fn || fn(el));
 
143
                        };
 
144
                    }
 
145
 
 
146
                    elements = Y.DOM.filterElementsBy(elements, wrapFn);
 
147
                }
 
148
                return elements;
 
149
            };
 
150
        }
 
151
    }(),
 
152
 
 
153
    /**
 
154
     * Finds all HTMLElement childNodes.
 
155
     * @method children
 
156
     * @param {HTMLElement} element The html element.
 
157
     * @param {Function} fn optional An optional boolean test to apply.
 
158
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
159
     * If no function is given, all children are collected.
 
160
     * @return {Array} The collection of child elements.
 
161
     */
 
162
    children: function(element, fn) {
 
163
        return Y.DOM.childrenByTag(element, null, fn);
 
164
    },
 
165
 
 
166
    /**
 
167
     * Finds the previous sibling of the element.
 
168
     * @method previous
 
169
     * @param {HTMLElement} element The html element.
 
170
     * @param {Function} fn optional An optional boolean test to apply.
 
171
     * The optional function is passed the current DOM node being tested as its only argument.
 
172
     * If no function is given, the first sibling is returned.
 
173
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
174
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
175
     */
 
176
    previous: function(element, fn) {
 
177
        return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn);
 
178
    },
 
179
 
 
180
    /**
 
181
     * Finds the next sibling of the element.
 
182
     * @method next
 
183
     * @param {HTMLElement} element The html element.
 
184
     * @param {Function} fn optional An optional boolean test to apply.
 
185
     * The optional function is passed the current DOM node being tested as its only argument.
 
186
     * If no function is given, the first sibling is returned.
 
187
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
188
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
189
     */
 
190
    next: function(element, fn) {
 
191
        return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn);
 
192
    },
 
193
 
 
194
    /**
 
195
     * Finds the ancestor of the element.
 
196
     * @method ancestor
 
197
     * @param {HTMLElement} element The html element.
 
198
     * @param {Function} fn optional An optional boolean test to apply.
 
199
     * The optional function is passed the current DOM node being tested as its only argument.
 
200
     * If no function is given, the parentNode is returned.
 
201
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
202
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
203
     */
 
204
    ancestor: function(element, fn) {
 
205
        return Y.DOM.elementByAxis(element, PARENT_NODE, fn);
 
206
    },
 
207
 
 
208
    /**
 
209
     * Searches the element by the given axis for the first matching element.
 
210
     * @method elementByAxis
 
211
     * @param {HTMLElement} element The html element.
 
212
     * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
 
213
     * @param {Function} fn optional An optional boolean test to apply.
 
214
     * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
 
215
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
216
     * If no function is given, the first element is returned.
 
217
     * @return {HTMLElement | null} The matching element or null if none found.
 
218
     */
 
219
    elementByAxis: function(element, axis, fn, all) {
 
220
        while (element && (element = element[axis])) { // NOTE: assignment
 
221
                if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
 
222
                    return element;
 
223
                }
 
224
        }
 
225
        return null;
 
226
    },
 
227
 
 
228
    /**
 
229
     * Finds all elements with the given tag.
 
230
     * @method byTag
 
231
     * @param {String} tag The tag being search for. 
 
232
     * @param {HTMLElement} root optional An optional root element to start from.
 
233
     * @param {Function} fn optional An optional boolean test to apply.
 
234
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
235
     * If no function is given, all elements with the given tag are returned.
 
236
     * @return {Array} The collection of matching elements.
 
237
     */
 
238
    byTag: function(tag, root, fn) {
 
239
        root = root || Y.config.doc;
 
240
 
 
241
        var elements = root.getElementsByTagName(tag),
 
242
            retNodes = [];
 
243
 
 
244
        for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
 
245
            if ( !fn || fn(elements[i]) ) {
 
246
                retNodes[retNodes[LENGTH]] = elements[i];
 
247
            }
 
248
        }
 
249
        return retNodes;
 
250
    },
 
251
 
 
252
    /**
 
253
     * Finds the first element with the given tag.
 
254
     * @method firstByTag
 
255
     * @param {String} tag The tag being search for. 
 
256
     * @param {HTMLElement} root optional An optional root element to start from.
 
257
     * @param {Function} fn optional An optional boolean test to apply.
 
258
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
259
     * If no function is given, the first match is returned. 
 
260
     * @return {HTMLElement} The matching element.
 
261
     */
 
262
    firstByTag: function(tag, root, fn) {
 
263
        root = root || Y.config.doc;
 
264
 
 
265
        var elements = root.getElementsByTagName(tag),
 
266
            ret = null;
 
267
 
 
268
        for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
 
269
            if ( !fn || fn(elements[i]) ) {
 
270
                ret = elements[i];
 
271
                break;
 
272
            }
 
273
        }
 
274
        return ret;
 
275
    },
 
276
 
 
277
    /**
 
278
     * Filters a collection of HTMLElements by the given attributes.
 
279
     * @method filterElementsBy
 
280
     * @param {Array} elements The collection of HTMLElements to filter.
 
281
     * @param {Function} fn A boolean test to apply.
 
282
     * The function is passed the current HTMLElement being tested as its only argument.
 
283
     * If no function is given, all HTMLElements are kept.
 
284
     * @return {Array} The filtered collection of HTMLElements.
 
285
     */
 
286
    filterElementsBy: function(elements, fn, firstOnly) {
 
287
        var ret = (firstOnly) ? null : [];
 
288
        for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
 
289
            if (elements[i][TAG_NAME] && (!fn || fn(elements[i]))) {
 
290
                if (firstOnly) {
 
291
                    ret = elements[i];
 
292
                    break;
 
293
                } else {
 
294
                    ret[ret[LENGTH]] = elements[i];
 
295
                }
 
296
            }
 
297
        }
 
298
 
 
299
        return ret;
 
300
    },
 
301
 
 
302
    /**
 
303
     * Determines whether or not one HTMLElement is or contains another HTMLElement.
 
304
     * @method contains
 
305
     * @param {HTMLElement} element The containing html element.
 
306
     * @param {HTMLElement} needle The html element that may be contained.
 
307
     * @return {Boolean} Whether or not the element is or contains the needle.
 
308
     */
 
309
    contains: function(element, needle) {
 
310
        var ret = false;
 
311
 
 
312
        if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
 
313
            ret = false;
 
314
        } else if (element[CONTAINS])  {
 
315
            if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
 
316
                ret = element[CONTAINS](needle);
 
317
            } else {
 
318
                ret = Y.DOM._bruteContains(element, needle); 
 
319
            }
 
320
        } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
 
321
            if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { 
 
322
                ret = true;
 
323
            }
 
324
        }
 
325
 
 
326
        return ret;
 
327
    },
 
328
 
 
329
    /**
 
330
     * Determines whether or not the HTMLElement is part of the document.
 
331
     * @method inDoc
 
332
     * @param {HTMLElement} element The containing html element.
 
333
     * @param {HTMLElement} doc optional The document to check.
 
334
     * @return {Boolean} Whether or not the element is attached to the document. 
 
335
     */
 
336
    inDoc: function(element, doc) {
 
337
        doc = doc || Y.config.doc;
 
338
        return Y.DOM.contains(doc.documentElement, element);
 
339
    },
 
340
 
 
341
    create: function(html, doc) {
 
342
        doc = doc || Y.config.doc;
 
343
        var m = re_tag.exec(html);
 
344
        var create = Y.DOM._create,
 
345
            custom = Y.DOM.creators,
 
346
            tag, ret;
 
347
 
 
348
        if (m && custom[m[1]]) {
 
349
            if (typeof custom[m[1]] === 'function') {
 
350
                create = custom[m[1]];
 
351
            } else {
 
352
                tag = custom[m[1]];
 
353
            }
 
354
        }
 
355
        ret = create(html, doc, tag);
 
356
        return (ret.childNodes.length > 1) ? ret.childNodes : ret.childNodes[0]; // collection or item
 
357
        //return ret.firstChild;
 
358
    },
 
359
 
 
360
    _create: function(html, doc, tag) {
 
361
        tag = tag || 'div';
 
362
        var frag = templateCache[tag] || doc.createElement(tag);
 
363
        frag.innerHTML = Y.Lang.trim(html);
 
364
        return frag;
 
365
    },
 
366
 
 
367
    /**
 
368
     * Brute force version of contains.
 
369
     * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
 
370
     * @method _bruteContains
 
371
     * @private
 
372
     * @param {HTMLElement} element The containing html element.
 
373
     * @param {HTMLElement} needle The html element that may be contained.
 
374
     * @return {Boolean} Whether or not the element is or contains the needle.
 
375
     */
 
376
    _bruteContains: function(element, needle) {
 
377
        while (needle) {
 
378
            if (element === needle) {
 
379
                return true;
 
380
            }
 
381
            needle = needle.parentNode;
 
382
        }
 
383
        return false;
 
384
    },
 
385
 
 
386
    /**
 
387
     * Memoizes dynamic regular expressions to boost runtime performance. 
 
388
     * @method _getRegExp
 
389
     * @private
 
390
     * @param {String} str The string to convert to a regular expression.
 
391
     * @param {String} flags optional An optinal string of flags.
 
392
     * @return {RegExp} An instance of RegExp
 
393
     */
 
394
    _getRegExp: function(str, flags) {
 
395
        flags = flags || '';
 
396
        Y.DOM._regexCache = Y.DOM._regexCache || {};
 
397
        if (!Y.DOM._regexCache[str + flags]) {
 
398
            Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
 
399
        }
 
400
        return Y.DOM._regexCache[str + flags];
 
401
    },
 
402
 
 
403
    /**
 
404
     * returns the appropriate document.
 
405
     * @method _getDoc
 
406
     * @private
 
407
     * @param {HTMLElement} element optional Target element.
 
408
     * @return {Object} The document for the given element or the default document. 
 
409
     */
 
410
    _getDoc: function(element) {
 
411
        element = element || {};
 
412
        return (element[NODE_TYPE] === 9) ? element : element[OWNER_DOCUMENT] ||
 
413
                                                Y.config.doc;
 
414
    },
 
415
 
 
416
    /**
 
417
     * returns the appropriate window.
 
418
     * @method _getWin
 
419
     * @private
 
420
     * @param {HTMLElement} element optional Target element.
 
421
     * @return {Object} The window for the given element or the default window. 
 
422
     */
 
423
    _getWin: function(element) {
 
424
        var doc = Y.DOM._getDoc(element);
 
425
        return (element.document) ? element : doc[DEFAULT_VIEW] ||
 
426
                                        doc[PARENT_WINDOW] || Y.config.win;
 
427
    },
 
428
 
 
429
    _childBy: function(element, tag, fn, rev) {
 
430
        var ret = null,
 
431
            root, axis;
 
432
 
 
433
        if (element) {
 
434
            if (rev) {
 
435
                root = element[LAST_CHILD];
 
436
                axis = PREVIOUS_SIBLING;
 
437
            } else {
 
438
                root = element[FIRST_CHILD];
 
439
                axis = NEXT_SIBLING;
 
440
            }
 
441
 
 
442
            if (Y.DOM._testElement(root, tag, fn)) { // is the matching element
 
443
                ret = root;
 
444
            } else { // need to scan nextSibling axis of firstChild to find matching element
 
445
                ret = Y.DOM.elementByAxis(root, axis, fn);
 
446
            }
 
447
        }
 
448
        return ret;
 
449
 
 
450
    },
 
451
 
 
452
    _testElement: function(element, tag, fn) {
 
453
        tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
 
454
        return (element && element[TAG_NAME] &&
 
455
                (!tag || element[TAG_NAME].toUpperCase() === tag) &&
 
456
                (!fn || fn(element)));
 
457
    },
 
458
 
 
459
    creators: {},
 
460
 
 
461
    _IESimpleCreate: function(html, doc) {
 
462
        doc = doc || Y.config.doc;
 
463
        return doc.createElement(html);
 
464
    }
 
465
};
 
466
 
 
467
 
 
468
(function() {
 
469
    var creators = Y.DOM.creators,
 
470
        create = Y.DOM.create,
 
471
        re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/;
 
472
 
 
473
    var TABLE_OPEN = '<table>',
 
474
        TABLE_CLOSE = '</table>';
 
475
 
 
476
    if (Y.UA.gecko || Y.UA.ie) { // require custom creation code for certain element types
 
477
        Y.mix(creators, {
 
478
            option: function(html, doc) {
 
479
                var frag = create('<select>' + html + '</select>');
 
480
                return frag;
 
481
            },
 
482
 
 
483
            tr: function(html, doc) {
 
484
                var frag = creators.tbody('<tbody>' + html + '</tbody>', doc);
 
485
                return frag.firstChild;
 
486
            },
 
487
 
 
488
            td: function(html, doc) {
 
489
                var frag = creators.tr('<tr>' + html + '</tr>', doc);
 
490
                return frag.firstChild;
 
491
            }, 
 
492
 
 
493
            tbody: function(html, doc) {
 
494
                var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc);
 
495
                return frag;
 
496
            },
 
497
 
 
498
            legend: 'fieldset'
 
499
        });
 
500
 
 
501
        creators.col = creators.tbody; // IE wraps in colgroup
 
502
    }
 
503
 
 
504
    if (Y.UA.ie) {
 
505
        // TODO: allow multiples ("<link><link>")
 
506
        creators.col = creators.script = creators.link = Y.DOM._IESimpleCreate; 
 
507
 
 
508
        // TODO: thead/tfoot with nested tbody
 
509
        creators.tbody = function(html, doc) {
 
510
            var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc);
 
511
            var tb = frag.children.tags('tbody')[0];
 
512
            if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
 
513
                tb.parentNode.removeChild(tb);
 
514
            }
 
515
            return frag;
 
516
        };
 
517
 
 
518
    }
 
519
 
 
520
    if (Y.UA.gecko || Y.UA.ie) { // require custom creation code for certain element types
 
521
        Y.mix(creators, {
 
522
                th: creators.td,
 
523
                thead: creators.tbody,
 
524
                tfoot: creators.tbody,
 
525
                caption: creators.tbody,
 
526
                colgroup: creators.tbody,
 
527
                col: creators.tbody,
 
528
                optgroup: creators.option
 
529
        });
 
530
    }
 
531
})();
 
532
 
 
533
/** 
 
534
 * The DOM utility provides a cross-browser abtraction layer
 
535
 * normalizing DOM tasks, and adds extra helper functionality
 
536
 * for other common tasks. 
 
537
 * @module dom
 
538
 * @submodule dom-base
 
539
 * @for DOM
 
540
 */
 
541
 
 
542
var CLASS_NAME = 'className';
 
543
 
 
544
Y.mix(Y.DOM, {
 
545
    /**
 
546
     * Determines whether a DOM element has the given className.
 
547
     * @method hasClass
 
548
     * @param {HTMLElement} element The DOM element. 
 
549
     * @param {String} className the class name to search for
 
550
     * @return {Boolean} Whether or not the element has the given class. 
 
551
     */
 
552
    hasClass: function(node, className) {
 
553
        var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
 
554
        return re.test(node[CLASS_NAME]);
 
555
    },
 
556
 
 
557
    /**
 
558
     * Adds a class name to a given DOM element.
 
559
     * @method addClass         
 
560
     * @param {HTMLElement} element The DOM element. 
 
561
     * @param {String} className the class name to add to the class attribute
 
562
     */
 
563
    addClass: function(node, className) {
 
564
        if (!Y.DOM.hasClass(node, className)) { // skip if already present 
 
565
            node[CLASS_NAME] = Y.Lang.trim([node[CLASS_NAME], className].join(' '));
 
566
        }
 
567
    },
 
568
 
 
569
    /**
 
570
     * Removes a class name from a given element.
 
571
     * @method removeClass         
 
572
     * @param {HTMLElement} element The DOM element. 
 
573
     * @param {String} className the class name to remove from the class attribute
 
574
     */
 
575
    removeClass: function(node, className) {
 
576
        if (className && Y.DOM.hasClass(node, className)) {
 
577
            node[CLASS_NAME] = Y.Lang.trim(node[CLASS_NAME].replace(Y.DOM._getRegExp('(?:^|\\s+)' +
 
578
                            className + '(?:\\s+|$)'), ' '));
 
579
 
 
580
            if ( Y.DOM.hasClass(node, className) ) { // in case of multiple adjacent
 
581
                Y.DOM.removeClass(node, className);
 
582
            }
 
583
        }                 
 
584
    },
 
585
 
 
586
    /**
 
587
     * Replace a class with another class for a given element.
 
588
     * If no oldClassName is present, the newClassName is simply added.
 
589
     * @method replaceClass  
 
590
     * @param {HTMLElement} element The DOM element. 
 
591
     * @param {String} oldClassName the class name to be replaced
 
592
     * @param {String} newClassName the class name that will be replacing the old class name
 
593
     */
 
594
    replaceClass: function(node, oldC, newC) {
 
595
        //Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node');
 
596
        Y.DOM.addClass(node, newC);
 
597
        Y.DOM.removeClass(node, oldC);
 
598
    },
 
599
 
 
600
    /**
 
601
     * If the className exists on the node it is removed, if it doesn't exist it is added.
 
602
     * @method toggleClass  
 
603
     * @param {HTMLElement} element The DOM element. 
 
604
     * @param {String} className the class name to be toggled
 
605
     */
 
606
    toggleClass: function(node, className) {
 
607
        if (Y.DOM.hasClass(node, className)) {
 
608
            Y.DOM.removeClass(node, className);
 
609
        } else {
 
610
            Y.DOM.addClass(node, className);
 
611
        }
 
612
    }
 
613
});
 
614
 
 
615
 
 
616
/** 
 
617
 * Add style management functionality to DOM.
 
618
 * @module dom
 
619
 * @submodule dom-style
 
620
 * @for DOM
 
621
 */
 
622
 
 
623
var DOCUMENT_ELEMENT = 'documentElement',
 
624
    DEFAULT_VIEW = 'defaultView',
 
625
    OWNER_DOCUMENT = 'ownerDocument',
 
626
    STYLE = 'style',
 
627
    FLOAT = 'float',
 
628
    CSS_FLOAT = 'cssFloat',
 
629
    STYLE_FLOAT = 'styleFloat',
 
630
    TRANSPARENT = 'transparent',
 
631
    VISIBLE = 'visible',
 
632
    WIDTH = 'width',
 
633
    HEIGHT = 'height',
 
634
    BORDER_TOP_WIDTH = 'borderTopWidth',
 
635
    BORDER_RIGHT_WIDTH = 'borderRightWidth',
 
636
    BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
 
637
    BORDER_LEFT_WIDTH = 'borderLeftWidth',
 
638
    GET_COMPUTED_STYLE = 'getComputedStyle',
 
639
 
 
640
    DOCUMENT = Y.config.doc,
 
641
    UNDEFINED = undefined,
 
642
 
 
643
    re_color = /color$/i;
 
644
 
 
645
 
 
646
Y.mix(Y.DOM, {
 
647
    CUSTOM_STYLES: {},
 
648
 
 
649
 
 
650
    /**
 
651
     * Sets a style property for a given element.
 
652
     * @method setStyle
 
653
     * @param {HTMLElement} An HTMLElement to apply the style to.
 
654
     * @param {String} att The style property to set. 
 
655
     * @param {String|Number} val The value. 
 
656
     */
 
657
    setStyle: function(node, att, val, style) {
 
658
        style = node[STYLE],
 
659
            CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES;
 
660
 
 
661
        if (style) {
 
662
            if (att in CUSTOM_STYLES) {
 
663
                if (CUSTOM_STYLES[att].set) {
 
664
                    CUSTOM_STYLES[att].set(node, val, style);
 
665
                    return; // NOTE: return
 
666
                } else if (typeof CUSTOM_STYLES[att] === 'string') {
 
667
                    att = CUSTOM_STYLES[att];
 
668
                }
 
669
            }
 
670
            style[att] = val; 
 
671
        }
 
672
    },
 
673
 
 
674
    /**
 
675
     * Returns the current style value for the given property.
 
676
     * @method getStyle
 
677
     * @param {HTMLElement} An HTMLElement to get the style from.
 
678
     * @param {String} att The style property to get. 
 
679
     */
 
680
    getStyle: function(node, att) {
 
681
        var style = node[STYLE],
 
682
            CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES,
 
683
            val = '';
 
684
 
 
685
        if (style) {
 
686
            if (att in CUSTOM_STYLES) {
 
687
                if (CUSTOM_STYLES[att].get) {
 
688
                    return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
 
689
                } else if (typeof CUSTOM_STYLES[att] === 'string') {
 
690
                    att = CUSTOM_STYLES[att];
 
691
                }
 
692
            }
 
693
            val = style[att];
 
694
            if (val === '') { // TODO: is empty string sufficient?
 
695
                val = Y.DOM[GET_COMPUTED_STYLE](node, att);
 
696
            }
 
697
        }
 
698
 
 
699
        return val;
 
700
    },
 
701
 
 
702
    /**
 
703
     * Sets multiple style properties.
 
704
     * @method setStyles
 
705
     * @param {HTMLElement} node An HTMLElement to apply the styles to. 
 
706
     * @param {Object} hash An object literal of property:value pairs. 
 
707
     */
 
708
    'setStyles': function(node, hash) {
 
709
        Y.each(hash, function(v, n) {
 
710
            Y.DOM.setStyle(node, n, v);
 
711
        }, Y.DOM);
 
712
    },
 
713
 
 
714
    /**
 
715
     * Returns the computed style for the given node.
 
716
     * @method getComputedStyle
 
717
     * @param {HTMLElement} An HTMLElement to get the style from.
 
718
     * @param {String} att The style property to get. 
 
719
     * @return {String} The computed value of the style property. 
 
720
     */
 
721
    getComputedStyle: function(node, att) {
 
722
        var val = '',
 
723
            doc = node[OWNER_DOCUMENT];
 
724
 
 
725
        if (node[STYLE]) {
 
726
            val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, '')[att];
 
727
        }
 
728
        return val;
 
729
    }
 
730
});
 
731
 
 
732
if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
 
733
    Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
 
734
} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
 
735
    Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
 
736
}
 
737
 
 
738
if (Y.UA.opera) { // opera defaults to hex instead of RGB
 
739
    Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
 
740
        var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
 
741
            val = view[GET_COMPUTED_STYLE](node, '')[att];
 
742
 
 
743
        if (re_color.test(att)) {
 
744
            val = Y.Color.toRGB(val);
 
745
        }
 
746
 
 
747
        return val;
 
748
    };
 
749
 
 
750
}
 
751
 
 
752
if (Y.UA.webkit) { // safari converts transparent to rgba()
 
753
    Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
 
754
        var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
 
755
            val = view[GET_COMPUTED_STYLE](node, '')[att];
 
756
 
 
757
        if (val === 'rgba(0, 0, 0, 0)') {
 
758
            val = TRANSPARENT; 
 
759
        }
 
760
 
 
761
        return val;
 
762
    };
 
763
 
 
764
}
 
765
 
 
766
 
 
767
 
 
768
/**
 
769
 * Adds position and region management functionality to DOM.
 
770
 * @module dom
 
771
 * @submodule dom-screen
 
772
 * @for DOM
 
773
 */
 
774
 
 
775
var OFFSET_TOP = 'offsetTop',
 
776
 
 
777
    DOCUMENT_ELEMENT = 'documentElement',
 
778
    COMPAT_MODE = 'compatMode',
 
779
    OFFSET_LEFT = 'offsetLeft',
 
780
    OFFSET_PARENT = 'offsetParent',
 
781
    POSITION = 'position',
 
782
    FIXED = 'fixed',
 
783
    RELATIVE = 'relative',
 
784
    LEFT = 'left',
 
785
    TOP = 'top',
 
786
    SCROLL_LEFT = 'scrollLeft',
 
787
    SCROLL_TOP = 'scrollTop',
 
788
    _BACK_COMPAT = 'BackCompat',
 
789
    MEDIUM = 'medium',
 
790
    HEIGHT = 'height',
 
791
    WIDTH = 'width',
 
792
    BORDER_LEFT_WIDTH = 'borderLeftWidth',
 
793
    BORDER_TOP_WIDTH = 'borderTopWidth',
 
794
    GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
 
795
    GET_COMPUTED_STYLE = 'getComputedStyle',
 
796
    RE_TABLE = /^t(?:able|d|h)$/i;
 
797
 
 
798
Y.mix(Y.DOM, {
 
799
 
 
800
 
 
801
    /**
 
802
     * Returns the inner height of the viewport (exludes scrollbar). 
 
803
     * @method winHeight
 
804
 
 
805
     */
 
806
    winHeight: function(node) {
 
807
        var h = Y.DOM._getWinSize(node)[HEIGHT];
 
808
        Y.log('winHeight returning ' + h, 'info', 'DOM');
 
809
        return h;
 
810
    },
 
811
 
 
812
    /**
 
813
     * Returns the inner width of the viewport (exludes scrollbar). 
 
814
     * @method winWidth
 
815
 
 
816
     */
 
817
    winWidth: function(node) {
 
818
        var w = Y.DOM._getWinSize(node)[WIDTH];
 
819
        Y.log('winWidth returning ' + w, 'info', 'DOM');
 
820
        return w;
 
821
    },
 
822
 
 
823
    /**
 
824
     * Document height 
 
825
     * @method docHeight
 
826
 
 
827
     */
 
828
    docHeight:  function(node) {
 
829
        var h = Y.DOM._getDocSize(node)[HEIGHT];
 
830
        Y.log('docHeight returning ' + h, 'info', 'DOM');
 
831
        return Math.max(h, Y.DOM._getWinSize(node)[HEIGHT]);
 
832
    },
 
833
 
 
834
    /**
 
835
     * Document width 
 
836
     * @method docWidth
 
837
 
 
838
     */
 
839
    docWidth:  function(node) {
 
840
        var w = Y.DOM._getDocSize(node)[WIDTH];
 
841
        Y.log('docWidth returning ' + w, 'info', 'DOM');
 
842
        return Math.max(w, Y.DOM._getWinSize(node)[WIDTH]);
 
843
    },
 
844
 
 
845
    /**
 
846
     * Amount page has been scroll vertically 
 
847
     * @method docScrollX
 
848
 
 
849
     */
 
850
    docScrollX: function(node) {
 
851
        var doc = Y.DOM._getDoc();
 
852
        return Math.max(doc[DOCUMENT_ELEMENT][SCROLL_LEFT], doc.body[SCROLL_LEFT]);
 
853
    },
 
854
 
 
855
    /**
 
856
     * Amount page has been scroll horizontally 
 
857
     * @method docScrollY
 
858
 
 
859
     */
 
860
    docScrollY:  function(node) {
 
861
        var doc = Y.DOM._getDoc();
 
862
        return Math.max(doc[DOCUMENT_ELEMENT][SCROLL_TOP], doc.body[SCROLL_TOP]);
 
863
    },
 
864
 
 
865
    /**
 
866
     * Gets the current position of an element based on page coordinates. 
 
867
     * Element must be part of the DOM tree to have page coordinates
 
868
     * (display:none or elements not appended return false).
 
869
     * @method getXY
 
870
     * @param element The target element
 
871
     * @return {Array} The XY position of the element
 
872
 
 
873
     TODO: test inDocument/display
 
874
     */
 
875
    getXY: function() {
 
876
 
 
877
 
 
878
 
 
879
 
 
880
        if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
 
881
            return function(node) {
 
882
                if (!node) {
 
883
                    return false;
 
884
                }
 
885
                var scrollLeft = Y.DOM.docScrollX(node),
 
886
                    scrollTop = Y.DOM.docScrollY(node),
 
887
                    box = node[GET_BOUNDING_CLIENT_RECT](),
 
888
                    doc = Y.DOM._getDoc(node),
 
889
                    //Round the numbers so we get sane data back
 
890
                    xy = [Math.floor(box[LEFT]), Math.floor(box[TOP])];
 
891
 
 
892
                    if (Y.UA.ie) {
 
893
                        var off1 = 2, off2 = 2,
 
894
                        mode = doc[COMPAT_MODE],
 
895
                        bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH),
 
896
                        bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
 
897
 
 
898
                        if (Y.UA.ie === 6) {
 
899
                            if (mode !== _BACK_COMPAT) {
 
900
                                off1 = 0;
 
901
                                off2 = 0;
 
902
                            }
 
903
                        }
 
904
                        
 
905
                        if ((mode == _BACK_COMPAT)) {
 
906
                            if (bLeft !== MEDIUM) {
 
907
                                off1 = parseInt(bLeft, 10);
 
908
                            }
 
909
                            if (bTop !== MEDIUM) {
 
910
                                off2 = parseInt(bTop, 10);
 
911
                            }
 
912
 
 
913
 
 
914
 
 
915
                        }
 
916
                        
 
917
                        xy[0] -= off1;
 
918
                        xy[1] -= off2;
 
919
 
 
920
                    }
 
921
 
 
922
                if ((scrollTop || scrollLeft)) {
 
923
                    xy[0] += scrollLeft;
 
924
                    xy[1] += scrollTop;
 
925
                }
 
926
 
 
927
                // gecko may return sub-pixel (non-int) values
 
928
                xy[0] = Math.floor(xy[0]);
 
929
                xy[1] = Math.floor(xy[1]);
 
930
 
 
931
                return xy;                   
 
932
            };
 
933
        } else {
 
934
            return function(node) { // manually calculate by crawling up offsetParents
 
935
                //Calculate the Top and Left border sizes (assumes pixels)
 
936
                var xy = [node[OFFSET_LEFT], node[OFFSET_TOP]],
 
937
                    parentNode = node,
 
938
                    bCheck = ((Y.UA.gecko || (Y.UA.webkit > 519)) ? true : false);
 
939
 
 
940
                while ((parentNode = parentNode[OFFSET_PARENT])) {
 
941
                    xy[0] += parentNode[OFFSET_LEFT];
 
942
                    xy[1] += parentNode[OFFSET_TOP];
 
943
                    if (bCheck) {
 
944
                        xy = Y.DOM._calcBorders(parentNode, xy);
 
945
                    }
 
946
                }
 
947
 
 
948
                // account for any scrolled ancestors
 
949
                if (Y.DOM.getStyle(node, POSITION) != FIXED) {
 
950
                    parentNode = node;
 
951
                    var scrollTop, scrollLeft;
 
952
 
 
953
                    while ((parentNode = parentNode.parentNode)) {
 
954
                        scrollTop = parentNode[SCROLL_TOP];
 
955
                        scrollLeft = parentNode[SCROLL_LEFT];
 
956
 
 
957
                        //Firefox does something funky with borders when overflow is not visible.
 
958
                        if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
 
959
                                xy = Y.DOM._calcBorders(parentNode, xy);
 
960
                        }
 
961
                        
 
962
 
 
963
                        if (scrollTop || scrollLeft) {
 
964
                            xy[0] -= scrollLeft;
 
965
                            xy[1] -= scrollTop;
 
966
                        }
 
967
                    }
 
968
                    xy[0] += Y.DOM.docScrollX(node);
 
969
                    xy[1] += Y.DOM.docScrollY(node);
 
970
 
 
971
                } else {
 
972
                    //Fix FIXED position -- add scrollbars
 
973
                    if (Y.UA.opera) {
 
974
                        xy[0] -= Y.DOM.docScrollX(node);
 
975
                        xy[1] -= Y.DOM.docScrollY(node);
 
976
                    } else if (Y.UA.webkit || Y.UA.gecko) {
 
977
                        xy[0] += Y.DOM.docScrollX(node);
 
978
                        xy[1] += Y.DOM.docScrollY(node);
 
979
                    }
 
980
                }
 
981
                //Round the numbers so we get sane data back
 
982
                xy[0] = Math.floor(xy[0]);
 
983
                xy[1] = Math.floor(xy[1]);
 
984
 
 
985
                return xy;                
 
986
            };
 
987
        }
 
988
    }(),// NOTE: Executing for loadtime branching
 
989
 
 
990
    /**
 
991
     * Gets the current X position of an element based on page coordinates. 
 
992
     * Element must be part of the DOM tree to have page coordinates
 
993
     * (display:none or elements not appended return false).
 
994
     * @method getX
 
995
     * @param element The target element
 
996
     * @return {Int} The X position of the element
 
997
     */
 
998
 
 
999
    getX: function(node) {
 
1000
        return Y.DOM.getXY(node)[0];
 
1001
    },
 
1002
 
 
1003
    /**
 
1004
     * Gets the current Y position of an element based on page coordinates. 
 
1005
     * Element must be part of the DOM tree to have page coordinates
 
1006
     * (display:none or elements not appended return false).
 
1007
     * @method getY
 
1008
     * @param element The target element
 
1009
     * @return {Int} The Y position of the element
 
1010
     */
 
1011
 
 
1012
    getY: function(node) {
 
1013
        return Y.DOM.getXY(node)[1];
 
1014
    },
 
1015
 
 
1016
    /**
 
1017
     * Set the position of an html element in page coordinates.
 
1018
     * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1019
     * @method setXY
 
1020
     * @param element The target element
 
1021
     * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
 
1022
     * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
 
1023
     */
 
1024
    setXY: function(node, xy, noRetry) {
 
1025
        var pos = Y.DOM.getStyle(node, POSITION),
 
1026
            setStyle = Y.DOM.setStyle,
 
1027
 
 
1028
            delta = [ // assuming pixels; if not we will have to retry
 
1029
                parseInt( Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10 ),
 
1030
                parseInt( Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10 )
 
1031
            ];
 
1032
    
 
1033
        if (pos == 'static') { // default to relative
 
1034
            pos = RELATIVE;
 
1035
            setStyle(node, POSITION, pos);
 
1036
        }
 
1037
 
 
1038
        var currentXY = Y.DOM.getXY(node);
 
1039
 
 
1040
 
 
1041
 
 
1042
 
 
1043
        if (currentXY === false) { // has to be part of doc to have xy
 
1044
 
 
1045
 
 
1046
 
 
1047
 
 
1048
 
 
1049
            Y.log('xy failed: node not available', 'error', 'Node');
 
1050
            return false; 
 
1051
        }
 
1052
        
 
1053
        if ( isNaN(delta[0]) ) {// in case of 'auto'
 
1054
            delta[0] = (pos == RELATIVE) ? 0 : node[OFFSET_LEFT];
 
1055
        } 
 
1056
        if ( isNaN(delta[1]) ) { // in case of 'auto'
 
1057
            delta[1] = (pos == RELATIVE) ? 0 : node[OFFSET_TOP];
 
1058
        } 
 
1059
 
 
1060
        if (xy[0] !== null) {
 
1061
            setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
 
1062
        }
 
1063
 
 
1064
        if (xy[1] !== null) {
 
1065
            setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
 
1066
        }
 
1067
      
 
1068
        if (!noRetry) {
 
1069
            var newXY = Y.DOM.getXY(node);
 
1070
 
 
1071
            // if retry is true, try one more time if we miss 
 
1072
           if ( (xy[0] !== null && newXY[0] != xy[0]) || 
 
1073
                (xy[1] !== null && newXY[1] != xy[1]) ) {
 
1074
               Y.DOM.setXY(node, xy, true);
 
1075
           }
 
1076
        }        
 
1077
 
 
1078
        Y.log('setXY setting position to ' + xy, 'info', 'Node');
 
1079
    },
 
1080
 
 
1081
    /**
 
1082
     * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
 
1083
     * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1084
     * @method setX
 
1085
     * @param element The target element
 
1086
     * @param {Int} x The X values for new position (coordinates are page-based)
 
1087
     */
 
1088
    setX: function(node, x) {
 
1089
        return Y.DOM.setXY(node, [x, null]);
 
1090
    },
 
1091
 
 
1092
    /**
 
1093
     * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
 
1094
     * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1095
     * @method setY
 
1096
     * @param element The target element
 
1097
     * @param {Int} y The Y values for new position (coordinates are page-based)
 
1098
     */
 
1099
    setY: function(node, y) {
 
1100
        return Y.DOM.setXY(node, [null, y]);
 
1101
    },
 
1102
 
 
1103
    _calcBorders: function(node, xy2) {
 
1104
        var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
 
1105
            l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
 
1106
        if (Y.UA.gecko) {
 
1107
            if (RE_TABLE.test(node.tagName)) {
 
1108
                t = 0;
 
1109
                l = 0;
 
1110
            }
 
1111
        }
 
1112
        xy2[0] += l;
 
1113
        xy2[1] += t;
 
1114
        return xy2;
 
1115
    },
 
1116
 
 
1117
    _getWinSize: function(node) {
 
1118
        var doc = Y.DOM._getDoc(),
 
1119
            win = doc.defaultView || doc.parentWindow,
 
1120
            mode = doc[COMPAT_MODE],
 
1121
            h = win.innerHeight,
 
1122
            w = win.innerWidth,
 
1123
            root = doc[DOCUMENT_ELEMENT];
 
1124
 
 
1125
        if ( mode && !Y.UA.opera ) { // IE, Gecko
 
1126
            if (mode != 'CSS1Compat') { // Quirks
 
1127
                root = doc.body; 
 
1128
            }
 
1129
            h = root.clientHeight;
 
1130
            w = root.clientWidth;
 
1131
        }
 
1132
        return { height: h, width: w }; 
 
1133
    },
 
1134
 
 
1135
    _getDocSize: function(node) {
 
1136
        var doc = Y.DOM._getDoc(),
 
1137
            root = doc[DOCUMENT_ELEMENT];
 
1138
 
 
1139
        if (doc[COMPAT_MODE] != 'CSS1Compat') {
 
1140
            root = doc.body;
 
1141
        }
 
1142
 
 
1143
        return { height: root.scrollHeight, width: root.scrollWidth };
 
1144
    }
 
1145
});
 
1146
 
 
1147
 
 
1148
 
 
1149
/**
 
1150
 * Adds position and region management functionality to DOM.
 
1151
 * @module dom
 
1152
 * @submodule dom-screen
 
1153
 * @for DOM
 
1154
 */
 
1155
 
 
1156
var OFFSET_WIDTH = 'offsetWidth',
 
1157
    OFFSET_HEIGHT = 'offsetHeight',
 
1158
    TOP = 'top',
 
1159
    RIGHT = 'right',
 
1160
    BOTTOM = 'bottom',
 
1161
    LEFT = 'left',
 
1162
    TAG_NAME = 'tagName';
 
1163
 
 
1164
var getOffsets = function(r1, r2) {
 
1165
    var t = Math.max(r1[TOP], r2[TOP]),
 
1166
        r = Math.min(r1[RIGHT], r2[RIGHT]),
 
1167
        b = Math.min(r1[BOTTOM], r2[BOTTOM]),
 
1168
        l = Math.max(r1[LEFT], r2[LEFT]),
 
1169
        ret = {};
 
1170
    
 
1171
    ret[TOP] = t;
 
1172
    ret[RIGHT] = r;
 
1173
    ret[BOTTOM] = b;
 
1174
    ret[LEFT] = l;
 
1175
    return ret;
 
1176
};
 
1177
 
 
1178
var DOM = DOM || Y.DOM;
 
1179
Y.mix(DOM, {
 
1180
    /**
 
1181
     * Returns an Object literal containing the following about this element: (top, right, bottom, left)
 
1182
     * @method region
 
1183
     * @param {HTMLElement} element The DOM element. 
 
1184
     @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
 
1185
     */
 
1186
    region: function(node) {
 
1187
        var x = DOM.getXY(node),
 
1188
            ret = false;
 
1189
        
 
1190
        if (x) {
 
1191
            ret = {
 
1192
                '0': x[0],
 
1193
                '1': x[1],
 
1194
                top: x[1],
 
1195
                right: x[0] + node[OFFSET_WIDTH],
 
1196
                bottom: x[1] + node[OFFSET_HEIGHT],
 
1197
                left: x[0],
 
1198
                height: node[OFFSET_HEIGHT],
 
1199
                width: node[OFFSET_WIDTH]
 
1200
            };
 
1201
        }
 
1202
 
 
1203
        return ret;
 
1204
    },
 
1205
 
 
1206
    /**
 
1207
     * Find the intersect information for the passes nodes.
 
1208
     * @method intersect
 
1209
     * @param {HTMLElement} element The first element 
 
1210
     * @param {HTMLElement | Object} element2 The element or region to check the interect with
 
1211
     * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
 
1212
     @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
 
1213
     */
 
1214
    intersect: function(node, node2, altRegion) {
 
1215
        var r = altRegion || DOM.region(node), region = {};
 
1216
 
 
1217
        var n = node2;
 
1218
        if (n[TAG_NAME]) {
 
1219
            region = DOM.region(n);
 
1220
        } else if (Y.Lang.isObject(node2)) {
 
1221
            region = node2;
 
1222
        } else {
 
1223
            return false;
 
1224
        }
 
1225
        
 
1226
        var off = getOffsets(region, r);
 
1227
        return {
 
1228
            top: off[TOP],
 
1229
            right: off[RIGHT],
 
1230
            bottom: off[BOTTOM],
 
1231
            left: off[LEFT],
 
1232
            area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
 
1233
            yoff: ((off[BOTTOM] - off[TOP])),
 
1234
            xoff: (off[RIGHT] - off[LEFT]),
 
1235
            inRegion: DOM.inRegion(node, node2, false, altRegion)
 
1236
        };
 
1237
        
 
1238
    },
 
1239
    /**
 
1240
     * Check if any part of this node is in the passed region
 
1241
     * @method inRegion
 
1242
     * @param {Object} node2 The node to get the region from or an Object literal of the region
 
1243
     * $param {Boolean} all Should all of the node be inside the region
 
1244
     * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
 
1245
     * @return {Boolean} True if in region, false if not.
 
1246
     */
 
1247
    inRegion: function(node, node2, all, altRegion) {
 
1248
        var region = {},
 
1249
            r = altRegion || DOM.region(node);
 
1250
 
 
1251
        var n = node2;
 
1252
        if (n[TAG_NAME]) {
 
1253
            region = DOM.region(n);
 
1254
        } else if (Y.Lang.isObject(node2)) {
 
1255
            region = node2;
 
1256
        } else {
 
1257
            return false;
 
1258
        }
 
1259
            
 
1260
        if (all) {
 
1261
            return (
 
1262
                r[LEFT]   >= region[LEFT]   &&
 
1263
                r[RIGHT]  <= region[RIGHT]  && 
 
1264
                r[TOP]    >= region[TOP]    && 
 
1265
                r[BOTTOM] <= region[BOTTOM]  );
 
1266
        } else {
 
1267
            var off = getOffsets(region, r);
 
1268
            if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
 
1269
                return true;
 
1270
            } else {
 
1271
                return false;
 
1272
            }
 
1273
            
 
1274
        }
 
1275
    },
 
1276
 
 
1277
    /**
 
1278
     * Check if any part of this element is in the viewport
 
1279
     * @method inViewportRegion
 
1280
     * @param {HTMLElement} element The DOM element. 
 
1281
     * @param {Boolean} all Should all of the node be inside the region
 
1282
     * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
 
1283
     * @return {Boolean} True if in region, false if not.
 
1284
     */
 
1285
    inViewportRegion: function(node, all, altRegion) {
 
1286
        return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
 
1287
            
 
1288
    },
 
1289
 
 
1290
    /**
 
1291
     * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
 
1292
     * @method viewportRegion
 
1293
     @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
 
1294
     */
 
1295
    viewportRegion: function(node) {
 
1296
        node = node || Y.config.doc.documentElement;
 
1297
        var r = {};
 
1298
        r[TOP] = DOM.docScrollY(node);
 
1299
        r[RIGHT] = DOM.winWidth(node) + DOM.docScrollX(node);
 
1300
        r[BOTTOM] = (DOM.docScrollY(node) + DOM.winHeight(node));
 
1301
        r[LEFT] = DOM.docScrollX(node);
 
1302
 
 
1303
        return r;
 
1304
    }
 
1305
});
 
1306
 
 
1307
/**
 
1308
 * Add style management functionality to DOM.
 
1309
 * @module dom
 
1310
 * @submodule dom-style
 
1311
 * @for DOM
 
1312
 */
 
1313
 
 
1314
var CLIENT_TOP = 'clientTop',
 
1315
    CLIENT_LEFT = 'clientLeft',
 
1316
    PARENT_NODE = 'parentNode',
 
1317
    RIGHT = 'right',
 
1318
    HAS_LAYOUT = 'hasLayout',
 
1319
    PX = 'px',
 
1320
    FILTER = 'filter',
 
1321
    FILTERS = 'filters',
 
1322
    OPACITY = 'opacity',
 
1323
    AUTO = 'auto',
 
1324
    CURRENT_STYLE = 'currentStyle';
 
1325
 
 
1326
// use alpha filter for IE opacity
 
1327
if (document[DOCUMENT_ELEMENT][STYLE][OPACITY] === UNDEFINED &&
 
1328
        document[DOCUMENT_ELEMENT][FILTERS]) {
 
1329
    Y.DOM.CUSTOM_STYLES[OPACITY] = {
 
1330
        get: function(node) {
 
1331
            var val = 100;
 
1332
            try { // will error if no DXImageTransform
 
1333
                val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
 
1334
 
 
1335
            } catch(e) {
 
1336
                try { // make sure its in the document
 
1337
                    val = node[FILTERS]('alpha')[OPACITY];
 
1338
                } catch(err) {
 
1339
                    Y.log('getStyle: IE opacity filter not found; returning 1', 'warn', 'DOM');
 
1340
                }
 
1341
            }
 
1342
            return val / 100;
 
1343
        },
 
1344
 
 
1345
        set: function(node, val, style) {
 
1346
            if (typeof style[FILTER] == 'string') { // in case not appended
 
1347
                style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
 
1348
                
 
1349
                if (!node[CURRENT_STYLE] || !node[CURRENT_STYLE][HAS_LAYOUT]) {
 
1350
                    style.zoom = 1; // needs layout 
 
1351
                }
 
1352
            }
 
1353
        }
 
1354
    };
 
1355
}
 
1356
 
 
1357
// IE getComputedStyle
 
1358
// TODO: unit-less lineHeight (e.g. 1.22)
 
1359
var re_size = /^width|height$/,
 
1360
    re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i;
 
1361
 
 
1362
var ComputedStyle = {
 
1363
    CUSTOM_STYLES: {},
 
1364
 
 
1365
    get: function(el, property) {
 
1366
        var value = '',
 
1367
            current = el[CURRENT_STYLE][property];
 
1368
 
 
1369
        if (property === OPACITY) {
 
1370
            value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);        
 
1371
        } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
 
1372
            value = current;
 
1373
        } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
 
1374
            value = Y.DOM.IE.COMPUTED[property](el, property);
 
1375
        } else if (re_unit.test(current)) { // convert to pixel
 
1376
            value = Y.DOM.IE.ComputedStyle.getPixel(el, property);
 
1377
        } else {
 
1378
            value = current;
 
1379
        }
 
1380
 
 
1381
        return value;
 
1382
    },
 
1383
 
 
1384
    getOffset: function(el, prop) {
 
1385
        var current = el[CURRENT_STYLE][prop],                        // value of "width", "top", etc.
 
1386
            capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
 
1387
            offset = 'offset' + capped,                             // "offsetWidth", "offsetTop", etc.
 
1388
            pixel = 'pixel' + capped,                               // "pixelWidth", "pixelTop", etc.
 
1389
            value = '';
 
1390
 
 
1391
        if (current == AUTO) {
 
1392
            var actual = el[offset]; // offsetHeight/Top etc.
 
1393
            if (actual === UNDEFINED) { // likely "right" or "bottom"
 
1394
                value = 0;
 
1395
            }
 
1396
 
 
1397
            value = actual;
 
1398
            if (re_size.test(prop)) { // account for box model diff 
 
1399
                el[STYLE][prop] = actual; 
 
1400
                if (el[offset] > actual) {
 
1401
                    // the difference is padding + border (works in Standards & Quirks modes)
 
1402
                    value = actual - (el[offset] - actual);
 
1403
                }
 
1404
                el[STYLE][prop] = AUTO; // revert to auto
 
1405
            }
 
1406
        } else { // convert units to px
 
1407
            if (!el[STYLE][pixel] && !el[STYLE][prop]) { // need to map style.width to currentStyle (no currentStyle.pixelWidth)
 
1408
                el[STYLE][prop] = current;              // no style.pixelWidth if no style.width
 
1409
            }
 
1410
            value = el[STYLE][pixel];
 
1411
        }
 
1412
        return value + PX;
 
1413
    },
 
1414
 
 
1415
    getBorderWidth: function(el, property) {
 
1416
        // clientHeight/Width = paddingBox (e.g. offsetWidth - borderWidth)
 
1417
        // clientTop/Left = borderWidth
 
1418
        var value = null;
 
1419
        if (!el[CURRENT_STYLE][HAS_LAYOUT]) { // TODO: unset layout?
 
1420
            el[STYLE].zoom = 1; // need layout to measure client
 
1421
        }
 
1422
 
 
1423
        switch(property) {
 
1424
            case BORDER_TOP_WIDTH:
 
1425
                value = el[CLIENT_TOP];
 
1426
                break;
 
1427
            case BORDER_BOTTOM_WIDTH:
 
1428
                value = el.offsetHeight - el.clientHeight - el[CLIENT_TOP];
 
1429
                break;
 
1430
            case BORDER_LEFT_WIDTH:
 
1431
                value = el[CLIENT_LEFT];
 
1432
                break;
 
1433
            case BORDER_RIGHT_WIDTH:
 
1434
                value = el.offsetWidth - el.clientWidth - el[CLIENT_LEFT];
 
1435
                break;
 
1436
        }
 
1437
        return value + PX;
 
1438
    },
 
1439
 
 
1440
    getPixel: function(node, att) {
 
1441
        // use pixelRight to convert to px
 
1442
        var val = null,
 
1443
            styleRight = node[CURRENT_STYLE][RIGHT],
 
1444
            current = node[CURRENT_STYLE][att];
 
1445
 
 
1446
        node[STYLE][RIGHT] = current;
 
1447
        val = node[STYLE].pixelRight;
 
1448
        node[STYLE][RIGHT] = styleRight; // revert
 
1449
 
 
1450
        return val + PX;
 
1451
    },
 
1452
 
 
1453
    getMargin: function(node, att) {
 
1454
        var val;
 
1455
        if (node[CURRENT_STYLE][att] == AUTO) {
 
1456
            val = 0 + PX;
 
1457
        } else {
 
1458
            val = Y.DOM.IE.ComputedStyle.getPixel(node, att);
 
1459
        }
 
1460
        return val;
 
1461
    },
 
1462
 
 
1463
    getVisibility: function(node, att) {
 
1464
        var current;
 
1465
        while ( (current = node[CURRENT_STYLE]) && current[att] == 'inherit') { // NOTE: assignment in test
 
1466
            node = node[PARENT_NODE];
 
1467
        }
 
1468
        return (current) ? current[att] : VISIBLE;
 
1469
    },
 
1470
 
 
1471
    getColor: function(node, att) {
 
1472
        var current = node[CURRENT_STYLE][att];
 
1473
 
 
1474
        if (!current || current === TRANSPARENT) {
 
1475
            Y.DOM.elementByAxis(node, PARENT_NODE, null, function(parent) {
 
1476
                current = parent[CURRENT_STYLE][att];
 
1477
                if (current && current !== TRANSPARENT) {
 
1478
                    node = parent;
 
1479
                    return true;
 
1480
                }
 
1481
            });
 
1482
        }
 
1483
 
 
1484
        return Y.Color.toRGB(current);
 
1485
    },
 
1486
 
 
1487
    getBorderColor: function(node, att) {
 
1488
        var current = node[CURRENT_STYLE];
 
1489
        var val = current[att] || current.color;
 
1490
        return Y.Color.toRGB(Y.Color.toHex(val));
 
1491
    }
 
1492
 
 
1493
};
 
1494
 
 
1495
//fontSize: getPixelFont,
 
1496
var IEComputed = {};
 
1497
 
 
1498
IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
 
1499
 
 
1500
IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
 
1501
 
 
1502
IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
 
1503
        IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
 
1504
        ComputedStyle.getBorderWidth;
 
1505
 
 
1506
IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
 
1507
        IEComputed.marginLeft = ComputedStyle.getMargin;
 
1508
 
 
1509
IEComputed.visibility = ComputedStyle.getVisibility;
 
1510
IEComputed.borderColor = IEComputed.borderTopColor =
 
1511
        IEComputed.borderRightColor = IEComputed.borderBottomColor =
 
1512
        IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
 
1513
 
 
1514
if (!Y.config.win[GET_COMPUTED_STYLE]) {
 
1515
    Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get; 
 
1516
}
 
1517
 
 
1518
Y.namespace('DOM.IE');
 
1519
Y.DOM.IE.COMPUTED = IEComputed;
 
1520
Y.DOM.IE.ComputedStyle = ComputedStyle;
 
1521
 
 
1522
 
 
1523
/**
 
1524
 * Provides helper methods for collecting and filtering DOM elements.
 
1525
 * @module dom
 
1526
 * @submodule selector
 
1527
 */
 
1528
 
 
1529
/**
 
1530
 * Provides helper methods for collecting and filtering DOM elements.
 
1531
 * @class Selector
 
1532
 * @static
 
1533
 */
 
1534
 
 
1535
var TAG = 'tag',
 
1536
    PARENT_NODE = 'parentNode',
 
1537
    PREVIOUS_SIBLING = 'previousSibling',
 
1538
    LENGTH = 'length',
 
1539
    NODE_TYPE = 'nodeType',
 
1540
    TAG_NAME = 'tagName',
 
1541
    ATTRIBUTES = 'attributes',
 
1542
    PSEUDOS = 'pseudos',
 
1543
    COMBINATOR = 'combinator';
 
1544
 
 
1545
var reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
 
1546
 
 
1547
var patterns = {
 
1548
    tag: /^((?:-?[_a-z]+[\w\-]*)|\*)/i,
 
1549
    attributes: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
 
1550
    pseudos: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
 
1551
    combinator: /^\s*([>+~]|\s)\s*/
 
1552
};
 
1553
 
 
1554
var Selector = {
 
1555
    /**
 
1556
     * Default document for use queries 
 
1557
     * @property document
 
1558
     * @type object
 
1559
     * @default window.document
 
1560
     */
 
1561
    document: Y.config.doc,
 
1562
    /**
 
1563
     * Mapping of attributes to aliases, normally to work around HTMLAttributes
 
1564
     * that conflict with JS reserved words.
 
1565
     * @property attrAliases
 
1566
     * @type object
 
1567
     */
 
1568
    attrAliases: {},
 
1569
 
 
1570
    /**
 
1571
     * Mapping of shorthand tokens to corresponding attribute selector 
 
1572
     * @property shorthand
 
1573
     * @type object
 
1574
     */
 
1575
    shorthand: {
 
1576
        '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
 
1577
        '\\.(-?[_a-z]+[-\\w]*)': '[class~=$1]'
 
1578
    },
 
1579
 
 
1580
    /**
 
1581
     * List of operators and corresponding boolean functions. 
 
1582
     * These functions are passed the attribute and the current node's value of the attribute.
 
1583
     * @property operators
 
1584
     * @type object
 
1585
     */
 
1586
    operators: {
 
1587
        '=': function(attr, val) { return attr === val; }, // Equality
 
1588
        '!=': function(attr, val) { return attr !== val; }, // Inequality
 
1589
        '~=': function(attr, val) { // Match one of space seperated words 
 
1590
            var s = ' ';
 
1591
            return (s + attr + s).indexOf((s + val + s)) > -1;
 
1592
        },
 
1593
        '|=': function(attr, val) { return Y.DOM._getRegExp('^' + val + '[-]?').test(attr); }, // Match start with value followed by optional hyphen
 
1594
        '^=': function(attr, val) { return attr.indexOf(val) === 0; }, // Match starts with value
 
1595
        '$=': function(attr, val) { return attr.lastIndexOf(val) === attr[LENGTH] - val[LENGTH]; }, // Match ends with value
 
1596
        '*=': function(attr, val) { return attr.indexOf(val) > -1; }, // Match contains value as substring 
 
1597
        '': function(attr, val) { return attr; } // Just test for existence of attribute
 
1598
    },
 
1599
 
 
1600
    /**
 
1601
     * List of pseudo-classes and corresponding boolean functions. 
 
1602
     * These functions are called with the current node, and any value that was parsed with the pseudo regex.
 
1603
     * @property pseudos
 
1604
     * @type object
 
1605
     */
 
1606
    pseudos: {
 
1607
        'root': function(node) {
 
1608
            return node === node.ownerDocument.documentElement;
 
1609
        },
 
1610
 
 
1611
        'nth-child': function(node, val) {
 
1612
            return Selector.getNth(node, val);
 
1613
        },
 
1614
 
 
1615
        'nth-last-child': function(node, val) {
 
1616
            return Selector.getNth(node, val, null, true);
 
1617
        },
 
1618
 
 
1619
        'nth-of-type': function(node, val) {
 
1620
            return Selector.getNth(node, val, node[TAG_NAME]);
 
1621
        },
 
1622
         
 
1623
        'nth-last-of-type': function(node, val) {
 
1624
            return Selector.getNth(node, val, node[TAG_NAME], true);
 
1625
        },
 
1626
         
 
1627
        'first-child': function(node) {
 
1628
            return Y.DOM.firstChild(node[PARENT_NODE]) === node;
 
1629
        },
 
1630
 
 
1631
        'last-child': function(node) {
 
1632
            return Y.DOM.lastChild(node[PARENT_NODE]) === node;
 
1633
        },
 
1634
 
 
1635
        'first-of-type': function(node, val) {
 
1636
            return Y.DOM.firstChildByTag(node[PARENT_NODE], node[TAG_NAME]) === node;
 
1637
        },
 
1638
         
 
1639
        'last-of-type': function(node, val) {
 
1640
            return Y.DOM.lastChildByTag(node[PARENT_NODE], node[TAG_NAME]) === node;
 
1641
        },
 
1642
         
 
1643
        'only-child': function(node) {
 
1644
            var children = Y.DOM.children(node[PARENT_NODE]);
 
1645
            return children[LENGTH] === 1 && children[0] === node;
 
1646
        },
 
1647
 
 
1648
        'only-of-type': function(node) {
 
1649
            return Y.DOM.childrenByTag(node[PARENT_NODE], node[TAG_NAME])[LENGTH] === 1;
 
1650
        },
 
1651
 
 
1652
        'empty': function(node) {
 
1653
            return node.childNodes[LENGTH] === 0;
 
1654
        },
 
1655
 
 
1656
        'not': function(node, simple) {
 
1657
            return !Selector.test(node, simple);
 
1658
        },
 
1659
 
 
1660
        'contains': function(node, str) {
 
1661
            var text = node.innerText || node.textContent || '';
 
1662
            return text.indexOf(str) > -1;
 
1663
        },
 
1664
        'checked': function(node) {
 
1665
            return node.checked === true;
 
1666
        }
 
1667
    },
 
1668
 
 
1669
    /**
 
1670
     * Test if the supplied node matches the supplied selector.
 
1671
     * @method test
 
1672
     *
 
1673
     * @param {HTMLElement | String} node An id or node reference to the HTMLElement being tested.
 
1674
     * @param {string} selector The CSS Selector to test the node against.
 
1675
     * @return{boolean} Whether or not the node matches the selector.
 
1676
     * @static
 
1677
    
 
1678
     */
 
1679
    test: function(node, selector) {
 
1680
        if (!node) {
 
1681
            return false;
 
1682
        }
 
1683
 
 
1684
        var groups = selector ? selector.split(',') : [];
 
1685
        if (groups[LENGTH]) {
 
1686
            for (var i = 0, len = groups[LENGTH]; i < len; ++i) {
 
1687
                if ( Selector._testNode(node, groups[i]) ) { // passes if ANY group matches
 
1688
                    return true;
 
1689
                }
 
1690
            }
 
1691
            return false;
 
1692
        }
 
1693
        return Selector._testNode(node, selector);
 
1694
    },
 
1695
 
 
1696
    /**
 
1697
     * Filters a set of nodes based on a given CSS selector. 
 
1698
     * @method filter
 
1699
     *
 
1700
     * @param {array} nodes A set of nodes/ids to filter. 
 
1701
     * @param {string} selector The selector used to test each node.
 
1702
     * @return{array} An array of nodes from the supplied array that match the given selector.
 
1703
     * @static
 
1704
     */
 
1705
    filter: function(nodes, selector) {
 
1706
        nodes = nodes || [];
 
1707
 
 
1708
        var result = Selector._filter(nodes, Selector._tokenize(selector)[0]);
 
1709
        Y.log('filter: returning:' + result[LENGTH], 'info', 'Selector');
 
1710
        return result;
 
1711
    },
 
1712
 
 
1713
    /**
 
1714
     * Retrieves a set of nodes based on a given CSS selector. 
 
1715
     * @method query
 
1716
     *
 
1717
     * @param {string} selector The CSS Selector to test the node against.
 
1718
     * @param {HTMLElement | String} root optional An id or HTMLElement to start the query from. Defaults to Selector.document.
 
1719
     * @param {Boolean} firstOnly optional Whether or not to return only the first match.
 
1720
     * @return {Array} An array of nodes that match the given selector.
 
1721
     * @static
 
1722
     */
 
1723
    query: function(selector, root, firstOnly) {
 
1724
        var result = Selector._query(selector, root, firstOnly);
 
1725
        //Y.log('query: ' + selector + ' returning ' + result, 'info', 'Selector');
 
1726
        return result;
 
1727
    },
 
1728
 
 
1729
    _query: function(selector, root, firstOnly, deDupe) {
 
1730
        var result =  (firstOnly) ? null : [];
 
1731
        if (!selector) {
 
1732
            return result;
 
1733
        }
 
1734
 
 
1735
        root = root || Selector.document;
 
1736
        var groups = selector.split(','); // TODO: handle comma in attribute/pseudo
 
1737
 
 
1738
        if (groups[LENGTH] > 1) {
 
1739
            var found;
 
1740
            for (var i = 0, len = groups[LENGTH]; i < len; ++i) {
 
1741
                found = arguments.callee(groups[i], root, firstOnly, true);
 
1742
                result = firstOnly ? found : result.concat(found); 
 
1743
            }
 
1744
            Selector._clearFoundCache();
 
1745
            return result;
 
1746
        }
 
1747
 
 
1748
        var tokens = Selector._tokenize(selector);
 
1749
        var idToken = tokens[Selector._getIdTokenIndex(tokens)],
 
1750
            nodes = [],
 
1751
            node,
 
1752
            id,
 
1753
            token = tokens.pop() || {};
 
1754
            
 
1755
        if (idToken) {
 
1756
            id = Selector._getId(idToken[ATTRIBUTES]);
 
1757
        }
 
1758
 
 
1759
        // use id shortcut when possible
 
1760
        if (id) {
 
1761
            node = Selector.document.getElementById(id);
 
1762
 
 
1763
            if (node && (root[NODE_TYPE] === 9 || Y.DOM.contains(root, node))) {
 
1764
                if ( Selector._testNode(node, null, idToken) ) {
 
1765
                    if (idToken === token) {
 
1766
                        nodes = [node]; // simple selector
 
1767
                    } else {
 
1768
                        root = node; // start from here
 
1769
                    }
 
1770
                }
 
1771
            } else {
 
1772
                return result;
 
1773
            }
 
1774
        }
 
1775
 
 
1776
        if (root && !nodes[LENGTH]) {
 
1777
            nodes = root.getElementsByTagName(token[TAG]);
 
1778
        }
 
1779
 
 
1780
        if (nodes[LENGTH]) {
 
1781
            result = Selector._filter(nodes, token, firstOnly, deDupe); 
 
1782
        }
 
1783
        return result;
 
1784
    },
 
1785
 
 
1786
    _filter: function(nodes, token, firstOnly, deDupe) {
 
1787
        var result = firstOnly ? null : [];
 
1788
 
 
1789
        result = Y.DOM.filterElementsBy(nodes, function(node) {
 
1790
            if (! Selector._testNode(node, '', token, deDupe)) {
 
1791
                return false;
 
1792
            }
 
1793
 
 
1794
            if (deDupe) {
 
1795
                if (node._found) {
 
1796
                    return false;
 
1797
                }
 
1798
                node._found = true;
 
1799
                Selector._foundCache[Selector._foundCache[LENGTH]] = node;
 
1800
            }
 
1801
            return true;
 
1802
        }, firstOnly);
 
1803
 
 
1804
        return result;
 
1805
    },
 
1806
 
 
1807
    _testNode: function(node, selector, token, deDupe) {
 
1808
        token = token || Selector._tokenize(selector).pop() || {};
 
1809
        var ops = Selector.operators,
 
1810
            pseudos = Selector.pseudos,
 
1811
            prev = token.previous,
 
1812
            i, len;
 
1813
 
 
1814
        if (!node[TAG_NAME] ||
 
1815
            (token[TAG] !== '*' && node[TAG_NAME].toUpperCase() !== token[TAG]) ||
 
1816
            (deDupe && node._found) ) {
 
1817
            return false;
 
1818
        }
 
1819
 
 
1820
        if (token[ATTRIBUTES][LENGTH]) {
 
1821
            var attribute;
 
1822
            for (i = 0, len = token[ATTRIBUTES][LENGTH]; i < len; ++i) {
 
1823
                attribute = node.getAttribute(token[ATTRIBUTES][i][0], 2);
 
1824
                if (attribute === undefined) {
 
1825
                    return false;
 
1826
                }
 
1827
                if ( ops[token[ATTRIBUTES][i][1]] &&
 
1828
                        !ops[token[ATTRIBUTES][i][1]](attribute, token[ATTRIBUTES][i][2])) {
 
1829
                    return false;
 
1830
                }
 
1831
            }
 
1832
        }
 
1833
 
 
1834
        if (token[PSEUDOS][LENGTH]) {
 
1835
            for (i = 0, len = token[PSEUDOS][LENGTH]; i < len; ++i) {
 
1836
                if (pseudos[token[PSEUDOS][i][0]] &&
 
1837
                        !pseudos[token[PSEUDOS][i][0]](node, token[PSEUDOS][i][1])) {
 
1838
                    return false;
 
1839
                }
 
1840
            }
 
1841
        }
 
1842
        return (prev && prev[COMBINATOR] !== ',') ?
 
1843
                Selector.combinators[prev[COMBINATOR]](node, token) :
 
1844
                true;
 
1845
    },
 
1846
 
 
1847
 
 
1848
    _foundCache: [],
 
1849
    _regexCache: {},
 
1850
 
 
1851
    _clearFoundCache: function() {
 
1852
        Y.log('getBySelector: clearing found cache of ' + Selector._foundCache[LENGTH] + ' elements');
 
1853
        for (var i = 0, len = Selector._foundCache[LENGTH]; i < len; ++i) {
 
1854
            try { // IE no like delete
 
1855
                delete Selector._foundCache[i]._found;
 
1856
            } catch(e) {
 
1857
                Selector._foundCache[i].removeAttribute('_found');
 
1858
            }
 
1859
        }
 
1860
        Selector._foundCache = [];
 
1861
        Y.log('getBySelector: done clearing Selector._foundCache');
 
1862
    },
 
1863
 
 
1864
    combinators: {
 
1865
        ' ': function(node, token) {
 
1866
            while ((node = node[PARENT_NODE])) {
 
1867
                if (Selector._testNode(node, '', token.previous)) {
 
1868
                    return true;
 
1869
                }
 
1870
            }  
 
1871
            return false;
 
1872
        },
 
1873
 
 
1874
        '>': function(node, token) {
 
1875
            return Selector._testNode(node[PARENT_NODE], null, token.previous);
 
1876
        },
 
1877
        '+': function(node, token) {
 
1878
            var sib = node[PREVIOUS_SIBLING];
 
1879
            while (sib && sib[NODE_TYPE] !== 1) {
 
1880
                sib = sib[PREVIOUS_SIBLING];
 
1881
            }
 
1882
 
 
1883
            if (sib && Selector._testNode(sib, null, token.previous)) {
 
1884
                return true; 
 
1885
            }
 
1886
            return false;
 
1887
        },
 
1888
 
 
1889
        '~': function(node, token) {
 
1890
            var sib = node[PREVIOUS_SIBLING];
 
1891
            while (sib) {
 
1892
                if (sib[NODE_TYPE] === 1 && Selector._testNode(sib, null, token.previous)) {
 
1893
                    return true;
 
1894
                }
 
1895
                sib = sib[PREVIOUS_SIBLING];
 
1896
            }
 
1897
 
 
1898
            return false;
 
1899
        }
 
1900
    },
 
1901
 
 
1902
 
 
1903
    /*
 
1904
        an+b = get every _a_th node starting at the _b_th
 
1905
        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
 
1906
        1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
 
1907
        an+0 = get every _a_th element, "0" may be omitted 
 
1908
    */
 
1909
    getNth: function(node, expr, tag, reverse) {
 
1910
        reNth.test(expr);
 
1911
 
 
1912
        var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
 
1913
            n = RegExp.$2, // "n"
 
1914
            oddeven = RegExp.$3, // "odd" or "even"
 
1915
            b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
 
1916
            op, i, len, siblings;
 
1917
 
 
1918
        if (tag) {
 
1919
            siblings = Y.DOM.childrenByTag(node[PARENT_NODE], tag);
 
1920
        } else {
 
1921
            siblings = Y.DOM.children(node[PARENT_NODE]);
 
1922
        }
 
1923
 
 
1924
        if (oddeven) {
 
1925
            a = 2; // always every other
 
1926
            op = '+';
 
1927
            n = 'n';
 
1928
            b = (oddeven === 'odd') ? 1 : 0;
 
1929
        } else if ( isNaN(a) ) {
 
1930
            a = (n) ? 1 : 0; // start from the first or no repeat
 
1931
        }
 
1932
 
 
1933
        if (a === 0) { // just the first
 
1934
            if (reverse) {
 
1935
                b = siblings[LENGTH] - b + 1; 
 
1936
            }
 
1937
 
 
1938
            if (siblings[b - 1] === node) {
 
1939
                return true;
 
1940
            } else {
 
1941
                return false;
 
1942
            }
 
1943
 
 
1944
        } else if (a < 0) {
 
1945
            reverse = !!reverse;
 
1946
            a = Math.abs(a);
 
1947
        }
 
1948
 
 
1949
        if (!reverse) {
 
1950
            for (i = b - 1, len = siblings[LENGTH]; i < len; i += a) {
 
1951
                if ( i >= 0 && siblings[i] === node ) {
 
1952
                    return true;
 
1953
                }
 
1954
            }
 
1955
        } else {
 
1956
            for (i = siblings[LENGTH] - b, len = siblings[LENGTH]; i >= 0; i -= a) {
 
1957
                if ( i < len && siblings[i] === node ) {
 
1958
                    return true;
 
1959
                }
 
1960
            }
 
1961
        }
 
1962
        return false;
 
1963
    },
 
1964
 
 
1965
    _getId: function(attr) {
 
1966
        for (var i = 0, len = attr[LENGTH]; i < len; ++i) {
 
1967
            if (attr[i][0] == 'id' && attr[i][1] === '=') {
 
1968
                return attr[i][2];
 
1969
            }
 
1970
        }
 
1971
    },
 
1972
 
 
1973
    _getIdTokenIndex: function(tokens) {
 
1974
        for (var i = 0, len = tokens[LENGTH]; i < len; ++i) {
 
1975
            if (Selector._getId(tokens[i][ATTRIBUTES])) {
 
1976
                return i;
 
1977
            }
 
1978
        }
 
1979
        return -1;
 
1980
    },
 
1981
 
 
1982
    /**
 
1983
        Break selector into token units per simple selector.
 
1984
        Combinator is attached to left-hand selector.
 
1985
     */
 
1986
    _tokenize: function(selector) {
 
1987
        var token = {},     // one token per simple selector (left selector holds combinator)
 
1988
            tokens = [],    // array of tokens
 
1989
            found = false,  // whether or not any matches were found this pass
 
1990
            match;          // the regex match
 
1991
 
 
1992
        selector = Selector._replaceShorthand(selector); // convert ID and CLASS shortcuts to attributes
 
1993
 
 
1994
        /*
 
1995
            Search for selector patterns, store, and strip them from the selector string
 
1996
            until no patterns match (invalid selector) or we run out of chars.
 
1997
 
 
1998
            Multiple attributes and pseudos are allowed, in any order.
 
1999
            for example:
 
2000
                'form:first-child[type=button]:not(button)[lang|=en]'
 
2001
        */
 
2002
        do {
 
2003
            found = false; // reset after full pass
 
2004
            for (var re in patterns) {
 
2005
                if (patterns.hasOwnProperty(re)) {
 
2006
                    if (re != TAG && re != COMBINATOR) { // only one allowed
 
2007
                        token[re] = token[re] || [];
 
2008
                    }
 
2009
                    if ((match = patterns[re].exec(selector))) { // note assignment
 
2010
                        found = true;
 
2011
                        if (re != TAG && re != COMBINATOR) { // only one allowed
 
2012
                            //token[re] = token[re] || [];
 
2013
 
 
2014
                            // capture ID for fast path to element
 
2015
                            if (re === ATTRIBUTES && match[1] === 'id') {
 
2016
                                token.id = match[3];
 
2017
                            }
 
2018
 
 
2019
                            token[re].push(match.slice(1));
 
2020
                        } else { // single selector (tag, combinator)
 
2021
                            token[re] = match[1];
 
2022
                        }
 
2023
                        selector = selector.replace(match[0], ''); // strip current match from selector
 
2024
                        if (re === COMBINATOR || !selector[LENGTH]) { // next token or done
 
2025
                            token[ATTRIBUTES] = Selector._fixAttributes(token[ATTRIBUTES]);
 
2026
                            token[PSEUDOS] = token[PSEUDOS] || [];
 
2027
                            token[TAG] = token[TAG] ? token[TAG].toUpperCase() : '*';
 
2028
                            tokens.push(token);
 
2029
 
 
2030
                            token = { // prep next token
 
2031
                                previous: token
 
2032
                            };
 
2033
                        }
 
2034
                    }
 
2035
                }
 
2036
            }
 
2037
        } while (found);
 
2038
 
 
2039
        return tokens;
 
2040
    },
 
2041
 
 
2042
    _fixAttributes: function(attr) {
 
2043
        var aliases = Selector.attrAliases;
 
2044
        attr = attr || [];
 
2045
        for (var i = 0, len = attr[LENGTH]; i < len; ++i) {
 
2046
            if (aliases[attr[i][0]]) { // convert reserved words, etc
 
2047
                attr[i][0] = aliases[attr[i][0]];
 
2048
            }
 
2049
            if (!attr[i][1]) { // use exists operator
 
2050
                attr[i][1] = '';
 
2051
            }
 
2052
        }
 
2053
        return attr;
 
2054
    },
 
2055
 
 
2056
    _replaceShorthand: function(selector) {
 
2057
        var shorthand = Selector.shorthand;
 
2058
        var attrs = selector.match(patterns[ATTRIBUTES]); // pull attributes to avoid false pos on "." and "#"
 
2059
        if (attrs) {
 
2060
            selector = selector.replace(patterns[ATTRIBUTES], 'REPLACED_ATTRIBUTE');
 
2061
        }
 
2062
        for (var re in shorthand) {
 
2063
            if (shorthand.hasOwnProperty(re)) {
 
2064
                selector = selector.replace(Y.DOM._getRegExp(re, 'gi'), shorthand[re]);
 
2065
            }
 
2066
        }
 
2067
 
 
2068
        if (attrs) {
 
2069
            for (var i = 0, len = attrs[LENGTH]; i < len; ++i) {
 
2070
                selector = selector.replace('REPLACED_ATTRIBUTE', attrs[i]);
 
2071
            }
 
2072
        }
 
2073
        return selector;
 
2074
    }
 
2075
 
 
2076
};
 
2077
 
 
2078
if (Y.UA.ie && Y.UA.ie < 8) { // rewrite class for IE (others use getAttribute('class')
 
2079
    Selector.attrAliases['class'] = 'className';
 
2080
    Selector.attrAliases['for'] = 'htmlFor';
 
2081
}
 
2082
 
 
2083
Y.Selector = Selector;
 
2084
Y.Selector.patterns = patterns;
 
2085
 
 
2086
 
 
2087
/**
 
2088
 * Add style management functionality to DOM.
 
2089
 * @module dom
 
2090
 * @submodule dom-style
 
2091
 * @for DOM
 
2092
 */
 
2093
 
 
2094
var TO_STRING = 'toString',
 
2095
    PARSE_INT = parseInt,
 
2096
    RE = RegExp;
 
2097
 
 
2098
Y.Color = {
 
2099
    KEYWORDS: {
 
2100
        black: '000',
 
2101
        silver: 'c0c0c0',
 
2102
        gray: '808080',
 
2103
        white: 'fff',
 
2104
        maroon: '800000',
 
2105
        red: 'f00',
 
2106
        purple: '800080',
 
2107
        fuchsia: 'f0f',
 
2108
        green: '008000',
 
2109
        lime: '0f0',
 
2110
        olive: '808000',
 
2111
        yellow: 'ff0',
 
2112
        navy: '000080',
 
2113
        blue: '00f',
 
2114
        teal: '008080',
 
2115
        aqua: '0ff'
 
2116
    },
 
2117
 
 
2118
    re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
 
2119
    re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
 
2120
    re_hex3: /([0-9A-F])/gi,
 
2121
 
 
2122
    toRGB: function(val) {
 
2123
        if (!Y.Color.re_RGB.test(val)) {
 
2124
            val = Y.Color.toHex(val);
 
2125
        }
 
2126
 
 
2127
        if(Y.Color.re_hex.exec(val)) {
 
2128
            val = 'rgb(' + [
 
2129
                PARSE_INT(RE.$1, 16),
 
2130
                PARSE_INT(RE.$2, 16),
 
2131
                PARSE_INT(RE.$3, 16)
 
2132
            ].join(', ') + ')';
 
2133
        }
 
2134
        return val;
 
2135
    },
 
2136
 
 
2137
    toHex: function(val) {
 
2138
        val = Y.Color.KEYWORDS[val] || val;
 
2139
        if (Y.Color.re_RGB.exec(val)) {
 
2140
            var r = (RE.$1.length === 1) ? '0' + RE.$1 : Number(RE.$1),
 
2141
                g = (RE.$2.length === 1) ? '0' + RE.$2 : Number(RE.$2),
 
2142
                b = (RE.$3.length === 1) ? '0' + RE.$3 : Number(RE.$3);
 
2143
 
 
2144
            val = [
 
2145
                r[TO_STRING](16),
 
2146
                g[TO_STRING](16),
 
2147
                b[TO_STRING](16)
 
2148
            ].join('');
 
2149
        }
 
2150
 
 
2151
        if (val.length < 6) {
 
2152
            val = val.replace(Y.Color.re_hex3, '$1$1');
 
2153
        }
 
2154
 
 
2155
        if (val !== 'transparent' && val.indexOf('#') < 0) {
 
2156
            val = '#' + val;
 
2157
        }
 
2158
 
 
2159
        return val.toLowerCase();
 
2160
    }
 
2161
};
 
2162
 
 
2163
 
 
2164
 
 
2165
 
 
2166
}, '3.0.0pr2' ,{skinnable:false});