~landscape/lazr-js/trunk

« back to all changes in this revision

Viewing changes to src-js/lazrjs/yui/3.0.0/build/dom/dom-base-debug.js

  • Committer: Sidnei da Silva
  • Date: 2009-10-21 21:43:07 UTC
  • mfrom: (120.2.15 yui-3.0.0)
  • mto: (124.5.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 126.
  • Revision ID: sidnei.da.silva@canonical.com-20091021214307-mpul9404n317puk5
- Merge from yui-3.0.0, resolving conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.net/yui/license.txt
 
5
version: 3.0.0
 
6
build: 1549
 
7
*/
 
8
YUI.add('dom-base', function(Y) {
 
9
 
 
10
(function(Y) {
 
11
/** 
 
12
 * The DOM utility provides a cross-browser abtraction layer
 
13
 * normalizing DOM tasks, and adds extra helper functionality
 
14
 * for other common tasks. 
 
15
 * @module dom
 
16
 * @submodule dom-base
 
17
 *
 
18
 */
 
19
 
 
20
/**
 
21
 * Provides DOM helper methods.
 
22
 * @class DOM
 
23
 *
 
24
 */
 
25
var NODE_TYPE = 'nodeType',
 
26
    OWNER_DOCUMENT = 'ownerDocument',
 
27
    DEFAULT_VIEW = 'defaultView',
 
28
    PARENT_WINDOW = 'parentWindow',
 
29
    TAG_NAME = 'tagName',
 
30
    PARENT_NODE = 'parentNode',
 
31
    FIRST_CHILD = 'firstChild',
 
32
    PREVIOUS_SIBLING = 'previousSibling',
 
33
    NEXT_SIBLING = 'nextSibling',
 
34
    CONTAINS = 'contains',
 
35
    COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
 
36
 
 
37
    documentElement = document.documentElement,
 
38
 
 
39
    re_tag = /<([a-z]+)/i;
 
40
 
 
41
Y.DOM = {
 
42
    /**
 
43
     * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
 
44
     * @method byId         
 
45
     * @param {String} id the id attribute 
 
46
     * @param {Object} doc optional The document to search. Defaults to current document 
 
47
     * @return {HTMLElement | null} The HTMLElement with the id, or null if none found. 
 
48
     */
 
49
    byId: function(id, doc) {
 
50
        doc = doc || Y.config.doc;
 
51
        // TODO: IE Name
 
52
        return doc.getElementById(id);
 
53
    },
 
54
 
 
55
    // @deprecated
 
56
    children: function(node, tag) {
 
57
        var ret = [];
 
58
        if (node) {
 
59
            tag = tag || '*';
 
60
            ret = Y.Selector.query('> ' + tag, node); 
 
61
        }
 
62
        return ret;
 
63
    },
 
64
 
 
65
    // @deprecated
 
66
    firstByTag: function(tag, root) {
 
67
        var ret;
 
68
        root = root || Y.config.doc;
 
69
 
 
70
        if (tag && root.getElementsByTagName) {
 
71
            ret = root.getElementsByTagName(tag)[0];
 
72
        }
 
73
 
 
74
        return ret || null;
 
75
    },
 
76
 
 
77
    /**
 
78
     * Returns the text content of the HTMLElement. 
 
79
     * @method getText         
 
80
     * @param {HTMLElement} element The html element. 
 
81
     * @return {String} The text content of the element (includes text of any descending elements).
 
82
     */
 
83
    getText: (documentElement.textContent !== undefined) ?
 
84
        function(element) {
 
85
            var ret = '';
 
86
            if (element) {
 
87
                ret = element.textContent;
 
88
            }
 
89
            return ret || '';
 
90
        } : function(element) {
 
91
            var ret = '';
 
92
            if (element) {
 
93
                ret = element.innerText;
 
94
            }
 
95
            return ret || '';
 
96
        },
 
97
 
 
98
    /**
 
99
     * Sets the text content of the HTMLElement. 
 
100
     * @method setText         
 
101
     * @param {HTMLElement} element The html element. 
 
102
     * @param {String} content The content to add. 
 
103
     */
 
104
    setText: (documentElement.textContent !== undefined) ?
 
105
        function(element, content) {
 
106
            if (element) {
 
107
                element.textContent = content;
 
108
            }
 
109
        } : function(element, content) {
 
110
            if (element) {
 
111
                element.innerText = content;
 
112
            }
 
113
        },
 
114
 
 
115
    /*
 
116
     * Finds the previous sibling of the element.
 
117
     * @method previous
 
118
     * @deprecated Use elementByAxis
 
119
     * @param {HTMLElement} element The html element.
 
120
     * @param {Function} fn optional An optional boolean test to apply.
 
121
     * The optional function is passed the current DOM node being tested as its only argument.
 
122
     * If no function is given, the first sibling is returned.
 
123
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
124
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
125
     */
 
126
    previous: function(element, fn, all) {
 
127
        return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
 
128
    },
 
129
 
 
130
    /*
 
131
     * Finds the next sibling of the element.
 
132
     * @method next
 
133
     * @deprecated Use elementByAxis
 
134
     * @param {HTMLElement} element The html element.
 
135
     * @param {Function} fn optional An optional boolean test to apply.
 
136
     * The optional function is passed the current DOM node being tested as its only argument.
 
137
     * If no function is given, the first sibling is returned.
 
138
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
139
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
140
     */
 
141
    next: function(element, fn, all) {
 
142
        return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
 
143
    },
 
144
 
 
145
    /*
 
146
     * Finds the ancestor of the element.
 
147
     * @method ancestor
 
148
     * @deprecated Use elementByAxis
 
149
     * @param {HTMLElement} element The html element.
 
150
     * @param {Function} fn optional An optional boolean test to apply.
 
151
     * The optional function is passed the current DOM node being tested as its only argument.
 
152
     * If no function is given, the parentNode is returned.
 
153
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
154
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
155
     */
 
156
     // TODO: optional stopAt node?
 
157
    ancestor: function(element, fn, all) {
 
158
        return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
 
159
    },
 
160
 
 
161
    /**
 
162
     * Searches the element by the given axis for the first matching element.
 
163
     * @method elementByAxis
 
164
     * @param {HTMLElement} element The html element.
 
165
     * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
 
166
     * @param {Function} fn optional An optional boolean test to apply.
 
167
     * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
 
168
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
169
     * If no function is given, the first element is returned.
 
170
     * @return {HTMLElement | null} The matching element or null if none found.
 
171
     */
 
172
    elementByAxis: function(element, axis, fn, all) {
 
173
        while (element && (element = element[axis])) { // NOTE: assignment
 
174
                if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
 
175
                    return element;
 
176
                }
 
177
        }
 
178
        return null;
 
179
    },
 
180
 
 
181
    /**
 
182
     * Determines whether or not one HTMLElement is or contains another HTMLElement.
 
183
     * @method contains
 
184
     * @param {HTMLElement} element The containing html element.
 
185
     * @param {HTMLElement} needle The html element that may be contained.
 
186
     * @return {Boolean} Whether or not the element is or contains the needle.
 
187
     */
 
188
    contains: function(element, needle) {
 
189
        var ret = false;
 
190
 
 
191
        if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
 
192
            ret = false;
 
193
        } else if (element[CONTAINS])  {
 
194
            if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
 
195
                ret = element[CONTAINS](needle);
 
196
            } else {
 
197
                ret = Y.DOM._bruteContains(element, needle); 
 
198
            }
 
199
        } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
 
200
            if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { 
 
201
                ret = true;
 
202
            }
 
203
        }
 
204
 
 
205
        return ret;
 
206
    },
 
207
 
 
208
    /**
 
209
     * Determines whether or not the HTMLElement is part of the document.
 
210
     * @method inDoc
 
211
     * @param {HTMLElement} element The containing html element.
 
212
     * @param {HTMLElement} doc optional The document to check.
 
213
     * @return {Boolean} Whether or not the element is attached to the document. 
 
214
     */
 
215
    inDoc: function(element, doc) {
 
216
        doc = doc || element[OWNER_DOCUMENT];
 
217
        var id = element.id;
 
218
        if (!id) { // TODO: remove when done?
 
219
            id = element.id = Y.guid();
 
220
        }
 
221
 
 
222
        return !! (doc.getElementById(id));
 
223
    },
 
224
 
 
225
    /**
 
226
     * Creates a new dom node using the provided markup string. 
 
227
     * @method create
 
228
     * @param {String} html The markup used to create the element
 
229
     * @param {HTMLDocument} doc An optional document context 
 
230
     */
 
231
    create: function(html, doc) {
 
232
        if (typeof html === 'string') {
 
233
            html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
 
234
        }
 
235
 
 
236
        if (!doc && Y.DOM._cloneCache[html]) {
 
237
            return Y.DOM._cloneCache[html].cloneNode(true); // NOTE: return
 
238
        }
 
239
 
 
240
        doc = doc || Y.config.doc;
 
241
        var m = re_tag.exec(html),
 
242
            create = Y.DOM._create,
 
243
            custom = Y.DOM.creators,
 
244
            ret = null,
 
245
            tag, nodes;
 
246
 
 
247
        if (m && custom[m[1]]) {
 
248
            if (typeof custom[m[1]] === 'function') {
 
249
                create = custom[m[1]];
 
250
            } else {
 
251
                tag = custom[m[1]];
 
252
            }
 
253
        }
 
254
 
 
255
        nodes = create(html, doc, tag).childNodes;
 
256
 
 
257
        if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
 
258
            ret = nodes[0].parentNode.removeChild(nodes[0]);
 
259
        } else { // return multiple nodes as a fragment
 
260
             ret = Y.DOM._nl2frag(nodes, doc);
 
261
        }
 
262
 
 
263
        if (ret) {
 
264
            Y.DOM._cloneCache[html] = ret.cloneNode(true);
 
265
        }
 
266
        return ret;
 
267
    },
 
268
 
 
269
    _nl2frag: function(nodes, doc) {
 
270
        var ret = null,
 
271
            i, len;
 
272
 
 
273
        if (nodes && (nodes.push || nodes.item) && nodes[0]) {
 
274
            doc = doc || nodes[0].ownerDocument; 
 
275
            ret = doc.createDocumentFragment();
 
276
 
 
277
            if (nodes.item) { // convert live list to static array
 
278
                nodes = Y.Array(nodes, 0, true);
 
279
            }
 
280
 
 
281
            for (i = 0, len = nodes.length; i < len; i++) {
 
282
                ret.appendChild(nodes[i]); 
 
283
            }
 
284
        } // else inline with log for minification
 
285
        else { Y.log('unable to convert ' + nodes + ' to fragment', 'warn', 'dom'); }
 
286
        return ret;
 
287
    },
 
288
 
 
289
 
 
290
    CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
 
291
        'for': 'htmlFor',
 
292
        'class': 'className'
 
293
    } : { // w3c
 
294
        'htmlFor': 'for',
 
295
        'className': 'class'
 
296
    },
 
