~mnordhoff/loggerhead/valid-html

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/yui/build/node/node-debug.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', 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
            Y.log('invalid node:' + node, 'error', 'Node');
 
421
            return null;
 
422
        }
 
423
        
 
424
        var yuid = Y.guid();
 
425
        try { // IE errors on non-element expandos (cant be reused)
 
426
            node._yuid = yuid;
 
427
        } catch(e) {}
 
428
        this._yuid = yuid;
 
429
        _nodes[yuid] = node;
 
430
        _instances[yuid] = this;
 
431
 
 
432
    };
 
433
 
 
434
    var SETTERS = {};
 
435
    var GETTERS = {
 
436
        /**
 
437
         * Normalizes nodeInnerText and textContent. 
 
438
         * @property text
 
439
         * @type String
 
440
         */
 
441
        'text': function(node) {
 
442
            return Y.DOM.getText(_nodes[node._yuid]);
 
443
        },
 
444
 
 
445
        /**
 
446
         * Returns a nodeList of option elements 
 
447
         * @property options
 
448
         * @type String
 
449
         */
 
450
        'options': function(node) {
 
451
            return (node) ? node.getElementsByTagName('option') : [];
 
452
        }
 
453
    };
 
454
 
 
455
    Node.setters = function(prop, fn) {
 
456
        if (typeof prop == 'string') {
 
457
            SETTERS[prop] = fn;
 
458
        } else { // assume object
 
459
            Y.each(prop, function(fn, prop) {
 
460
                Node.setters(prop, fn);
 
461
            });
 
462
        } 
 
463
    };
 
464
 
 
465
    Node.getters = function(prop, fn) {
 
466
        if (typeof prop == 'string') {
 
467
            GETTERS[prop] = fn;
 
468
        } else { // assume object
 
469
            Y.each(prop, function(fn, prop) {
 
470
                Node.getters(prop, fn);
 
471
            });
 
472
        } 
 
473
    };
 
474
 
 
475
    Node.methods = function(name, fn) {
 
476
        if (typeof name == 'string') {
 
477
            Node.prototype[name] = function() {
 
478
                var args = slice.call(arguments);
 
479
                args.unshift(this);
 
480
                var ret = fn.apply(null, args);
 
481
                if (ret === undefined) {
 
482
                    ret = this;
 
483
                }
 
484
                return ret;
 
485
            };
 
486
 
 
487
            addNodeListMethod(name);
 
488
 
 
489
 
 
490
        } else { // assume object
 
491
            Y.each(name, function(fn, name) {
 
492
                Node.methods(name, fn);
 
493
            });
 
494
        }
 
495
    };
 
496
 
 
497
    Node.getDOMNode = function(node) {
 
498
        var ret;
 
499
 
 
500
        if (node.nodeType) {
 
501
            ret = node;
 
502
        } else if (typeof node === 'string') {
 
503
            ret = Selector.query(node, null, true);
 
504
        } else {
 
505
            ret = _nodes[node._yuid];
 
506
        }
 
507
        return ret || null;
 
508
    };
 
509
 
 
510
    Node.wrapDOMMethod = function(name) {
 
511
        return function() {
 
512
            var args = slice.call(arguments);
 
513
            args.unshift(Y.Node.getDOMNode(args.shift()));
 
514
            return Y.DOM[name].apply(Y.DOM, args);
 
515
        };
 
516
 
 
517
    };
 
518
 
 
519
    Node.addDOMMethods = function(methods) {
 
520
        var add = {}; 
 
521
        Y.each(methods, function(v, n) {
 
522
            add[v] = Y.Node.wrapDOMMethod(v);
 
523
        });
 
524
 
 
525
        Y.Node.methods(add);
 
526
    };
 
