~mnordhoff/loggerhead/valid-html

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/yui/build/node/node-base.js

  • Committer: Matt Nordhoff
  • Date: 2008-10-25 21:09:19 UTC
  • mfrom: (229.2.4 trunk)
  • Revision ID: mnordhoff@mattnordhoff.com-20081025210919-cbnlvd2ow5qpnxvh
Merge trunk.

This undoes the whitespace adjustment I made in macros.pt.

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.0pr1
 
6
*/
 
7
YUI.add('node-base', function(Y) {
 
8
 
 
9
    /**
 
10
     * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
 
11
     * @module node
 
12
     * @submodule node-base
 
13
     */     
 
14
 
 
15
    /**
 
16
     * The Node class provides a wrapper for manipulating DOM Nodes.
 
17
     * Node properties can be accessed via the set/get methods.
 
18
     * Use Y.get() to retrieve Node instances.
 
19
     *
 
20
     * <strong>NOTE:</strong> All node properties are accessed using the
 
21
     * <code>set</code> and <code>get</code> methods.
 
22
     *
 
23
     * @class Node
 
24
     * @constructor
 
25
     */
 
26
 
 
27
    var BASE_NODE                   = 0, 
 
28
        ELEMENT_NODE                = 1,
 
29
        //ATTRIBUTE_NODE              = 2,
 
30
        //TEXT_NODE                   = 3,
 
31
        //CDATA_SECTION_NODE          = 4,
 
32
        //ENTITY_REFERENCE_NODE       = 5,
 
33
        //ENTITY_NODE                 = 6,
 
34
        //PROCESSING_INSTRUCTION_NODE = 7,
 
35
        //COMMENT_NODE                = 8,
 
36
        DOCUMENT_NODE               = 9; //,
 
37
        //DOCUMENT_TYPE_NODE          = 10,
 
38
        //DOCUMENT_FRAGMENT_NODE      = 11,
 
39
        //NOTATION_NODE               = 12;
 
40
 
 
41
    var OWNER_DOCUMENT = 'ownerDocument',
 
42
        TAG_NAME = 'tagName',
 
43
        NODE_NAME = 'nodeName',
 
44
        NODE_TYPE = 'nodeType';
 
45
 
 
46
    var RE_VALID_PROP_TYPES = /(?:string|boolean|number)/;
 
47
 
 
48
    var Selector = Y.Selector;
 
49
    var _instances = {};
 
50
    var _nodes = {};
 
51
    var _nodelists = {};
 
52
    var _restrict = null;
 
53
 
 
54
    var slice = [].slice;
 
55
 
 
56
    var wrapDOM = function(node) {
 
57
        var ret = null,
 
58
            yuid = (node) ? node._yuid : null,
 
59
            instance = _instances[yuid],
 
60
            existingNode = _nodes[yuid];
 
61
 
 
62
        if (node) {
 
63
            if (NODE_TYPE in node) {
 
64
                if (instance && existingNode && node === existingNode) {
 
65
                    ret = instance; // reuse existing Nodes if nodes match
 
66
                } else {
 
67
                    ret = new Node(node);
 
68
                }
 
69
            } else if ('item' in node || 'push' in node) {
 
70
                ret = new Y.NodeList(node);
 
71
            }
 
72
        }
 
73
        return ret;
 
74
    };
 
75
 
 
76
    var wrapFn = function(fn) {
 
77
        var ret = null;
 
78
        if (fn) {
 
79
            ret = (typeof fn === 'string') ?
 
80
            function(n) {
 
81
                return Y.Selector.test(n, fn);
 
82
            } : 
 
83
            function(n) {
 
84
                return fn(_instances[n._yuid]);
 
85
            };
 
86
        }
 
87
 
 
88
        return ret;
 
89
    };
 
90
 
 
91
    var getDoc = function(node) {
 
92
        node = _nodes[node._yuid];
 
93
        return (node[NODE_TYPE] === 9) ? node : node[OWNER_DOCUMENT];
 
94
    };
 
95
 
 
96
    // returns HTMLElement
 
97
    var getDOMNode = function(node) {
 
98
        if (node && !node.nodeType && node._yuid) {
 
99
            node = _nodes[node._yuid];
 
100
        }
 
101
 
 
102
        return  node || null;
 
103
 
 
104
    };
 
105
 
 
106
    /*
 
107
     * Wraps the input and outputs of a node instance
 
108
     */
 
109
    var nodeInOut = function(method, a, b, c, d, e) {
 
110
        if (a) { // first 2 may be Node instances or nodes (TODO: or strings?)
 
111
            a = getDOMNode(a);
 
112
            if (b) {
 
113
                b = getDOMNode(b);
 
114
            }
 
115
        }
 
116
        return wrapDOM(_nodes[this._yuid][method](a, b, c, d, e));
 
117
    };
 
118
 
 
119
    /*
 
120
     * Wraps the return value in a node instance
 
121
     */
 
122
    var nodeOut = function(method, a, b, c, d, e) {
 
123
        return wrapDOM(_nodes[this._yuid][method](a, b, c, d, e));
 
124
    };
 
125
 
 
126
    /* 
 
127
     * Returns directy from node method call 
 
128
     */
 
129
    var rawOut = function(method, a, b, c, d, e) {
 
130
        return _nodes[this._yuid][method](a, b, c, d, e);
 
131
    };
 
132
 
 
133
    var noOut = function(method, a, b, c, d, e) {
 
134
        _nodes[this._yuid][method](a, b, c, d, e);
 
135
        return this;
 
136
    };
 
137
 
 
138
    var PROPS_WRAP = {
 
139
        /**
 
140
         * Returns a Node instance. 
 
141
         * @property parentNode
 
142
         * @type Node
 
143
         */
 
144
        'parentNode': BASE_NODE,
 
145
 
 
146
        /**
 
147
         * Returns a NodeList instance. 
 
148
         * @property childNodes
 
149
         * @type NodeList
 
150
         */
 
151
        'childNodes': BASE_NODE,
 
152
 
 
153
        /**
 
154
         * Returns a NodeList instance. 
 
155
         * @property children
 
156
         * @type NodeList
 
157
         */
 
158
        'children': function(node) {
 
159
            node = _nodes[node._yuid];
 
160
            var children = node.children;
 
161
 
 
162
            if (children === undefined) {
 
163
                var childNodes = node.childNodes;
 
164
                children = [];
 
165
 
 
166
                for (var i = 0, len = childNodes.length; i < len; ++i) {
 
167
                    if (childNodes[i][TAG_NAME]) {
 
168
                        children[children.length] = childNodes[i];
 
169
                    }
 
170
                }
 
171
            }
 
172
            return children;
 
173
        },
 
174
 
 
175
        /**
 
176
         * Returns a Node instance. 
 
177
         * @property firstChild
 
178
         * @type Node
 
179
         */
 
180
        'firstChild': BASE_NODE,
 
181
 
 
182
        /**
 
183
         * Returns a Node instance. 
 
184
         * @property lastChild
 
185
         * @type Node
 
186
         */
 
187
        'lastChild': BASE_NODE,
 
188
 
 
189
        /**
 
190
         * Returns a Node instance. 
 
191
         * @property previousSibling
 
192
         * @type Node
 
193
         */
 
194
        'previousSibling': BASE_NODE,
 
195
 
 
196
        /**
 
197
         * Returns a Node instance. 
 
198
         * @property previousSibling
 
199
         * @type Node
 
200
         */
 
201
        'nextSibling': BASE_NODE,
 
202
 
 
203
        /**
 
204
         * Returns a Node instance. 
 
205
         * @property ownerDocument
 
206
         * @type Doc
 
207
         */
 
208
        'ownerDocument': BASE_NODE,
 
209
 
 
210
        /**
 
211
         * Returns a Node instance. 
 
212
         * @property offsetParent
 
213
         * @type Node
 
214
         */
 
215
        'offsetParent': ELEMENT_NODE,
 
216
 
 
217
        /**
 
218
         * Returns a Node instance. 
 
219
         * @property documentElement
 
220
         * @type Node
 
221
         */
 
222
        'documentElement': DOCUMENT_NODE,
 
223
 
 
224
        /**
 
225
         * Returns a Node instance. 
 
226
         * @property body
 
227
         * @type Node
 
228
         */
 
229
        'body': DOCUMENT_NODE,
 
230
 
 
231
        // form
 
232
        /**
 
233
         * Returns a NodeList instance. 
 
234
         * @property elements
 
235
         * @type NodeList
 
236
         */
 
237
        'elements': ELEMENT_NODE,
 
238
 
 
239
        // table
 
240
        /**
 
241
         * Returns a NodeList instance. 
 
242
         * @property rows
 
243
         * @type NodeList
 
244
         */
 
245
        'rows': ELEMENT_NODE,
 
246
 
 
247
        /**
 
248
         * Returns a NodeList instance. 
 
249
         * @property cells
 
250
         * @type NodeList
 
251
         */
 
252
        'cells': ELEMENT_NODE,
 
253
 
 
254
        /**
 
255
         * Returns a Node instance. 
 
256
         * @property tHead
 
257
         * @type Node
 
258
         */
 
259
        'tHead': ELEMENT_NODE,
 
260
 
 
261
        /**
 
262
         * Returns a Node instance. 
 
263
         * @property tFoot
 
264
         * @type Node
 
265
         */
 
266
        'tFoot': ELEMENT_NODE,
 
267
 
 
268
        /**
 
269
         * Returns a NodeList instance. 
 
270
         * @property tBodies
 
271
         * @type NodeList
 
272
         */
 
273
        'tBodies': ELEMENT_NODE
 
274
    };
 
275
    var METHODS = {
 
276
        /**
 
277
         * Passes through to DOM method.
 
278
         * @method replaceChild
 
279
         * @param {HTMLElement | Node} node Node to be inserted 
 
280
         * @param {HTMLElement | Node} refNode Node to be replaced 
 
281
         * @return {Node} The replaced node 
 
282
         */
 
283
        replaceChild: nodeInOut,
 
284
 
 
285
        /**
 
286
         * Passes through to DOM method.
 
287
         * @method appendChild
 
288
         * @param {HTMLElement | Node} node Node to be appended 
 
289
         * @return {Node} The appended node 
 
290
         */
 
291
        appendChild: nodeInOut,
 
292
 
 
293
        /**
 
294
         * Passes through to DOM method.
 
295
         * @method insertBefore
 
296
         * @param {HTMLElement | Node} newNode Node to be appended 
 
297
         * @param {HTMLElement | Node} refNode Node to be inserted before 
 
298
         * @return {Node} The inserted node 
 
299
         */
 
300
        insertBefore: nodeInOut,
 
301
 
 
302
        /**
 
303
         * Passes through to DOM method.
 
304
         * @method removeChild
 
305
         * @param {HTMLElement | Node} node Node to be removed 
 
306
         * @return {Node} The removed node 
 
307
         */
 
308
        removeChild: nodeInOut,
 
309
 
 
310
        /**
 
311
         * Passes through to DOM method.
 
312
         * @method hasChildNodes
 
313
         * @return {Boolean} Whether or not the node has any childNodes 
 
314
         */
 
315
        hasChildNodes: rawOut,
 
316
 
 
317
        /**
 
318
         * Passes through to DOM method.
 
319
         * @method cloneNode
 
320
         * @param {HTMLElement | Node} node Node to be cloned 
 
321
         * @return {Node} The clone 
 
322
         */
 
323
        cloneNode: nodeOut,
 
324
 
 
325
        /**
 
326
         * Passes through to DOM method.
 
327
         * @method getAttribute
 
328
         * @param {String} attribute The attribute to retrieve 
 
329
         * @return {String} The current value of the attribute 
 
330
         */
 
331
        getAttribute: rawOut,
 
332
 
 
333
        /**
 
334
         * Passes through to DOM method.
 
335
         * @method setAttribute
 
336
         * @param {String} attribute The attribute to set 
 
337
         * @param {String} The value to apply to the attribute 
 
338
         * @chainable
 
339
         */
 
340
        setAttribute: noOut,
 
341
 
 
342
        /**
 
343
         * Passes through to DOM method.
 
344
         * @method hasAttribute
 
345
         * @param {String} attribute The attribute to test for 
 
346
         * @return {Boolean} Whether or not the attribute is present 
 
347
         */
 
348
        hasAttribute: rawOut,
 
349
 
 
350
        /**
 
351
         * Passes through to DOM method.
 
352
         * @method scrollIntoView
 
353
         * @chainable
 
354
         */
 
355
        scrollIntoView: noOut,
 
356
 
 
357
        /**
 
358
         * Passes through to DOM method.
 
359
         * @method getElementsByTagName
 
360
         * @param {String} tagName The tagName to collect 
 
361
         * @return {NodeList} A NodeList representing the HTMLCollection
 
362
         */
 
363
        getElementsByTagName: nodeOut,
 
364
 
 
365
        /**
 
366
         * Passes through to DOM method.
 
367
         * @method focus
 
368
         * @chainable
 
369
         */
 
370
        focus: noOut,
 
371
 
 
372
        /**
 
373
         * Passes through to DOM method.
 
374
         * @method blur
 
375
         * @chainable
 
376
         */
 
377
        blur: noOut,
 
378
 
 
379
        /**
 
380
         * Passes through to DOM method.
 
381
         * Only valid on FORM elements
 
382
         * @method submit
 
383
         * @chainable
 
384
         */
 
385
        submit: noOut,
 
386
 
 
387
        /**
 
388
         * Passes through to DOM method.
 
389
         * Only valid on FORM elements
 
390
         * @method reset
 
391
         * @chainable
 
392
         */
 
393
        reset: noOut
 
394
    };
 
395
 
 
396
    var addNodeListMethod = function(name) {
 
397
        NodeList.prototype[name] = function() {
 
398
            var a = [],
 
399
                nodes = _nodelists[this._yuid],
 
400
                ret;
 
401
 
 
402
            for (var i = 0, len = nodes.length; i < len; ++i) {
 
403
                _nodes[_tmpNode._yuid] = nodes[i];
 
404
                ret = _tmpNode[name].apply(_tmpNode, arguments);
 
405
                if (ret !== _tmpNode) {
 
406
                    a[i] = ret;
 
407
                }
 
408
            }
 
409
 
 
410
            return a.length ? a : this;
 
411
        };
 
412
    };
 
413
 
 
414
    var METHODS_INVOKE = {
 
415
        'getBoundingClientRect': true
 
416
    };
 
417
 
 
418
    var Node = function(node) {
 
419
        if (!node || !node[NODE_TYPE]) {
 
420
            return null;
 
421
        }
 
422
        
 
423
        var yuid = Y.guid();
 
424
        try { // IE errors on non-element expandos (cant be reused)
 
425
            node._yuid = yuid;
 
426
        } catch(e) {}
 
427
        this._yuid = yuid;
 
428
        _nodes[yuid] = node;
 
429
        _instances[yuid] = this;
 
430
 
 
431
    };
 
432
 
 
433
    var SETTERS = {};
 
434
    var GETTERS = {
 
435
        /**
 
436
         * Normalizes nodeInnerText and textContent. 
 
437
         * @property text
 
438
         * @type String
 
439
         */
 
440
        'text': function(node) {
 
441
            return Y.DOM.getText(_nodes[node._yuid]);
 
442
        },
 
443
 
 
444
        /**
 
445
         * Returns a nodeList of option elements 
 
446
         * @property options
 
447
         * @type String
 
448
         */
 
449
        'options': function(node) {
 
450
            return (node) ? node.getElementsByTagName('option') : [];
 
451
        }
 
452
    };
 
453
 
 
454
    Node.setters = function(prop, fn) {
 
455
        if (typeof prop == 'string') {
 
456
            SETTERS[prop] = fn;
 
457
        } else { // assume object
 
458
            Y.each(prop, function(fn, prop) {
 
459
                Node.setters(prop, fn);
 
460
            });
 
461
        } 
 
462
    };
 
463
 
 
464
    Node.getters = function(prop, fn) {
 
465
        if (typeof prop == 'string') {
 
466
            GETTERS[prop] = fn;
 
467
        } else { // assume object
 
468
            Y.each(prop, function(fn, prop) {
 
469
                Node.getters(prop, fn);
 
470
            });
 
471
        } 
 
472
    };
 
473
 
 
474
    Node.methods = function(name, fn) {
 
475
        if (typeof name == 'string') {
 
476
            Node.prototype[name] = function() {
 
477
                var args = slice.call(arguments);
 
478
                args.unshift(this);
 
479
                var ret = fn.apply(null, args);
 
480
                if (ret === undefined) {
 
481
                    ret = this;
 
482
                }
 
483
                return ret;
 
484
            };
 
485
 
 
486
            addNodeListMethod(name);
 
487
 
 
488
 
 
489
        } else { // assume object
 
490
            Y.each(name, function(fn, name) {
 
491
                Node.methods(name, fn);
 
492
            });
 
493
        }
 
494
    };
 
495
 
 
496
    Node.getDOMNode = function(node) {
 
497
        var ret;
 
498
 
 
499
        if (node.nodeType) {
 
500
            ret = node;
 
501
        } else if (typeof node === 'string') {
 
502
            ret = Selector.query(node, null, true);
 
503
        } else {
 
504
            ret = _nodes[node._yuid];
 
505
        }
 
506
        return ret || null;
 
507
    };
 
508
 
 
509
    Node.wrapDOMMethod = function(name) {
 
510
        return function() {
 
511
            var args = slice.call(arguments);
 
512
            args.unshift(Y.Node.getDOMNode(args.shift()));
 
513
            return Y.DOM[name].apply(Y.DOM, args);
 
514
        };
 
515
 
 
516
    };
 
517
 
 
518
    Node.addDOMMethods = function(methods) {
 
519
        var add = {}; 
 
520
        Y.each(methods, function(v, n) {
 
521
            add[v] = Y.Node.wrapDOMMethod(v);
 
522
        });
 
523
 
 
524
        Y.Node.methods(add);
 
525
    };
 
526
 
 
527
    Node.prototype = {
 
528
        /**
 
529
         * Set the value of the property/attribute on the HTMLElement bound to this Node.
 
530
         * Only strings/numbers/booleans are passed through unless a SETTER exists.
 
531
         * @method set
 
532
         * @param {String} prop Property to set 
 
533
         * @param {any} val Value to apply to the given property
 
534
         * @chainable
 
535
         */
 
536
        set: function(prop, val) {
 
537
            var node = _nodes[this._yuid];
 
538
            if (prop in SETTERS) { // use custom setter
 
539
                SETTERS[prop](this, prop, val);  // passing Node instance
 
540
            } else if (RE_VALID_PROP_TYPES.test(typeof node[prop])) { // safe to write
 
541
                node[prop] = val;
 
542
            }
 
543
            return this;
 
544
        },
 
545
 
 
546
        /**
 
547
         * Get the value of the property/attribute on the HTMLElement bound to this Node.
 
548
         * Only strings/numbers/booleans are passed through unless a GETTER exists.
 
549
         * @method get
 
550
         * @param {String} prop Property to get 
 
551
         * @return {any} Current value of the property
 
552
         */
 
553
        get: function(prop) {
 
554
            var val;
 
555
            var node = _nodes[this._yuid];
 
556
            if (prop in GETTERS) { // use custom getter
 
557
                val = GETTERS[prop](this, prop);
 
558
            } else if (prop in PROPS_WRAP) { // wrap DOM object (HTMLElement, HTMLCollection, Document)
 
559
                if (typeof PROPS_WRAP[prop] === 'function') {
 
560
                    val = PROPS_WRAP[prop](this);
 
561
                } else {
 
562
                    val = node[prop];
 
563
                }
 
564
 
 
565
                if (_restrict && _restrict[this._yuid] && !Y.DOM.contains(node, val)) {
 
566
                    val = null; // not allowed to go outside of root node
 
567
                } else {
 
568
                    val = wrapDOM(val);
 
569
                }
 
570
            } else if (RE_VALID_PROP_TYPES.test(typeof node[prop])) { // safe to read
 
571
                val = node[prop];
 
572
            }
 
573
            return val;
 
574
        },
 
575
 
 
576
        invoke: function(method, a, b, c, d, e) {
 
577
            if (a) { // first 2 may be Node instances or strings
 
578
                a = (a[NODE_TYPE]) ? a : getDOMNode(a);
 
579
                if (b) {
 
580
                    b = (b[NODE_TYPE]) ? b : getDOMNode(b);
 
581
                }
 
582
            }
 
583
            var node = _nodes[this._yuid];
 
584
            if (node && METHODS_INVOKE[method] && node[method]) {
 
585
                return node[method](a, b, c, d, e);
 
586
            }
 
587
            return null;
 
588
        },
 
589
 
 
590
        hasMethod: function(method) {
 
591
            return !!(METHODS_INVOKE[method] && _nodes[this._yuid][method]);
 
592
        },
 
593
 
 
594
        //normalize: function() {},
 
595
        //isSupported: function(feature, version) {},
 
596
        toString: function() {
 
597
            var node = _nodes[this._yuid] || {};
 
598
            return node.id || node[NODE_NAME] || 'undefined node';
 
599
        },
 
600
 
 
601
        /**
 
602
         * Retrieves a single node based on the given CSS selector. 
 
603
         * @method query
 
604
         *
 
605
         * @param {string} selector The CSS selector to test against.
 
606
         * @return {Node} A Node instance for the matching HTMLElement.
 
607
         */
 
608
        query: function(selector) {
 
609
            return wrapDOM(Selector.query(selector, _nodes[this._yuid], true));
 
610
        },
 
611
 
 
612
        /**
 
613
         * Retrieves a nodeList based on the given CSS selector. 
 
614
         * @method queryAll
 
615
         *
 
616
         * @param {string} selector The CSS selector to test against.
 
617
         * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
 
618
         */
 
619
        queryAll: function(selector) {
 
620
            return wrapDOM(Selector.query(selector, _nodes[this._yuid]));
 
621
        },
 
622
 
 
623
        /**
 
624
         * Test if the supplied node matches the supplied selector.
 
625
         * @method test
 
626
         *
 
627
         * @param {string} selector The CSS selector to test against.
 
628
         * @return {boolean} Whether or not the node matches the selector.
 
629
         */
 
630
        test: function(selector) {
 
631
            return Selector.test(_nodes[this._yuid], selector);
 
632
        },
 
633
 
 
634
        /**
 
635
         * Compares nodes to determine if they match.
 
636
         * Node instances can be compared to each other and/or HTMLElements.
 
637
         * @method compareTo
 
638
         * @param {HTMLElement | Node} refNode The reference node to compare to the node.
 
639
         * @return {Boolean} True if the nodes match, false if they do not. 
 
640
         */
 
641
        compareTo: function(refNode) {
 
642
            refNode = refNode[NODE_TYPE] ? refNode : _nodes[refNode._yuid];
 
643
            return _nodes[this._yuid] === refNode;
 
644
        },
 
645
 
 
646
       /*
 
647
         * Returns the nearest ancestor that passes the test applied by supplied boolean method.
 
648
         * @method ancestor
 
649
         * @param {String | Function} fn A selector or boolean method for testing elements.
 
650
         * If a function is used, it receives the current node being tested as the only argument.
 
651
         * @return {Node} The matching Node instance or null if not found
 
652
         */
 
653
        ancestor: function(fn) {
 
654
            return wrapDOM(Y.DOM.elementByAxis(_nodes[this._yuid], 'parentNode', wrapFn(fn)));
 
655
        },
 
656
 
 
657
        /**
 
658
         * Returns the previous matching sibling. 
 
659
         * Returns the nearest element node sibling if no method provided.
 
660
         * @method previous
 
661
         * @param {String | Function} fn A selector or boolean method for testing elements.
 
662
         * If a function is used, it receives the current node being tested as the only argument.
 
663
         * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
 
664
         * @return {Node} Node instance or null if not found
 
665
         */
 
666
        previous: function(fn, all) {
 
667
            return wrapDOM(Y.DOM.elementByAxis(_nodes[this._yuid], 'previousSibling', wrapFn(fn)), all);
 
668
        }, 
 
669
 
 
670
        /**
 
671
         * Returns the next matching sibling. 
 
672
         * Returns the nearest element node sibling if no method provided.
 
673
         * @method next
 
674
         * @param {String | Function} fn A selector or boolean method for testing elements.
 
675
         * If a function is used, it receives the current node being tested as the only argument.
 
676
         * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
 
677
         * @return {Node} Node instance or null if not found
 
678
         */
 
679
        next: function(fn, all) {
 
680
            return wrapDOM(Y.DOM.elementByAxis(_nodes[this._yuid], 'nextSibling', wrapFn(fn)), all);
 
681
        },
 
682
        
 
683
       /**
 
684
         * Attaches a DOM event handler.
 
685
         * @method attach
 
686
         * @param {String} type The type of DOM Event to listen for 
 
687
         * @param {Function} fn The handler to call when the event fires 
 
688
         * @param {Object} arg An argument object to pass to the handler 
 
689
         */
 
690
 
 
691
        attach: function(type, fn, arg) {
 
692
            var args = slice.call(arguments, 0);
 
693
            args.unshift(_nodes[this._yuid]);
 
694
            return Y.Event.addListener.apply(Y.Event, args);
 
695
        },
 
696
 
 
697
       /**
 
698
         * Alias for attach.
 
699
         * @method on
 
700
         * @param {String} type The type of DOM Event to listen for 
 
701
         * @param {Function} fn The handler to call when the event fires 
 
702
         * @param {Object} arg An argument object to pass to the handler 
 
703
         * @see attach
 
704
         */
 
705
 
 
706
        on: function(type, fn, arg) {
 
707
            return this.attach.apply(this, arguments);
 
708
        },
 
709
 
 
710
        addEventListener: function(type, fn, arg) {
 
711
            return Y.Event.nativeAdd(_nodes[this._yuid], type, fn, arg);
 
712
        },
 
713
        
 
714
       /**
 
715
         * Detaches a DOM event handler. 
 
716
         * @method detach
 
717
         * @param {String} type The type of DOM Event
 
718
         * @param {Function} fn The handler to call when the event fires 
 
719
         */
 
720
        detach: function(type, fn) {
 
721
            var args = slice.call(arguments, 0);
 
722
            args.unshift(_nodes[this._yuid]);
 
723
            return Y.Event.removeListener.apply(Y.Event, args);
 
724
        },
 
725
 
 
726
        removeEventListener: function(type, fn) {
 
727
            return Y.Event.nativeRemove(_nodes[this._yuid], type, fn);
 
728
        },
 
729
 
 
730
       /**
 
731
         * Creates a Node instance from HTML string
 
732
         * @method create
 
733
         * @param {String|Array} html The string of html to create
 
734
         * @return {Node} A new Node instance 
 
735
         */
 
736
        create: function(html) {
 
737
            return Y.Node.create(html);
 
738
        },
 
739
 
 
740
        /**
 
741
         * Determines whether the ndoe is an ancestor of another HTML element in the DOM hierarchy.
 
742
         * @method contains
 
743
         * @param {Node | HTMLElement} needle The possible node or descendent
 
744
         * @return {Boolean} Whether or not this node is the needle its ancestor
 
745
         */
 
746
        contains: function(needle) {
 
747
            return Y.DOM.contains(_nodes[this._yuid], getDOMNode(needle));
 
748
        },
 
749
 
 
750
        /**
 
751
         * Applies the supplied plugin to the node.
 
752
         * @method plug
 
753
         * @param {Function} The plugin Class to apply
 
754
         * @param {Object} config An optional config to pass to the constructor
 
755
         * @chainable
 
756
         */
 
757
        plug: function(PluginClass, config) {
 
758
            config = config || {};
 
759
            config.owner = this;
 
760
            if (PluginClass && PluginClass.NS) {
 
761
                this[PluginClass.NS] = new PluginClass(config);
 
762
            }
 
763
            return this;
 
764
        },
 
765
 
 
766
        /**
 
767
         * Determines whether the node is appended to the document.
 
768
         * @method inDoc
 
769
         * @param {Node|HTMLElement} doc optional An optional document to check against.
 
770
         * Defaults to current document. 
 
771
         * @return {Boolean} Whether or not this node is appended to the document. 
 
772
         */
 
773
        inDoc: function(doc) {
 
774
            var node = _nodes[this._yuid];
 
775
            doc = (doc) ? getDoc(doc) : node.ownerDocument;
 
776
            if (doc.documentElement) {
 
777
                return Y.DOM.contains(doc.documentElement, node);
 
778
            }
 
779
        }
 
780
    };
 
781
 
 
782
    Y.each(METHODS, function(fn, method) {
 
783
        Node.prototype[method] = function() {
 
784
            return fn.apply(this, [method].concat(slice.call(arguments)));
 
785
        };
 
786
    });
 
787
 
 
788
    /** 
 
789
     * Creates a Node instance from an HTML string
 
790
     * @method create
 
791
     * @param {String} html HTML string
 
792
     */
 
793
    Node.create = function(html) {
 
794
        return wrapDOM(Y.DOM.create(html));
 
795
    };
 
796
 
 
797
    Node.getById = function(id, doc) {
 
798
        doc = (doc && doc[NODE_TYPE]) ? doc : Y.config.doc;
 
799
        return wrapDOM(doc.getElementById(id));
 
800
    };
 
801
 
 
802
    /**
 
803
     * Retrieves a Node instance for the given object/string. 
 
804
     * Note: Use 'document' string to retrieve document Node instance from string
 
805
     * @method get
 
806
     * @static
 
807
     * @param {document|HTMLElement|HTMLCollection|Array|String} node The object to wrap.
 
808
     * @param {document|Node} doc optional The document containing the node. Defaults to current document.
 
809
     * @param {boolean} isRoot optional Whether or not this node should be treated as a root node. Root nodes
 
810
     * aren't allowed to traverse outside their DOM tree.
 
811
     * @return {Node} A wrapper instance for the supplied object.
 
812
     */
 
813
    Node.get = function(node, doc, isRoot) {
 
814
        if (node instanceof Node) {
 
815
            return node;
 
816
        }
 
817
 
 
818
        if (!doc) {
 
819
            doc = Y.config.doc;
 
820
        } else if (doc._yuid && _nodes[doc._yuid]) {
 
821
            doc = _nodes[doc._yuid]; 
 
822
        }
 
823
    
 
824
        if (node && typeof node === 'string') {
 
825
            if (node === 'document') {
 
826
                node = Y.config.doc;
 
827
            } else {
 
828
                node = Y.Selector.query(node, doc, true);
 
829
            }
 
830
        }
 
831
 
 
832
        node = wrapDOM(node);
 
833
 
 
834
        if (isRoot) {
 
835
            _restrict = _restrict || {};
 
836
            _restrict[node._yuid] = node;
 
837
        }
 
838
 
 
839
        return node;
 
840
    };
 
841
 
 
842
    /**
 
843
     * Retrieves a NodeList instance for the given object/string. 
 
844
     * @method all
 
845
     * @static
 
846
     * @param {HTMLCollection|Array|String} node The object to wrap.
 
847
     * @param {document|Node} doc optional The document containing the node. Defaults to current document.
 
848
     * @return {NodeList} A NodeList instance for the supplied nodes.
 
849
     */
 
850
    Node.all = function(nodes, doc) {
 
851
        if (nodes instanceof NodeList) {
 
852
            return nodes;
 
853
        }
 
854
 
 
855
        if (!doc) {
 
856
            doc = Y.config.doc;
 
857
        } else if (doc._yuid && _nodes[doc._yuid]) {
 
858
            doc = _nodes[doc._yuid]; 
 
859
        }
 
860
    
 
861
        if (nodes && typeof nodes == 'string') {
 
862
            nodes = Selector.query(nodes, doc);
 
863
        }
 
864
 
 
865
        return wrapDOM(nodes);
 
866
 
 
867
    };
 
868
 
 
869
    /** 
 
870
     * A wrapper for manipulating multiple DOM elements
 
871
     * @class NodeList
 
872
     * @extends Node
 
873
     * @constructor
 
874
     */
 
875
    var NodeList = function(nodes) {
 
876
        // TODO: input validation
 
877
        _nodelists[Y.stamp(this)] = nodes;
 
878
    };
 
879
 
 
880
    // used to call Node methods against NodeList nodes
 
881
    var _tmpNode = Node.create('<div></div>');
 
882
    NodeList.prototype = {};
 
883
 
 
884
    Y.each(Node.prototype, function(fn, name) {
 
885
        if (typeof Node.prototype[name] == 'function') {
 
886
            addNodeListMethod(name);
 
887
        }
 
888
    });
 
889
 
 
890
    Y.mix(NodeList.prototype, {
 
891
        /**
 
892
         * Retrieves the Node instance at the given index. 
 
893
         * @method item
 
894
         *
 
895
         * @param {Number} index The index of the target Node.
 
896
         * @return {Node} The Node instance at the given index.
 
897
         */
 
898
        item: function(index) {
 
899
            var node = _nodelists[this._yuid][index];
 
900
            return (node && node[TAG_NAME]) ? wrapDOM(node) : (node && node.get) ? node : null;
 
901
        },
 
902
 
 
903
        /**
 
904
         * Set the value of the property/attribute on all HTMLElements bound to this NodeList.
 
905
         * Only strings/numbers/booleans are passed through unless a SETTER exists.
 
906
         * @method set
 
907
         * @param {String} prop Property to set 
 
908
         * @param {any} val Value to apply to the given property
 
909
         * @see Node
 
910
         * @chainable
 
911
         */
 
912
        set: function(name, val) {
 
913
            var nodes = _nodelists[this._yuid];
 
914
            for (var i = 0, len = nodes.length; i < len; ++i) {
 
915
                _nodes[_tmpNode._yuid] = nodes[i];
 
916
                _tmpNode.set(name, val);
 
917
            }
 
918
 
 
919
            return this;
 
920
        },
 
921
 
 
922
        /**
 
923
         * Get the value of the property/attribute for each of the HTMLElements bound to this NodeList.
 
924
         * Only strings/numbers/booleans are passed through unless a GETTER exists.
 
925
         * @method get
 
926
         * @param {String} prop Property to get 
 
927
         * @return {Array} Array containing the current values mapped to the Node indexes 
 
928
         * @see Node
 
929
         */
 
930
        get: function(name) {
 
931
            if (name == 'length') { // TODO: remove
 
932
                return _nodelists[this._yuid].length;
 
933
            }
 
934
            var nodes = _nodelists[this._yuid];
 
935
            var ret = [];
 
936
            for (var i = 0, len = nodes.length; i < len; ++i) {
 
937
                _nodes[_tmpNode._yuid] = nodes[i];
 
938
                ret[i] = _tmpNode.get(name);
 
939
            }
 
940
 
 
941
            return ret;
 
942
        },
 
943
 
 
944
        /**
 
945
         * Filters the NodeList instance down to only nodes matching the given selector.
 
946
         * @method filter
 
947
         * @param {String} selector The selector to filter against
 
948
         * @return {NodeList} NodeList containing the updated collection 
 
949
         * @see Selector
 
950
         */
 
951
        filter: function(selector) {
 
952
            return wrapDOM(Selector.filter(_nodelists[this._yuid], selector));
 
953
        },
 
954
 
 
955
        /**
 
956
         * Applies the given function to each Node in the NodeList.
 
957
         * @method each
 
958
         * @param {Function} fn The function to apply 
 
959
         * @param {Object} context optional An optional context to apply the function with
 
960
         * Default context is the NodeList instance
 
961
         * @return {NodeList} NodeList containing the updated collection 
 
962
         * @chainable
 
963
         */
 
964
        each: function(fn, context) {
 
965
            context = context || this;
 
966
            var nodes = _nodelists[this._yuid];
 
967
            for (var i = 0, len = nodes.length; i < len; ++i) {
 
968
                fn.call(context, Y.Node.get(nodes[i]), i, this);
 
969
            }
 
970
            return this;
 
971
        },
 
972
 
 
973
        /**
 
974
         * Returns the current number of items in the NodeList.
 
975
         * @method size
 
976
         * @return {Int} The number of items in the NodeList. 
 
977
         */
 
978
        size: function() {
 
979
            return _nodelists[this._yuid].length;
 
980
        },
 
981
 
 
982
        toString: function() {
 
983
            var nodes = _nodelists[this._yuid] || [];
 
984
            return 'NodeList (' + nodes.length + ' items)';
 
985
        }
 
986
 
 
987
    }, true);
 
988
 
 
989
    Y.Node = Node;
 
990
    Y.NodeList = NodeList;
 
991
    Y.all = Y.Node.all;
 
992
    Y.get = Y.Node.get;
 
993
/**
 
994
 * Extended Node interface for managing classNames.
 
995
 * @module node
 
996
 * @for Node
 
997
 */
 
998
 
 
999
    Y.Node.addDOMMethods([
 
1000
        /**
 
1001
         * Determines whether the node has the given className.
 
1002
         * @method hasClass
 
1003
         * @param {String} className the class name to search for
 
1004
         * @return {Boolean} Whether or not the node has the given class. 
 
1005
         */
 
1006
        'hasClass',
 
1007
 
 
1008
        /**
 
1009
         * Adds a class name to the node.
 
1010
         * @method addClass         
 
1011
         * @param {String} className the class name to add to the node's class attribute
 
1012
         * @chainable
 
1013
         */
 
1014
        'addClass',
 
1015
 
 
1016
        /**
 
1017
         * Removes a class name from the node.
 
1018
         * @method removeClass         
 
1019
         * @param {String} className the class name to remove from the node's class attribute
 
1020
         * @chainable
 
1021
         */
 
1022
        'removeClass',
 
1023
 
 
1024
        /**
 
1025
         * Replace a class with another class.
 
1026
         * If no oldClassName is present, the newClassName is simply added.
 
1027
         * @method replaceClass  
 
1028
         * @param {String} oldClassName the class name to be replaced
 
1029
         * @param {String} newClassName the class name that will be replacing the old class name
 
1030
         * @chainable
 
1031
         */
 
1032
        'replaceClass',
 
1033
 
 
1034
        /**
 
1035
         * If the className exists on the node it is removed, if it doesn't exist it is added.
 
1036
         * @method toggleClass  
 
1037
         * @param {String} className the class name to be toggled
 
1038
         * @chainable
 
1039
         */
 
1040
        'toggleClass'
 
1041
    ]);
 
1042
 
 
1043
 
 
1044
}, '3.0.0pr1' ,{requires:['dom-base', 'selector']});