297
 
 
298
    /**
 
299
     * Provides a normalized attribute interface. 
 
300
     * @method setAttibute
 
301
     * @param {String | HTMLElement} el The target element for the attribute.
 
302
     * @param {String} attr The attribute to set.
 
303
     * @param {String} val The value of the attribute.
 
304
     */
 
305
    setAttribute: function(el, attr, val, ieAttr) {
 
306
        if (el && el.setAttribute) {
 
307
            attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
 
308
            el.setAttribute(attr, val, ieAttr);
 
309
        }
 
310
    },
 
311
 
 
312
 
 
313
    /**
 
314
     * Provides a normalized attribute interface. 
 
315
     * @method getAttibute
 
316
     * @param {String | HTMLElement} el The target element for the attribute.
 
317
     * @param {String} attr The attribute to get.
 
318
     * @return {String} The current value of the attribute. 
 
319
     */
 
320
    getAttribute: function(el, attr, ieAttr) {
 
321
        ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
 
322
        var ret = '';
 
323
        if (el && el.getAttribute) {
 
324
            attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
 
325
            ret = el.getAttribute(attr, ieAttr);
 
326
 
 
327
            if (ret === null) {
 
328
                ret = ''; // per DOM spec
 
329
            }
 
330
        }
 
331
        return ret;
 
332
    },
 