527
 
 
528
    Node.prototype = {
 
529
        /**
 
530
         * Set the value of the property/attribute on the HTMLElement bound to this Node.
 
531
         * Only strings/numbers/booleans are passed through unless a SETTER exists.
 
532
         * @method set
 
533
         * @param {String} prop Property to set 
 
534
         * @param {any} val Value to apply to the given property
 
535
         * @chainable
 
536
         */
 
537
        set: function(prop, val) {
 
538
            var node = _nodes[this._yuid];
 
539
            if (prop in SETTERS) { // use custom setter
 
540
                SETTERS[prop](this, prop, val);  // passing Node instance
 
541
            } else if (RE_VALID_PROP_TYPES.test(typeof node[prop])) { // safe to write
 
542
                node[prop] = val;
 
543
            }
 
544
            return this;
 
545
        },
 
546
 
 
547
        /**
 
548
         * Get the value of the property/attribute on the HTMLElement bound to this Node.
 
549
         * Only strings/numbers/booleans are passed through unless a GETTER exists.
 
550
         * @method get
 
551
         * @param {String} prop Property to get 
 
552
         * @return {any} Current value of the property
 
553
         */
 
554
        get: function(prop) {
 
555
            var val;
 
556
            var node = _nodes[this._yuid];
 
557
            if (prop in GETTERS) { // use custom getter
 
558
                val = GETTERS[prop](this, prop);
 
559
            } else if (prop in PROPS_WRAP) { // wrap DOM object (HTMLElement, HTMLCollection, Document)
 
560
                if (typeof PROPS_WRAP[prop] === 'function') {
 
561
                    val = PROPS_WRAP[prop](this);
 
562
                } else {
 
563
                    val = node[prop];
 
564
                }
 
565
 
 
566
                if (_restrict && _restrict[this._yuid] && !Y.DOM.contains(node, val)) {
 
567
                    val = null; // not allowed to go outside of root node
 
568
                } else {
 
569
                    val = wrapDOM(val);
 
570
                }
 
571
            } else if (RE_VALID_PROP_TYPES.test(typeof node[prop])) { // safe to read
 
572
                val = node[prop];
 
573
            }
 
574
            return val;
 
575
        },
 
576
 
 
577
        invoke: function(method, a, b, c, d, e) {
 
578
            if (a) { // first 2 may be Node instances or strings
 
579
                a = (a[NODE_TYPE]) ? a : getDOMNode(a);
 
580
                if (b) {
 
581
                    b = (b[NODE_TYPE]) ? b : getDOMNode(b);
 
582
                }
 
583
            }
 
584
            var node = _nodes[this._yuid];
 
585
            if (node && METHODS_INVOKE[method] && node[method]) {
 
586
                return node[method](a, b, c, d, e);
 
587
            }
 
588
            return null;
 
589
        },
 
590
 
 
591
        hasMethod: function(method) {
 
592
            return !!(METHODS_INVOKE[method] && _nodes[this._yuid][method]);
 
593
        },
 
594
 
 
595
        //normalize: function() {},
 
596
        //isSupported: function(feature, version) {},
 
597
        toString: function() {
 
598
            var node = _nodes[this._yuid] || {};
 
599
            return node.id || node[NODE_NAME] || 'undefined node';
 
600
        },
 
601
 
 
602
        /**
 
603
         * Retrieves a single node based on the given CSS selector. 
 
604
         * @method query
 
605
         *
 
606
         * @param {string} selector The CSS selector to test against.
 
607
         * @return {Node} A Node instance for the matching HTMLElement.
 
608
         */
 
609
        query: function(selector) {
 
610
            return wrapDOM(Selector.query(selector, _nodes[this._yuid], true));
 
611
        },
 
612
 
 
613
        /**
 
614
         * Retrieves a nodeList based on the given CSS selector. 
 
615
         * @method queryAll
 
616
         *
 
617
         * @param {string} selector The CSS selector to test against.
 
618
         * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
 
619
         */
 
620
        queryAll: function(selector) {
 
621
            return wrapDOM(Selector.query(selector, _nodes[this._yuid]));
 
622
        },
 
623
 
 
624
        /**
 
625
         * Test if the supplied node matches the supplied selector.
 
626
         * @method test
 
627
         *
 
628
         * @param {string} selector The CSS selector to test against.
 
629
         * @return {boolean} Whether or not the node matches the selector.
 
630
         */
 
631
        test: function(selector) {
 
632
            return Selector.test(_nodes[this._yuid], selector);
 
633
        },
 
634
 
 
635
        /**
 
636
         * Compares nodes to determine if they match.
 
637
         * Node instances can be compared to each other and/or HTMLElements.
 
638
         * @method compareTo
 
639
         * @param {HTMLElement | Node} refNode The reference node to compare to the node.
 
640
         * @return {Boolean} True if the nodes match, false if they do not. 
 
641
         */
 
642
        compareTo: function(refNode) {
 
643
            refNode = refNode[NODE_TYPE] ? refNode : _nodes[refNode._yuid];
 
644
            return _nodes[this._yuid] === refNode;
 
645
        },
 
646
 
 
647
       /*
 
648
         * Returns the nearest ancestor that passes the test applied by supplied boolean method.
 
649
         * @method ancestor
 
650
         * @param {String | Function} fn A selector or boolean method for testing elements.
 
651
         * If a function is used, it receives the current node being tested as the only argument.
 
652
         * @return {Node} The matching Node instance or null if not found
 
653
         */
 
654
        ancestor: function(fn) {
 
655
            return wrapDOM(Y.DOM.elementByAxis(_nodes[this._yuid], 'parentNode', wrapFn(fn)));
 
656
        },
 
657
 
 
658
        /**
 
659
         * Returns the previous matching sibling. 
 
660
         * Returns the nearest element node sibling if no method provided.
 
661
         * @method previous
 
662
         * @param {String | Function} fn A selector or boolean method for testing elements.
 
663
         * If a function is used, it receives the current node being tested as the only argument.
 
664
         * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
 
665
         * @return {Node} Node instance or null if not found
 
666
         */
 
667
        previous: function(fn, all) {
 
668
            return wrapDOM(Y.DOM.elementByAxis(_nodes[this._yuid], 'previousSibling', wrapFn(fn)), all);
 
669
        }, 
 
670
 
 
671
        /**
 
672
         * Returns the next matching sibling. 
 
673
         * Returns the nearest element node sibling if no method provided.
 
674
         * @method next
 
675
         * @param {String | Function} fn A selector or boolean method for testing elements.
 
676
         * If a function is used, it receives the current node being tested as the only argument.
 
677
         * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
 
678
         * @return {Node} Node instance or null if not found
 
679
         */
 
680
        next: function(fn, all) {
 
681
            return wrapDOM(Y.DOM.elementByAxis(_nodes[this._yuid], 'nextSibling', wrapFn(fn)), all);
 
682
        },
 
683
        
 
684
       /**
 
685
         * Attaches a DOM event handler.
 
686
         * @method attach
 
687
         * @param {String} type The type of DOM Event to listen for 
 
688
         * @param {Function} fn The handler to call when the event fires 
 
689
         * @param {Object} arg An argument object to pass to the handler 
 
690
         */
 
691
 
 
692
        attach: function(type, fn, arg) {
 
693
            var args = slice.call(arguments, 0);
 
694
            args.unshift(_nodes[this._yuid]);
 
695
            return Y.Event.addListener.apply(Y.Event, args);
 
696
        },
 
697
 
 
698
       /**
 
699
         * Alias for attach.
 
700
         * @method on
 
701
         * @param {String} type The type of DOM Event to listen for 
 
702
         * @param {Function} fn The handler to call when the event fires 
 
703
         * @param {Object} arg An argument object to pass to the handler 
 
704
         * @see attach
 
705
         */
 
706
 
 
707
        on: function(type, fn, arg) {
 
708
            return this.attach.apply(this, arguments);
 
709
        },
 
710
 
 
711
        addEventListener: function(type, fn, arg) {
 
712
            return Y.Event.nativeAdd(_nodes[this._yuid], type, fn, arg);
 
713
        },
 
714
        
 
715
       /**
 
716
         * Detaches a DOM event handler. 
 
717
         * @method detach
 
718
         * @param {String} type The type of DOM Event
 
719
         * @param {Function} fn The handler to call when the event fires 
 
720
         */
 
721
        detach: function(type, fn) {
 
722
            var args = slice.call(arguments, 0);
 
723
            args.unshift(_nodes[this._yuid]);
 
724
            return Y.Event.removeListener.apply(Y.Event, args);
 
725
        },
 
726
 
 
727
        removeEventListener: function(type, fn) {
 
728
            return Y.Event.nativeRemove(_nodes[this._yuid], type, fn);
 
729
        },
 
730
 
 
731
       /**
 
732
         * Creates a Node instance from HTML string
 
733
         * @method create
 
734
         * @param {String|Array} html The string of html to create
 
735
         * @return {Node} A new Node instance 
 
736
         */
 
737
        create: function(html) {
 
738
            return Y.Node.create(html);
 
739
        },
 
740
 
 
741
        /**
 
742
         * Determines whether the ndoe is an ancestor of another HTML element in the DOM hierarchy.
 
743
         * @method contains
 
744
         * @param {Node | HTMLElement} needle The possible node or descendent
 
745
         * @return {Boolean} Whether or not this node is the needle its ancestor
 
746
         */
 
747
        contains: function(needle) {
 
748
            return Y.DOM.contains(_nodes[this._yuid], getDOMNode(needle));
 
749
        },
 
750
 
 
751
        /**
 
752
         * Applies the supplied plugin to the node.
 
753
         * @method plug
 
754
         * @param {Function} The plugin Class to apply
 
755
         * @param {Object} config An optional config to pass to the constructor
 
756
         * @chainable
 
757
         */
 
758
        plug: function(PluginClass, config) {
 
759
            config = config || {};
 
760
            config.owner = this;
 
761
            if (PluginClass && PluginClass.NS) {
 
762
                this[PluginClass.NS] = new PluginClass(config);
 
763
            }
 
764
            return this;
 
765
        },
 
766
 
 
767
        /**
 
768
         * Determines whether the node is appended to the document.
 
769
         * @method inDoc
 
770
         * @param {Node|HTMLElement} doc optional An optional document to check against.
 
771
         * Defaults to current document. 
 
772
         * @return {Boolean} Whether or not this node is appended to the document. 
 
773
         */
 
774
        inDoc: function(doc) {
 
775
            var node = _nodes[this._yuid];
 
776
            doc = (doc) ? getDoc(doc) : node.ownerDocument;
 
777
            if (doc.documentElement) {
 
778
                return Y.DOM.contains(doc.documentElement, node);
 
779
            }
 
780
        }
 
781
    };
 
782
 
 
783
    Y.each(METHODS, function(fn, method) {
 
784
        Node.prototype[method] = function() {
 
785
            return fn.apply(this, [method].concat(slice.call(arguments)));
 
786
        };
 
787
    });
 
788
 
 
789
    /** 
 
790
     * Creates a Node instance from an HTML string
 
791
     * @method create
 
792
     * @param {String} html HTML string
 
793
     */
 
794
    Node.create = function(html) {
 
795
        return wrapDOM(Y.DOM.create(html));
 
796
    };
 
797
 
 
798
    Node.getById = function(id, doc) {
 
799
        doc = (doc && doc[NODE_TYPE]) ? doc : Y.config.doc;
 
800
        return wrapDOM(doc.getElementById(id));
 
801
    };
 
802
 
 
803
    /**
 
804
     * Retrieves a Node instance for the given object/string. 
 
805
     * Note: Use 'document' string to retrieve document Node instance from string
 
806
     * @method get
 
807
     * @static
 
808
     * @param {document|HTMLElement|HTMLCollection|Array|String} node The object to wrap.
 
809
     * @param {document|Node} doc optional The document containing the node. Defaults to current document.
 
810
     * @param {boolean} isRoot optional Whether or not this node should be treated as a root node. Root nodes
 
811
     * aren't allowed to traverse outside their DOM tree.
 
812
     * @return {Node} A wrapper instance for the supplied object.
 
813
     */
 