333
 
 
334
    isWindow: function(obj) {
 
335
        return obj.alert && obj.document;
 
336
    },
 
337
 
 
338
    _fragClones: {
 
339
        div: document.createElement('div')
 
340
    },
 
341
 
 
342
    _create: function(html, doc, tag) {
 
343
        tag = tag || 'div';
 
344
 
 
345
        var frag = Y.DOM._fragClones[tag];
 
346
        if (frag) {
 
347
            frag = frag.cloneNode(false);
 
348
        } else {
 
349
            frag = Y.DOM._fragClones[tag] = doc.createElement(tag);
 
350
        }
 
351
        frag.innerHTML = html;
 
352
        return frag;
 
353
    },
 
354
 
 
355
    _removeChildNodes: function(node) {
 
356
        while (node.firstChild) {
 
357
            node.removeChild(node.firstChild);
 
358
        }
 
359
    },
 
360
 
 
361
    _cloneCache: {},
 
362
 
 
363
    /**
 
364
     * Inserts content in a node at the given location 
 
365
     * @method addHTML
 
366
     * @param {HTMLElement} node The node to insert into
 
367
     * @param {String} content The content to be inserted 
 
368
     * @param {String} where Where to insert the content; default is after lastChild 
 
369
     */
 
370
    addHTML: function(node, content, where) {
 
371
        if (typeof content === 'string') {
 
372
            content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
 
373
        }
 
374
 
 
375
        var newNode = Y.DOM._cloneCache[content],
 
376
            nodeParent = node.parentNode;
 
377
            
 
378
        if (newNode) {
 
379
            newNode = newNode.cloneNode(true);
 
380
        } else {
 
381
            if (content.nodeType) { // domNode
 
382
                newNode = content;
 
383
            } else { // create from string and cache
 
384
                newNode = Y.DOM.create(content);
 
385
            }
 
386
        }
 
387
 
 
388
        if (where) {
 
389
            if (where.nodeType) { // insert regardless of relationship to node
 
390
                // TODO: check if node.contains(where)?
 
391
                where.parentNode.insertBefore(newNode, where);
 
392
            } else {
 
393
                switch (where) {
 
394
                    case 'replace':
 
395
                        while (node.firstChild) {
 
396
                            node.removeChild(node.firstChild);
 
397
                        }
 
398
                        node.appendChild(newNode);
 
399
                        break;
 
400
                    case 'before':
 
401
                        nodeParent.insertBefore(newNode, node);
 
402
                        break;
 
403
                    case 'after':
 
404
                        if (node.nextSibling) { // IE errors if refNode is null
 
405
                            nodeParent.insertBefore(newNode, node.nextSibling);
 
406
                        } else {
 
407
                            nodeParent.appendChild(newNode);
 
408
                        }
 
409
                        break;
 
410
                    default:
 
411
                        node.appendChild(newNode);
 
412
                }
 
413
            }
 
414
        } else {
 
415
            node.appendChild(newNode);
 
416
        }
 
417
 
 
418
        return newNode;
 
419
    },
 
420
 
 
421
    VALUE_SETTERS: {},
 
422
 
 
423
    VALUE_GETTERS: {},
 
424
 
 
425
    getValue: function(node) {
 
426
        var ret = '', // TODO: return null?
 
427
            getter;
 
428
 
 
429
        if (node && node[TAG_NAME]) {
 
430
            getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
 
431
 
 
432
            if (getter) {
 
433
                ret = getter(node);
 
434
            } else {
 
435
                ret = node.value;
 
436
            }
 
437
        }
 
438
 
 
439
        return (typeof ret === 'string') ? ret : '';
 
440
    },
 
441
 
 
442
    setValue: function(node, val) {
 
443
        var setter;
 
444
 
 
445
        if (node && node[TAG_NAME]) {
 
446
            setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
 
447
 
 
448
            if (setter) {
 
449
                setter(node, val);
 
450
            } else {
 
451
                node.value = val;
 
452
            }
 
453
        }
 
454
    },
 
455
 
 
456
    /**
 
457
     * Brute force version of contains.
 
458
     * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
 
459
     * @method _bruteContains
 
460
     * @private
 
461
     * @param {HTMLElement} element The containing html element.
 
462
     * @param {HTMLElement} needle The html element that may be contained.
 
463
     * @return {Boolean} Whether or not the element is or contains the needle.
 
464
     */
 
465
    _bruteContains: function(element, needle) {
 
466
        while (needle) {
 
467
            if (element === needle) {
 
468
                return true;
 
469
            }
 
470
            needle = needle.parentNode;
 
471
        }
 
472
        return false;
 
473
    },
 
474
 
 
475
// TODO: move to Lang?
 
476
    /**
 
477
     * Memoizes dynamic regular expressions to boost runtime performance. 
 
478
     * @method _getRegExp
 
479
     * @private
 
480
     * @param {String} str The string to convert to a regular expression.
 
481
     * @param {String} flags optional An optinal string of flags.
 
482
     * @return {RegExp} An instance of RegExp
 
483
     */
 
484
    _getRegExp: function(str, flags) {
 
485
        flags = flags || '';
 
486
        Y.DOM._regexCache = Y.DOM._regexCache || {};
 
487
        if (!Y.DOM._regexCache[str + flags]) {
 
488
            Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
 
489
        }
 
490
        return Y.DOM._regexCache[str + flags];
 
491
    },
 