814
    Node.get = function(node, doc, isRoot) {
 
815
        if (node instanceof Node) {
 
816
            return node;
 
817
        }
 
818
 
 
819
        if (!doc) {
 
820
            doc = Y.config.doc;
 
821
        } else if (doc._yuid && _nodes[doc._yuid]) {
 
822
            doc = _nodes[doc._yuid]; 
 
823
        }
 
824
    
 
825
        if (node && typeof node === 'string') {
 
826
            if (node === 'document') {
 
827
                node = Y.config.doc;
 
828
            } else {
 
829
                node = Y.Selector.query(node, doc, true);
 
830
            }
 
831
        }
 
832
 
 
833
        node = wrapDOM(node);
 
834
 
 
835
        if (isRoot) {
 
836
            _restrict = _restrict || {};
 
837
            _restrict[node._yuid] = node;
 
838
        }
 
839
 
 
840
        return node;
 
841
    };
 
842
 
 
843
    /**
 
844
     * Retrieves a NodeList instance for the given object/string. 
 
845
     * @method all
 
846
     * @static
 
847
     * @param {HTMLCollection|Array|String} node The object to wrap.
 
848
     * @param {document|Node} doc optional The document containing the node. Defaults to current document.
 
849
     * @return {NodeList} A NodeList instance for the supplied nodes.
 
850
     */
 