492
 
 
493
// TODO: make getDoc/Win true privates?
 
494
    /**
 
495
     * returns the appropriate document.
 
496
     * @method _getDoc
 
497
     * @private
 
498
     * @param {HTMLElement} element optional Target element.
 
499
     * @return {Object} The document for the given element or the default document. 
 
500
     */
 
501
    _getDoc: function(element) {
 
502
        element = element || {};
 
503
 
 
504
        return (element[NODE_TYPE] === 9) ? element : // element === document
 
505
                element[OWNER_DOCUMENT] || // element === DOM node
 
506
                element.document || // element === window
 
507
                Y.config.doc; // default
 
508
    },
 
509
 
 
510
    /**
 
511
     * returns the appropriate window.
 
512
     * @method _getWin
 
513
     * @private
 
514
     * @param {HTMLElement} element optional Target element.
 
515
     * @return {Object} The window for the given element or the default window. 
 
516
     */
 
517
    _getWin: function(element) {
 
518
        var doc = Y.DOM._getDoc(element);
 
519
        return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
 
520
    },
 
521
 
 
522
    _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
 
523
        fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
 
524
        var result,
 
525
            ret = [];
 
526
 
 
527
        if (fn && nodes) {
 
528
            Y.each(nodes, function(node) {
 
529
                if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
 
530
                    ret[ret.length] = result;
 
531
                }
 
532
            });
 
533
        }
 
534
 
 
535
        return ret.length ? ret : nodes;
 
536
    },
 
537
 
 
538
    _testElement: function(element, tag, fn) {
 
539
        tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
 
540
        return (element && element[TAG_NAME] &&
 
541
                (!tag || element[TAG_NAME].toUpperCase() === tag) &&
 
542
                (!fn || fn(element)));
 
543
    },
 
544
 
 
545
    creators: {},
 
546
 
 
547
    _IESimpleCreate: function(html, doc) {
 
548
        doc = doc || Y.config.doc;
 
549
        return doc.createElement(html);
 
550
    }
 
551
};
 
552
 
 
553
 
 
554
(function(Y) {
 
555
    var creators = Y.DOM.creators,
 
556
        create = Y.DOM.create,
 
557
        re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
 
558
 
 
559
        TABLE_OPEN = '<table>',
 
560
        TABLE_CLOSE = '</table>';
 
561
 
 
562
    if (Y.UA.ie) {
 
563
        Y.mix(creators, {
 
564
        // TODO: thead/tfoot with nested tbody
 
565
            // IE adds TBODY when creating TABLE elements (which may share this impl)
 
566
            tbody: function(html, doc) {
 
567
                var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
 
568
                    tb = frag.children.tags('tbody')[0];
 
569
 
 
570
                if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
 
571
                    tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
 
572
                }
 
573
                return frag;
 
574
            },
 
575
 
 
576
            script: function(html, doc) {
 
577
                var frag = doc.createElement('div');
 
578
 
 
579
                frag.innerHTML = '-' + html;
 
580
                frag.removeChild(frag[FIRST_CHILD]);
 
581
                return frag;
 
582
            }
 
583
 
 
584
        }, true);
 
585
 
 
586
        Y.mix(Y.DOM.VALUE_GETTERS, {
 
587
            button: function(node) {
 
588
                return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
 
589
            }
 
590
        });
 
591
 
 
592
        Y.mix(Y.DOM.VALUE_SETTERS, {
 
593
            // IE: node.value changes the button text, which should be handled via innerHTML
 
594
            button: function(node, val) {
 
595
                var attr = node.attributes.value;
 
596
                if (!attr) {
 
597
                    attr = node[OWNER_DOCUMENT].createAttribute('value');
 
598
                    node.setAttributeNode(attr);
 
599
                }
 
600
 
 
601
                attr.value = val;
 
602
            }
 
603
        });
 
604
    }
 
605
 
 
606
    if (Y.UA.gecko || Y.UA.ie) {
 
607
        Y.mix(creators, {
 
608
            option: function(html, doc) {
 
609
                return create('<select>' + html + '</select>', doc);
 
610
            },
 
611
 
 
612
            tr: function(html, doc) {
 
613
                return create('<tbody>' + html + '</tbody>', doc);
 
614
            },
 
615
 
 
616
            td: function(html, doc) {
 
617
                return create('<tr>' + html + '</tr>', doc);
 
618
            }, 
 
619
 
 
620
            tbody: function(html, doc) {
 
621
                return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
 
622
            }
 
623
        });
 
624
 
 
625
        Y.mix(creators, {
 
626
            legend: 'fieldset',
 
627
            th: creators.td,
 
628
            thead: creators.tbody,
 
629
            tfoot: creators.tbody,
 
630
            caption: creators.tbody,
 
631
            colgroup: creators.tbody,
 
632
            col: creators.tbody,
 
633
            optgroup: creators.option
 
634
        });
 
635
    }
 
636
 
 
637
    Y.mix(Y.DOM.VALUE_GETTERS, {
 
638
        option: function(node) {
 
639
            var attrs = node.attributes;
 
640
            return (attrs.value && attrs.value.specified) ? node.value : node.text;
 
641
        },
 
642
 
 
643
        select: function(node) {
 
644
            var val = node.value,
 
645
                options = node.options;
 
646
 
 
647
            if (options && val === '') {
 
648
                if (node.multiple) {
 
649
                    Y.log('multiple select normalization not implemented', 'warn', 'DOM');
 
650
                } else {
 
651
                    val = Y.DOM.getValue(options[node.selectedIndex], 'value');
 
652
                }
 
653
            }
 
654
 
 
655
            return val;
 
656
        }
 
657
    });
 
658
})(Y);
 
659
 
 
660
})(Y);
 
661
var addClass, hasClass, removeClass;
 
662
 
 
663
Y.mix(Y.DOM, {
 
664
    /**
 
665
     * Determines whether a DOM element has the given className.
 
666
     * @method hasClass
 
667
     * @param {HTMLElement} element The DOM element. 
 
668
     * @param {String} className the class name to search for
 
669
     * @return {Boolean} Whether or not the element has the given class. 
 
670
     */
 
671
    hasClass: function(node, className) {
 
672
        var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
 
673
        return re.test(node.className);
 
674
    },
 
675
 
 
676
    /**
 
677
     * Adds a class name to a given DOM element.
 
678
     * @method addClass         
 
679
     * @param {HTMLElement} element The DOM element. 
 
680
     * @param {String} className the class name to add to the class attribute
 
681
     */
 
682
    addClass: function(node, className) {
 
683
        if (!Y.DOM.hasClass(node, className)) { // skip if already present 
 
684
            node.className = Y.Lang.trim([node.className, className].join(' '));
 
685
        }
 
686
    },
 
687
 
 
688
    /**
 
689
     * Removes a class name from a given element.
 
690
     * @method removeClass         
 
691
     * @param {HTMLElement} element The DOM element. 
 
692
     * @param {String} className the class name to remove from the class attribute
 
693
     */
 
694
    removeClass: function(node, className) {
 
695
        if (className && hasClass(node, className)) {
 
696
            node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
 
697
                            className + '(?:\\s+|$)'), ' '));
 
698
 
 
699
            if ( hasClass(node, className) ) { // in case of multiple adjacent
 
700
                removeClass(node, className);
 
701
            }
 
702
        }                 
 
703
    },
 
704
 
 
705
    /**
 
706
     * Replace a class with another class for a given element.
 
707
     * If no oldClassName is present, the newClassName is simply added.
 
708
     * @method replaceClass  
 
709
     * @param {HTMLElement} element The DOM element. 
 
710
     * @param {String} oldClassName the class name to be replaced
 
711
     * @param {String} newClassName the class name that will be replacing the old class name
 
712
     */
 
713
    replaceClass: function(node, oldC, newC) {
 
714
        //Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node');
 
715
        addClass(node, newC);
 
716
        removeClass(node, oldC);
 
717
    },
 
718
 
 
719
    /**
 
720
     * If the className exists on the node it is removed, if it doesn't exist it is added.
 
721
     * @method toggleClass  
 
722
     * @param {HTMLElement} element The DOM element. 
 
723
     * @param {String} className the class name to be toggled
 
724
     */
 
725
    toggleClass: function(node, className) {
 
726
        if (hasClass(node, className)) {
 
727
            removeClass(node, className);
 
728
        } else {
 
729
            addClass(node, className);
 
730
        }
 
731
    }
 
732
});
 
733
 
 
734
hasClass = Y.DOM.hasClass;
 
735
removeClass = Y.DOM.removeClass;
 
736
addClass = Y.DOM.addClass;
 
737
 
 
738
 
 
739
 
 
740
}, '3.0.0' ,{requires:['oop']});