851
    Node.all = function(nodes, doc) {
 
852
        if (nodes instanceof NodeList) {
 
853
            return nodes;
 
854
        }
 
855
 
 
856
        if (!doc) {
 
857
            doc = Y.config.doc;
 
858
        } else if (doc._yuid && _nodes[doc._yuid]) {
 
859
            doc = _nodes[doc._yuid]; 
 
860
        }
 
861
    
 
862
        if (nodes && typeof nodes == 'string') {
 
863
            nodes = Selector.query(nodes, doc);
 
864
        }
 
865
 
 
866
        return wrapDOM(nodes);
 
867
 
 
868
    };
 
869
 
 
870
    /** 
 
871
     * A wrapper for manipulating multiple DOM elements
 
872
     * @class NodeList
 
873
     * @extends Node
 
874
     * @constructor
 
875
     */
 
876
    var NodeList = function(nodes) {
 
877
        // TODO: input validation
 
878
        _nodelists[Y.stamp(this)] = nodes;
 
879
    };
 
880
 
 
881
    // used to call Node methods against NodeList nodes
 
882
    var _tmpNode = Node.create('<div></div>');
 
883
    NodeList.prototype = {};
 
884
 
 
885
    Y.each(Node.prototype, function(fn, name) {
 
886
        if (typeof Node.prototype[name] == 'function') {
 
887
            addNodeListMethod(name);
 
888
        }
 
889
    });
 
890
 
 
891
    Y.mix(NodeList.prototype, {
 
892
        /**
 
893
         * Retrieves the Node instance at the given index. 
 
894
         * @method item
 
895
         *
 
896
         * @param {Number} index The index of the target Node.
 
897
         * @return {Node} The Node instance at the given index.
 
898
         */
 
899
        item: function(index) {
 
900
            var node = _nodelists[this._yuid][index];
 
901
            return (node && node[TAG_NAME]) ? wrapDOM(node) : (node && node.get) ? node : null;
 
902
        },
 
903
 
 
904
        /**
 
905
         * Set the value of the property/attribute on all HTMLElements bound to this NodeList.
 
906
         * Only strings/numbers/booleans are passed through unless a SETTER exists.
 
907
         * @method set
 
908
         * @param {String} prop Property to set 
 
909
         * @param {any} val Value to apply to the given property
 
910
         * @see Node
 
911
         * @chainable
 
912
         */
 
913
        set: function(name, val) {
 
914
            var nodes = _nodelists[this._yuid];
 
915
            for (var i = 0, len = nodes.length; i < len; ++i) {
 
916
                _nodes[_tmpNode._yuid] = nodes[i];
 
917
                _tmpNode.set(name, val);
 
918
            }
 
919
 
 
920
            return this;
 
921
        },
 
922
 
 
923
        /**
 
924
         * Get the value of the property/attribute for each of the HTMLElements bound to this NodeList.
 
925
         * Only strings/numbers/booleans are passed through unless a GETTER exists.
 
926
         * @method get
 
927
         * @param {String} prop Property to get 
 
928
         * @return {Array} Array containing the current values mapped to the Node indexes 
 
929
         * @see Node
 
930
         */
 
931
        get: function(name) {
 
932
            if (name == 'length') { // TODO: remove
 
933
                Y.log('the length property is deprecated; use size()', 'warn', 'NodeList');
 
934
                return _nodelists[this._yuid].length;
 
935
            }
 
936
            var nodes = _nodelists[this._yuid];
 
937
            var ret = [];
 
938
            for (var i = 0, len = nodes.length; i < len; ++i) {
 
939
                _nodes[_tmpNode._yuid] = nodes[i];
 
940
                ret[i] = _tmpNode.get(name);
 
941
            }
 
942
 
 
943
            return ret;
 
944
        },
 
945
 
 
946
        /**
 
947
         * Filters the NodeList instance down to only nodes matching the given selector.
 
948
         * @method filter
 
949
         * @param {String} selector The selector to filter against
 
950
         * @return {NodeList} NodeList containing the updated collection 
 
951
         * @see Selector
 
952
         */
 
953
        filter: function(selector) {
 
954
            return wrapDOM(Selector.filter(_nodelists[this._yuid], selector));
 
955
        },
 
956
 
 
957
        /**
 
958
         * Applies the given function to each Node in the NodeList.
 
959
         * @method each
 
960
         * @param {Function} fn The function to apply 
 
961
         * @param {Object} context optional An optional context to apply the function with
 
962
         * Default context is the NodeList instance
 
963
         * @return {NodeList} NodeList containing the updated collection 
 
964
         * @chainable
 
965
         */
 
966
        each: function(fn, context) {
 
967
            context = context || this;
 
968
            var nodes = _nodelists[this._yuid];
 
969
            for (var i = 0, len = nodes.length; i < len; ++i) {
 
970
                fn.call(context, Y.Node.get(nodes[i]), i, this);
 
971
            }
 
972
            return this;
 
973
        },
 
974
 
 
975
        /**
 
976
         * Returns the current number of items in the NodeList.
 
977
         * @method size
 
978
         * @return {Int} The number of items in the NodeList. 
 
979
         */
 
980
        size: function() {
 
981
            return _nodelists[this._yuid].length;
 
982
        },
 
983
 
 
984
        toString: function() {
 
985
            var nodes = _nodelists[this._yuid] || [];
 
986
            return 'NodeList (' + nodes.length + ' items)';
 
987
        }
 
988
 
 
989
    }, true);
 
990
 
 
991
    Y.Node = Node;
 
992
    Y.NodeList = NodeList;
 
993
    Y.all = Y.Node.all;
 
994
    Y.get = Y.Node.get;
 
995
/**
 
996
 * Extended Node interface for managing node styles.
 
997
 * @module node
 
998
 * @submodule node-style
 
999
 * @for Node
 
1000
 */
 
1001
 
 
1002
Y.Node.addDOMMethods([
 
1003
    /**
 
1004
     * Returns the style's current value.
 
1005
     * @method getStyle
 
1006
     * @param {String} attr The style attribute to retrieve. 
 
1007
     * @return {String} The current value of the style property for the element.
 
1008
     */
 
1009
    'getStyle',
 
1010
 
 
1011
    /**
 
1012
     * Returns the computed value for the given style property.
 
1013
     * @method getComputedStyle
 
1014
     * @param {String} attr The style attribute to retrieve. 
 
1015
     * @return {String} The computed value of the style property for the element.
 
1016
     */
 
1017
    'getComputedStyle',
 
1018
 
 
1019
    /**
 
1020
     * Sets a style property of the node.
 
1021
     * @method setStyle
 
1022
     * @param {String} attr The style attribute to set. 
 
1023
     * @param {String|Number} val The value. 
 
1024
     * @chainable
 
1025
     */
 
1026
    'setStyle',
 
1027
 
 
1028
    /**
 
1029
     * Sets multiple style properties on the node.
 
1030
     * @method setStyles
 
1031
     * @param {Object} hash An object literal of property:value pairs. 
 
1032
     * @chainable
 
1033
     */
 
1034
    'setStyles'
 
1035
]);
 
1036
 
 
1037
/**
 
1038
 * Extended Node interface for managing classNames.
 
1039
 * @module node
 
1040
 * @for Node
 
1041
 */
 
1042
 
 
1043
    Y.Node.addDOMMethods([
 
1044
        /**
 
1045
         * Determines whether the node has the given className.
 
1046
         * @method hasClass
 
1047
         * @param {String} className the class name to search for
 
1048
         * @return {Boolean} Whether or not the node has the given class. 
 
1049
         */
 
1050
        'hasClass',
 
1051
 
 
1052
        /**
 
1053
         * Adds a class name to the node.
 
1054
         * @method addClass         
 
1055
         * @param {String} className the class name to add to the node's class attribute
 
1056
         * @chainable
 
1057
         */
 
1058
        'addClass',
 
1059
 
 
1060
        /**
 
1061
         * Removes a class name from the node.
 
1062
         * @method removeClass         
 
1063
         * @param {String} className the class name to remove from the node's class attribute
 
1064
         * @chainable
 
1065
         */
 
1066
        'removeClass',
 
1067
 
 
1068
        /**
 
1069
         * Replace a class with another class.
 
1070
         * If no oldClassName is present, the newClassName is simply added.
 
1071
         * @method replaceClass  
 
1072
         * @param {String} oldClassName the class name to be replaced
 
1073
         * @param {String} newClassName the class name that will be replacing the old class name
 
1074
         * @chainable
 
1075
         */
 
1076
        'replaceClass',
 
1077
 
 
1078
        /**
 
1079
         * If the className exists on the node it is removed, if it doesn't exist it is added.
 
1080
         * @method toggleClass  
 
1081
         * @param {String} className the class name to be toggled
 
1082
         * @chainable
 
1083
         */
 
1084
        'toggleClass'
 
1085
    ]);
 
1086
/**
 
1087
 * Extended Node interface for managing regions and screen positioning.
 
1088
 * Adds support for positioning elements and normalizes window size and scroll detection. 
 
1089
 * @module node
 
1090
 * @submodule node-screen
 
1091
 * @for Node
 
1092
 */
 
1093
 
 
1094
var ATTR = [
 
1095
        /**
 
1096
         * Returns a region object for the node 
 
1097
         * @property region
 
1098
         * @type Node
 
1099
         */
 
1100
        'region',
 
1101
        /**
 
1102
         * Returns a region object for the node's viewport 
 
1103
         * @property viewportRegion
 
1104
         * @type Node
 
1105
         */
 
1106
        'viewportRegion'
 
1107
    ],
 
1108
 
 
1109
    getNode = Y.Node.getDOMNode;
 
1110
 
 
1111
Y.each(ATTR, function(v, n) {
 
1112
    Y.Node.getters(v, Y.Node.wrapDOMMethod(v));
 
1113
});
 
1114
 
 
1115
Y.Node.addDOMMethods([
 
1116
    /**
 
1117
     * Determines whether or not the node is currently visible in the viewport. 
 
1118
     * @method inViewportRegion         
 
1119
     * @return {Boolean} Whether or not the node is currently positioned
 
1120
     * within the viewport's region
 
1121
     */
 
1122
    'inViewportRegion'
 
1123
]);
 
1124
 
 
1125
// these need special treatment to extract 2nd node arg
 
1126
Y.Node.methods({
 
1127
    /**
 
1128
     * Compares the intersection of the node with another node or region 
 
1129
     * @method intersect         
 
1130
     * @param {Node|Object} node2 The node or region to compare with.
 
1131
     * @param {Object} altRegion An alternate region to use (rather than this node's). 
 
1132
     * @return {Object} An object representing the intersection of the regions. 
 
1133
     */
 
1134
    intersect: function(node1, node2, altRegion) {
 
1135
        if (node2 instanceof Y.Node) { // might be a region object
 
1136
            node2 = getNode(node2);
 
1137
        }
 
1138
        return Y.DOM.intersect(getNode(node1), node2, altRegion); 
 
1139
    },
 
1140
 
 
1141
    /**
 
1142
     * Determines whether or not the node is within the giving region.
 
1143
     * @method inRegion         
 
1144
     * @param {Node|Object} node2 The node or region to compare with.
 
1145
     * @param {Boolean} all Whether or not all of the node must be in the region. 
 
1146
     * @param {Object} altRegion An alternate region to use (rather than this node's). 
 
1147
     * @return {Object} An object representing the intersection of the regions. 
 
1148
     */
 
1149
    inRegion: function(node1, node2, all, altRegion) {
 
1150
        if (node2 instanceof Y.Node) { // might be a region object
 
1151
            node2 = getNode(node2);
 
1152
        }
 
1153
        return Y.DOM.inRegion(getNode(node1), node2, all, altRegion); 
 
1154
    }
 
1155
});
 
1156
 
 
1157
/**
 
1158
 * Extended Node interface for managing regions and screen positioning.
 
1159
 * Adds support for positioning elements and normalizes window size and scroll detection. 
 
1160
 * @module node
 
1161
 * @submodule node-screen
 
1162
 * @for Node
 
1163
 */
 
1164
 
 
1165
    Y.each([
 
1166
        /**
 
1167
         * Returns the inner width of the viewport (exludes scrollbar). 
 
1168
         * @property winWidth
 
1169
         * @type {Int}
 
1170
         */
 
1171
        'winWidth',
 
1172
 
 
1173
        /**
 
1174
         * Returns the inner height of the viewport (exludes scrollbar). 
 
1175
         * @property winHeight
 
1176
         * @type {Int}
 
1177
         */
 
1178
        'winHeight',
 
1179
 
 
1180
        /**
 
1181
         * Document width 
 
1182
         * @property winHeight
 
1183
         * @type {Int}
 
1184
         */
 
1185
        'docWidth',
 
1186
 
 
1187
        /**
 
1188
         * Document height 
 
1189
         * @property docHeight
 
1190
         * @type {Int}
 
1191
         */
 
1192
        'docHeight',
 
1193
 
 
1194
        /**
 
1195
         * Amount page has been scroll vertically 
 
1196
         * @property docScrollX
 
1197
         * @type {Int}
 
1198
         */
 
1199
        'docScrollX',
 
1200
 
 
1201
        /**
 
1202
         * Amount page has been scroll horizontally 
 
1203
         * @property docScrollY
 
1204
         * @type {Int}
 
1205
         */
 
1206
        'docScrollY'
 
1207
        ],
 
1208
        function(v, n) {
 
1209
            Y.Node.getters(v, Y.Node.wrapDOMMethod(v));
 
1210
        }
 
1211
    );
 
1212
 
 
1213
    Y.Node.addDOMMethods([
 
1214
    /**
 
1215
     * Gets the current position of the node in page coordinates. 
 
1216
     * Nodes must be part of the DOM tree to have page coordinates
 
1217
     * (display:none or nodes not appended return false).
 
1218
     * @method getXY
 
1219
     * @return {Array} The XY position of the node
 
1220
    */
 
1221
        'getXY',
 
1222
 
 
1223
    /**
 
1224
     * Set the position of the node in page coordinates, regardless of how the node is positioned.
 
1225
     * The node must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1226
     * @method setXY
 
1227
     * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
 
1228
     * @chainable
 
1229
     */
 
1230
        'setXY',
 
1231
 
 
1232
    /**
 
1233
     * Gets the current position of the node in page coordinates. 
 
1234
     * Nodes must be part of the DOM tree to have page coordinates
 
1235
     * (display:none or nodes not appended return false).
 
1236
     * @method getX
 
1237
     * @return {Int} The X position of the node
 
1238
    */
 
1239
        'getX',
 
1240
 
 
1241
    /**
 
1242
     * Set the position of the node in page coordinates, regardless of how the node is positioned.
 
1243
     * The node must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1244
     * @method setX
 
1245
     * @param {Int} x X value for new position (coordinates are page-based)
 
1246
     * @chainable
 
1247
     */
 
1248
        'setX',
 
1249
 
 
1250
    /**
 
1251
     * Gets the current position of the node in page coordinates. 
 
1252
     * Nodes must be part of the DOM tree to have page coordinates
 
1253
     * (display:none or nodes not appended return false).
 
1254
     * @method getY
 
1255
     * @return {Int} The Y position of the node
 
1256
    */
 
1257
        'getY',
 
1258
 
 
1259
    /**
 
1260
     * Set the position of the node in page coordinates, regardless of how the node is positioned.
 
1261
     * The node must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1262
     * @method setY
 
1263
     * @param {Int} y Y value for new position (coordinates are page-based)
 
1264
     * @chainable
 
1265
     */
 
1266
        'setY'
 
1267
    ]);
 
1268
 
 
1269
 
 
1270
}, '3.0.0pr1' ,{requires:['dom']});