Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('yui2-datatable', function(Y) {
2
    var YAHOO    = Y.YUI2;
3
    /*
4
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
5
Code licensed under the BSD License:
6
http://developer.yahoo.com/yui/license.html
7
version: 2.9.0
8
*/
9
/**
10
 * Mechanism to execute a series of callbacks in a non-blocking queue.  Each callback is executed via setTimout unless configured with a negative timeout, in which case it is run in blocking mode in the same execution thread as the previous callback.  Callbacks can be function references or object literals with the following keys:
11
 * <ul>
12
 *    <li><code>method</code> - {Function} REQUIRED the callback function.</li>
13
 *    <li><code>scope</code> - {Object} the scope from which to execute the callback.  Default is the global window scope.</li>
14
 *    <li><code>argument</code> - {Array} parameters to be passed to method as individual arguments.</li>
15
 *    <li><code>timeout</code> - {number} millisecond delay to wait after previous callback completion before executing this callback.  Negative values cause immediate blocking execution.  Default 0.</li>
16
 *    <li><code>until</code> - {Function} boolean function executed before each iteration.  Return true to indicate completion and proceed to the next callback.</li>
17
 *    <li><code>iterations</code> - {Number} number of times to execute the callback before proceeding to the next callback in the chain. Incompatible with <code>until</code>.</li>
18
 * </ul>
19
 *
20
 * @namespace YAHOO.util
21
 * @class Chain
22
 * @constructor
23
 * @param callback* {Function|Object} Any number of callbacks to initialize the queue
24
*/
25
YAHOO.util.Chain = function () {
26
    /**
27
     * The callback queue
28
     * @property q
29
     * @type {Array}
30
     * @private
31
     */
32
    this.q = [].slice.call(arguments);
33
 
34
    /**
35
     * Event fired when the callback queue is emptied via execution (not via
36
     * a call to chain.stop().
37
     * @event end
38
     */
39
    this.createEvent('end');
40
};
41
 
42
YAHOO.util.Chain.prototype = {
43
    /**
44
     * Timeout id used to pause or stop execution and indicate the execution state of the Chain.  0 indicates paused or stopped, -1 indicates blocking execution, and any positive number indicates non-blocking execution.
45
     * @property id
46
     * @type {number}
47
     * @private
48
     */
49
    id   : 0,
50
 
51
    /**
52
     * Begin executing the chain, or resume execution from the last paused position.
53
     * @method run
54
     * @return {Chain} the Chain instance
55
     */
56
    run : function () {
57
        // Grab the first callback in the queue
58
        var c  = this.q[0],
59
            fn;
60
 
61
        // If there is no callback in the queue or the Chain is currently
62
        // in an execution mode, return
63
        if (!c) {
64
            this.fireEvent('end');
65
            return this;
66
        } else if (this.id) {
67
            return this;
68
        }
69
 
70
        fn = c.method || c;
71
 
72
        if (typeof fn === 'function') {
73
            var o    = c.scope || {},
74
                args = c.argument || [],
75
                ms   = c.timeout || 0,
76
                me   = this;
77
 
78
            if (!(args instanceof Array)) {
79
                args = [args];
80
            }
81
 
82
            // Execute immediately if the callback timeout is negative.
83
            if (ms < 0) {
84
                this.id = ms;
85
                if (c.until) {
86
                    for (;!c.until();) {
87
                        // Execute the callback from scope, with argument
88
                        fn.apply(o,args);
89
                    }
90
                } else if (c.iterations) {
91
                    for (;c.iterations-- > 0;) {
92
                        fn.apply(o,args);
93
                    }
94
                } else {
95
                    fn.apply(o,args);
96
                }
97
                this.q.shift();
98
                this.id = 0;
99
                return this.run();
100
            } else {
101
                // If the until condition is set, check if we're done
102
                if (c.until) {
103
                    if (c.until()) {
104
                        // Shift this callback from the queue and execute the next
105
                        // callback
106
                        this.q.shift();
107
                        return this.run();
108
                    }
109
                // Otherwise if either iterations is not set or we're
110
                // executing the last iteration, shift callback from the queue
111
                } else if (!c.iterations || !--c.iterations) {
112
                    this.q.shift();
113
                }
114
 
115
                // Otherwise set to execute after the configured timeout
116
                this.id = setTimeout(function () {
117
                    // Execute the callback from scope, with argument
118
                    fn.apply(o,args);
119
                    // Check if the Chain was not paused from inside the callback
120
                    if (me.id) {
121
                        // Indicate ready to run state
122
                        me.id = 0;
123
                        // Start the fun all over again
124
                        me.run();
125
                    }
126
                },ms);
127
            }
128
        }
129
 
130
        return this;
131
    },
132
 
133
    /**
134
     * Add a callback to the end of the queue
135
     * @method add
136
     * @param c {Function|Object} the callback function ref or object literal
137
     * @return {Chain} the Chain instance
138
     */
139
    add  : function (c) {
140
        this.q.push(c);
141
        return this;
142
    },
143
 
144
    /**
145
     * Pause the execution of the Chain after the current execution of the
146
     * current callback completes.  If called interstitially, clears the
147
     * timeout for the pending callback. Paused Chains can be restarted with
148
     * chain.run()
149
     * @method pause
150
     * @return {Chain} the Chain instance
151
     */
152
    pause: function () {
153
        // Conditional added for Caja compatibility
154
        if (this.id > 0) {
155
            clearTimeout(this.id);
156
        }
157
        this.id = 0;
158
        return this;
159
    },
160
 
161
    /**
162
     * Stop and clear the Chain's queue after the current execution of the
163
     * current callback completes.
164
     * @method stop
165
     * @return {Chain} the Chain instance
166
     */
167
    stop : function () {
168
        this.pause();
169
        this.q = [];
170
        return this;
171
    }
172
};
173
YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider);
174
 
175
/**
176
 * Augments the Event Utility with a <code>delegate</code> method that
177
 * facilitates easy creation of delegated event listeners.  (Note: Using CSS
178
 * selectors as the filtering criteria for delegated event listeners requires
179
 * inclusion of the Selector Utility.)
180
 *
181
 * @module event-delegate
182
 * @title Event Utility Event Delegation Module
183
 * @namespace YAHOO.util
184
 * @requires event
185
 */
186
 
187
(function () {
188
 
189
    var Event = YAHOO.util.Event,
190
        Lang = YAHOO.lang,
191
        delegates = [],
192
 
193
 
194
        getMatch = function(el, selector, container) {
195
 
196
            var returnVal;
197
 
198
            if (!el || el === container) {
199
                returnVal = false;
200
            }
201
            else {
202
                returnVal = YAHOO.util.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
203
            }
204
 
205
            return returnVal;
206
 
207
        };
208
 
209
 
210
    Lang.augmentObject(Event, {
211
 
212
        /**
213
         * Creates a delegate function used to call event listeners specified
214
         * via the <code>YAHOO.util.Event.delegate</code> method.
215
         *
216
         * @method _createDelegate
217
         *
218
         * @param {Function} fn        The method (event listener) to call.
219
         * @param {Function|string} filter Function or CSS selector used to
220
         * determine for what element(s) the event listener should be called.
221
         * @param {Object}   obj    An arbitrary object that will be
222
         *                             passed as a parameter to the listener.
223
         * @param {Boolean|object}  overrideContext  If true, the value of the
224
         *                             obj parameter becomes the execution context
225
         *                          of the listener. If an object, this object
226
         *                          becomes the execution context.
227
         * @return {Function} Function that will call the event listener
228
         * specified by the <code>YAHOO.util.Event.delegate</code> method.
229
         * @private
230
         * @for Event
231
         * @static
232
         */
233
        _createDelegate: function (fn, filter, obj, overrideContext) {
234
 
235
            return function (event) {
236
 
237
                var container = this,
238
                    target = Event.getTarget(event),
239
                    selector = filter,
240
 
241
                    //    The user might have specified the document object
242
                    //    as the delegation container, in which case it is not
243
                    //    nessary to scope the provided CSS selector(s) to the
244
                    //    delegation container
245
                    bDocument = (container.nodeType === 9),
246
 
247
                    matchedEl,
248
                    context,
249
                    sID,
250
                    sIDSelector;
251
 
252
 
253
                if (Lang.isFunction(filter)) {
254
                    matchedEl = filter(target);
255
                }
256
                else if (Lang.isString(filter)) {
257
 
258
                    if (!bDocument) {
259
 
260
                        sID = container.id;
261
 
262
                        if (!sID) {
263
                            sID = Event.generateId(container);
264
                        }
265
 
266
                        //    Scope all selectors to the container
267
                        sIDSelector = ("#" + sID + " ");
268
                        selector = (sIDSelector + filter).replace(/,/gi, ("," + sIDSelector));
269
 
270
                    }
271
 
272
 
273
                    if (YAHOO.util.Selector.test(target, selector)) {
274
                        matchedEl = target;
275
                    }
276
                    else if (YAHOO.util.Selector.test(target, ((selector.replace(/,/gi, " *,")) + " *"))) {
277
 
278
                        //    The target is a descendant of an element matching
279
                        //    the selector, so crawl up to find the ancestor that
280
                        //    matches the selector
281
 
282
                        matchedEl = getMatch(target, selector, container);
283
 
284
                    }
285
 
286
                }
287
 
288
 
289
                if (matchedEl) {
290
 
291
                    //    The default context for delegated listeners is the
292
                    //    element that matched the filter.
293
 
294
                    context = matchedEl;
295
 
296
                    if (overrideContext) {
297
                        if (overrideContext === true) {
298
                            context = obj;
299
                        } else {
300
                            context = overrideContext;
301
                        }
302
                    }
303
 
304
                    //    Call the listener passing in the container and the
305
                    //    element that matched the filter in case the user
306
                    //    needs those.
307
 
308
                    return fn.call(context, event, matchedEl, container, obj);
309
 
310
                }
311
 
312
            };
313
 
314
        },
315
 
316
 
317
        /**
318
         * Appends a delegated event listener.  Delegated event listeners
319
         * receive three arguments by default: the DOM event, the element
320
         * specified by the filtering function or CSS selector, and the
321
         * container element (the element to which the event listener is
322
         * bound).  (Note: Using the delegate method requires the event-delegate
323
         * module.  Using CSS selectors as the filtering criteria for delegated
324
         * event listeners requires inclusion of the Selector Utility.)
325
         *
326
         * @method delegate
327
         *
328
         * @param {String|HTMLElement|Array|NodeList} container An id, an element
329
         *  reference, or a collection of ids and/or elements to assign the
330
         *  listener to.
331
         * @param {String}   type     The type of event listener to append
332
         * @param {Function} fn        The method the event invokes
333
         * @param {Function|string} filter Function or CSS selector used to
334
         * determine for what element(s) the event listener should be called.
335
         * When a function is specified, the function should return an
336
         * HTML element.  Using a CSS Selector requires the inclusion of the
337
         * CSS Selector Utility.
338
         * @param {Object}   obj    An arbitrary object that will be
339
         *                             passed as a parameter to the listener
340
         * @param {Boolean|object}  overrideContext  If true, the value of the obj parameter becomes
341
         *                             the execution context of the listener. If an
342
         *                             object, this object becomes the execution
343
         *                             context.
344
         * @return {Boolean} Returns true if the action was successful or defered,
345
         *                   false if one or more of the elements
346
         *                   could not have the listener attached,
347
         *                   or if the operation throws an exception.
348
         * @static
349
         * @for Event
350
         */
351
        delegate: function (container, type, fn, filter, obj, overrideContext) {
352
 
353
            var sType = type,
354
                fnMouseDelegate,
355
                fnDelegate;
356
 
357
 
358
            if (Lang.isString(filter) && !YAHOO.util.Selector) {
359
                YAHOO.log("Using a CSS selector to define the filtering criteria for a delegated listener requires the Selector Utility.", "error", "Event");
360
                return false;
361
            }
362
 
363
 
364
            if (type == "mouseenter" || type == "mouseleave") {
365
 
366
                if (!Event._createMouseDelegate) {
367
                    YAHOO.log("Delegating a " + type + " event requires the event-mouseenter module.", "error", "Event");
368
                    return false;
369
                }
370
 
371
                //    Look up the real event--either mouseover or mouseout
372
                sType = Event._getType(type);
373
 
374
                fnMouseDelegate = Event._createMouseDelegate(fn, obj, overrideContext);
375
 
376
                fnDelegate = Event._createDelegate(function (event, matchedEl, container) {
377
 
378
                    return fnMouseDelegate.call(matchedEl, event, container);
379
 
380
                }, filter, obj, overrideContext);
381
 
382
            }
383
            else {
384
 
385
                fnDelegate = Event._createDelegate(fn, filter, obj, overrideContext);
386
 
387
            }
388
 
389
            delegates.push([container, sType, fn, fnDelegate]);
390
 
391
            return Event.on(container, sType, fnDelegate);
392
 
393
        },
394
 
395
 
396
        /**
397
         * Removes a delegated event listener.
398
         *
399
         * @method removeDelegate
400
         *
401
         * @param {String|HTMLElement|Array|NodeList} container An id, an element
402
         *  reference, or a collection of ids and/or elements to remove
403
         *  the listener from.
404
         * @param {String} type The type of event to remove.
405
         * @param {Function} fn The method the event invokes.  If fn is
406
         *  undefined, then all event listeners for the type of event are
407
         *  removed.
408
         * @return {boolean} Returns true if the unbind was successful, false
409
         *  otherwise.
410
         * @static
411
         * @for Event
412
         */
413
        removeDelegate: function (container, type, fn) {
414
 
415
            var sType = type,
416
                returnVal = false,
417
                index,
418
                cacheItem;
419
 
420
            //    Look up the real event--either mouseover or mouseout
421
            if (type == "mouseenter" || type == "mouseleave") {
422
                sType = Event._getType(type);
423
            }
424
 
425
            index = Event._getCacheIndex(delegates, container, sType, fn);
426
 
427
            if (index >= 0) {
428
                cacheItem = delegates[index];
429
            }
430
 
431
 
432
            if (container && cacheItem) {
433
 
434
                returnVal = Event.removeListener(cacheItem[0], cacheItem[1], cacheItem[3]);
435
 
436
                if (returnVal) {
437
                    delete delegates[index][2];
438
                    delete delegates[index][3];
439
                    delegates.splice(index, 1);
440
                }
441
 
442
            }
443
 
444
            return returnVal;
445
 
446
        }
447
 
448
    });
449
 
450
}());
451
 
452
 
453
/**
454
 * Augments the Event Utility with support for the mouseenter and mouseleave
455
 * events:  A mouseenter event fires the first time the mouse enters an
456
 * element; a mouseleave event first the first time the mouse leaves an
457
 * element.
458
 *
459
 * @module event-mouseenter
460
 * @title Event Utility mouseenter and mouseout Module
461
 * @namespace YAHOO.util
462
 * @requires event
463
 */
464
 
465
(function () {
466
 
467
    var Event = YAHOO.util.Event,
468
        Lang = YAHOO.lang,
469
 
470
        addListener = Event.addListener,
471
        removeListener = Event.removeListener,
472
        getListeners = Event.getListeners,
473
 
474
        delegates = [],
475
 
476
        specialTypes = {
477
            mouseenter: "mouseover",
478
            mouseleave: "mouseout"
479
        },
480
 
481
        remove = function(el, type, fn) {
482
 
483
            var index = Event._getCacheIndex(delegates, el, type, fn),
484
                cacheItem,
485
                returnVal;
486
 
487
            if (index >= 0) {
488
                cacheItem = delegates[index];
489
            }
490
 
491
            if (el && cacheItem) {
492
 
493
                //    removeListener will translate the value of type
494
                returnVal = removeListener.call(Event, cacheItem[0], type, cacheItem[3]);
495
 
496
                if (returnVal) {
497
                    delete delegates[index][2];
498
                    delete delegates[index][3];
499
                    delegates.splice(index, 1);
500
                }
501
 
502
            }
503
 
504
            return returnVal;
505
 
506
        };
507
 
508
 
509
    Lang.augmentObject(Event._specialTypes, specialTypes);
510
 
511
    Lang.augmentObject(Event, {
512
 
513
        /**
514
         * Creates a delegate function used to call mouseover and mouseleave
515
         * event listeners specified via the
516
         * <code>YAHOO.util.Event.addListener</code>
517
         * or <code>YAHOO.util.Event.on</code> method.
518
         *
519
         * @method _createMouseDelegate
520
         *
521
         * @param {Function} fn        The method (event listener) to call
522
         * @param {Object}   obj    An arbitrary object that will be
523
         *                             passed as a parameter to the listener
524
         * @param {Boolean|object}  overrideContext  If true, the value of the
525
         *                             obj parameter becomes the execution context
526
         *                          of the listener. If an object, this object
527
         *                          becomes the execution context.
528
         * @return {Function} Function that will call the event listener
529
         * specified by either the <code>YAHOO.util.Event.addListener</code>
530
         * or <code>YAHOO.util.Event.on</code> method.
531
         * @private
532
         * @static
533
         * @for Event
534
         */
535
        _createMouseDelegate: function (fn, obj, overrideContext) {
536
 
537
            return function (event, container) {
538
 
539
                var el = this,
540
                    relatedTarget = Event.getRelatedTarget(event),
541
                    context,
542
                    args;
543
 
544
                if (el != relatedTarget && !YAHOO.util.Dom.isAncestor(el, relatedTarget)) {
545
 
546
                    context = el;
547
 
548
                    if (overrideContext) {
549
                        if (overrideContext === true) {
550
                            context = obj;
551
                        } else {
552
                            context = overrideContext;
553
                        }
554
                    }
555
 
556
                    // The default args passed back to a mouseenter or
557
                    // mouseleave listener are: the event, and any object
558
                    // the user passed when subscribing
559
 
560
                    args = [event, obj];
561
 
562
                    // Add the element and delegation container as arguments
563
                    // when delegating mouseenter and mouseleave
564
 
565
                    if (container) {
566
                        args.splice(1, 0, el, container);
567
                    }
568
 
569
                    return fn.apply(context, args);
570
 
571
                }
572
 
573
            };
574
 
575
        },
576
 
577
        addListener: function (el, type, fn, obj, overrideContext) {
578
 
579
            var fnDelegate,
580
                returnVal;
581
 
582
            if (specialTypes[type]) {
583
 
584
                fnDelegate = Event._createMouseDelegate(fn, obj, overrideContext);
585
 
586
                fnDelegate.mouseDelegate = true;
587
 
588
                delegates.push([el, type, fn, fnDelegate]);
589
 
590
                //    addListener will translate the value of type
591
                returnVal = addListener.call(Event, el, type, fnDelegate);
592
 
593
            }
594
            else {
595
                returnVal = addListener.apply(Event, arguments);
596
            }
597
 
598
            return returnVal;
599
 
600
        },
601
 
602
        removeListener: function (el, type, fn) {
603
 
604
            var returnVal;
605
 
606
            if (specialTypes[type]) {
607
                returnVal = remove.apply(Event, arguments);
608
            }
609
            else {
610
                returnVal = removeListener.apply(Event, arguments);
611
            }
612
 
613
            return returnVal;
614
 
615
        },
616
 
617
        getListeners: function (el, type) {
618
 
619
            //    If the user specified the type as mouseover or mouseout,
620
            //    need to filter out those used by mouseenter and mouseleave.
621
            //    If the user specified the type as mouseenter or mouseleave,
622
            //    need to filter out the true mouseover and mouseout listeners.
623
 
624
            var listeners = [],
625
                elListeners,
626
                bMouseOverOrOut = (type === "mouseover" || type === "mouseout"),
627
                bMouseDelegate,
628
                i,
629
                l;
630
 
631
            if (type && (bMouseOverOrOut || specialTypes[type])) {
632
 
633
                elListeners = getListeners.call(Event, el, this._getType(type));
634
 
635
                if (elListeners) {
636
 
637
                    for (i=elListeners.length-1; i>-1; i--) {
638
 
639
                        l = elListeners[i];
640
                        bMouseDelegate = l.fn.mouseDelegate;
641
 
642
                        if ((specialTypes[type] && bMouseDelegate) || (bMouseOverOrOut && !bMouseDelegate)) {
643
                            listeners.push(l);
644
                        }
645
 
646
                    }
647
 
648
                }
649
 
650
            }
651
            else {
652
                listeners = getListeners.apply(Event, arguments);
653
            }
654
 
655
            return (listeners && listeners.length) ? listeners : null;
656
 
657
        }
658
 
659
    }, true);
660
 
661
    Event.on = Event.addListener;
662
 
663
}());
664
YAHOO.register("event-mouseenter", YAHOO.util.Event, {version: "2.9.0", build: "2800"});
665
 
666
var Y = YAHOO,
667
    Y_DOM = YAHOO.util.Dom,
668
    EMPTY_ARRAY = [],
669
    Y_UA = Y.env.ua,
670
    Y_Lang = Y.lang,
671
    Y_DOC = document,
672
    Y_DOCUMENT_ELEMENT = Y_DOC.documentElement,
673
 
674
    Y_DOM_inDoc = Y_DOM.inDocument,
675
    Y_mix = Y_Lang.augmentObject,
676
    Y_guid = Y_DOM.generateId,
677
 
678
    Y_getDoc = function(element) {
679
        var doc = Y_DOC;
680
        if (element) {
681
            doc = (element.nodeType === 9) ? element : // element === document
682
                element.ownerDocument || // element === DOM node
683
                element.document || // element === window
684
                Y_DOC; // default
685
        }
686
 
687
        return doc;
688
    },
689
 
690
    Y_Array = function(o, startIdx) {
691
        var l, a, start = startIdx || 0;
692
 
693
        // IE errors when trying to slice HTMLElement collections
694
        try {
695
            return Array.prototype.slice.call(o, start);
696
        } catch (e) {
697
            a = [];
698
            l = o.length;
699
            for (; start < l; start++) {
700
                a.push(o[start]);
701
            }
702
            return a;
703
        }
704
    },
705
 
706
    Y_DOM_allById = function(id, root) {
707
        root = root || Y_DOC;
708
        var nodes = [],
709
            ret = [],
710
            i,
711
            node;
712
 
713
        if (root.querySelectorAll) {
714
            ret = root.querySelectorAll('[id="' + id + '"]');
715
        } else if (root.all) {
716
            nodes = root.all(id);
717
 
718
            if (nodes) {
719
                // root.all may return HTMLElement or HTMLCollection.
720
                // some elements are also HTMLCollection (FORM, SELECT).
721
                if (nodes.nodeName) {
722
                    if (nodes.id === id) { // avoid false positive on name
723
                        ret.push(nodes);
724
                        nodes = EMPTY_ARRAY; // done, no need to filter
725
                    } else { //  prep for filtering
726
                        nodes = [nodes];
727
                    }
728
                }
729
 
730
                if (nodes.length) {
731
                    // filter out matches on node.name
732
                    // and element.id as reference to element with id === 'id'
733
                    for (i = 0; node = nodes[i++];) {
734
                        if (node.id === id  ||
735
                                (node.attributes && node.attributes.id &&
736
                                node.attributes.id.value === id)) {
737
                            ret.push(node);
738
                        }
739
                    }
740
                }
741
            }
742
        } else {
743
            ret = [Y_getDoc(root).getElementById(id)];
744
        }
745
 
746
        return ret;
747
    };
748
 
749
/**
750
 * The selector-native module provides support for native querySelector
751
 * @module dom
752
 * @submodule selector-native
753
 * @for Selector
754
 */
755
 
756
/**
757
 * Provides support for using CSS selectors to query the DOM
758
 * @class Selector
759
 * @static
760
 * @for Selector
761
 */
762
 
763
var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
764
    OWNER_DOCUMENT = 'ownerDocument',
765
 
766
Selector = {
767
    _foundCache: [],
768
 
769
    useNative: true,
770
 
771
    _compare: ('sourceIndex' in Y_DOCUMENT_ELEMENT) ?
772
        function(nodeA, nodeB) {
773
            var a = nodeA.sourceIndex,
774
                b = nodeB.sourceIndex;
775
 
776
            if (a === b) {
777
                return 0;
778
            } else if (a > b) {
779
                return 1;
780
            }
781
 
782
            return -1;
783
 
784
        } : (Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION] ?
785
        function(nodeA, nodeB) {
786
            if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
787
                return -1;
788
            } else {
789
                return 1;
790
            }
791
        } :
792
        function(nodeA, nodeB) {
793
            var rangeA, rangeB, compare;
794
            if (nodeA && nodeB) {
795
                rangeA = nodeA[OWNER_DOCUMENT].createRange();
796
                rangeA.setStart(nodeA, 0);
797
                rangeB = nodeB[OWNER_DOCUMENT].createRange();
798
                rangeB.setStart(nodeB, 0);
799
                compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
800
            }
801
 
802
            return compare;
803
 
804
    }),
805
 
806
    _sort: function(nodes) {
807
        if (nodes) {
808
            nodes = Y_Array(nodes, 0, true);
809
            if (nodes.sort) {
810
                nodes.sort(Selector._compare);
811
            }
812
        }
813
 
814
        return nodes;
815
    },
816
 
817
    _deDupe: function(nodes) {
818
        var ret = [],
819
            i, node;
820
 
821
        for (i = 0; (node = nodes[i++]);) {
822
            if (!node._found) {
823
                ret[ret.length] = node;
824
                node._found = true;
825
            }
826
        }
827
 
828
        for (i = 0; (node = ret[i++]);) {
829
            node._found = null;
830
            node.removeAttribute('_found');
831
        }
832
 
833
        return ret;
834
    },
835
 
836
    /**
837
     * Retrieves a set of nodes based on a given CSS selector.
838
     * @method query
839
     *
840
     * @param {string} selector The CSS Selector to test the node against.
841
     * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
842
     * @param {Boolean} firstOnly optional Whether or not to return only the first match.
843
     * @return {Array} An array of nodes that match the given selector.
844
     * @static
845
     */
846
    query: function(selector, root, firstOnly, skipNative) {
847
        if (typeof root == 'string') {
848
            root = Y_DOM.get(root);
849
            if (!root) {
850
                return (firstOnly) ? null : [];
851
            }
852
        } else {
853
            root = root || Y_DOC;
854
        }
855
 
856
        var ret = [],
857
            useNative = (Selector.useNative && Y_DOC.querySelector && !skipNative),
858
            queries = [[selector, root]],
859
            query,
860
            result,
861
            i,
862
            fn = (useNative) ? Selector._nativeQuery : Selector._bruteQuery;
863
 
864
        if (selector && fn) {
865
            // split group into seperate queries
866
            if (!skipNative && // already done if skipping
867
                    (!useNative || root.tagName)) { // split native when element scoping is needed
868
                queries = Selector._splitQueries(selector, root);
869
            }
870
 
871
            for (i = 0; (query = queries[i++]);) {
872
                result = fn(query[0], query[1], firstOnly);
873
                if (!firstOnly) { // coerce DOM Collection to Array
874
                    result = Y_Array(result, 0, true);
875
                }
876
                if (result) {
877
                    ret = ret.concat(result);
878
                }
879
            }
880
 
881
            if (queries.length > 1) { // remove dupes and sort by doc order
882
                ret = Selector._sort(Selector._deDupe(ret));
883
            }
884
        }
885
 
886
        Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
887
        return (firstOnly) ? (ret[0] || null) : ret;
888
 
889
    },
890
 
891
    // allows element scoped queries to begin with combinator
892
    // e.g. query('> p', document.body) === query('body > p')
893
    _splitQueries: function(selector, node) {
894
        var groups = selector.split(','),
895
            queries = [],
896
            prefix = '',
897
            i, len;
898
 
899
        if (node) {
900
            // enforce for element scoping
901
            if (node.tagName) {
902
                node.id = node.id || Y_guid();
903
                prefix = '[id="' + node.id + '"] ';
904
            }
905
 
906
            for (i = 0, len = groups.length; i < len; ++i) {
907
                selector =  prefix + groups[i];
908
                queries.push([selector, node]);
909
            }
910
        }
911
 
912
        return queries;
913
    },
914
 
915
    _nativeQuery: function(selector, root, one) {
916
        if (Y_UA.webkit && selector.indexOf(':checked') > -1 &&
917
                (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
918
            return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
919
        }
920
        try {
921
            //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
922
            return root['querySelector' + (one ? '' : 'All')](selector);
923
        } catch(e) { // fallback to brute if available
924
            //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
925
            return Selector.query(selector, root, one, true); // redo with skipNative true
926
        }
927
    },
928
 
929
    filter: function(nodes, selector) {
930
        var ret = [],
931
            i, node;
932
 
933
        if (nodes && selector) {
934
            for (i = 0; (node = nodes[i++]);) {
935
                if (Selector.test(node, selector)) {
936
                    ret[ret.length] = node;
937
                }
938
            }
939
        } else {
940
            Y.log('invalid filter input (nodes: ' + nodes +
941
                    ', selector: ' + selector + ')', 'warn', 'Selector');
942
        }
943
 
944
        return ret;
945
    },
946
 
947
    test: function(node, selector, root) {
948
        var ret = false,
949
            groups = selector.split(','),
950
            useFrag = false,
951
            parent,
952
            item,
953
            items,
954
            frag,
955
            i, j, group;
956
 
957
        if (node && node.tagName) { // only test HTMLElements
958
 
959
            // we need a root if off-doc
960
            if (!root && !Y_DOM_inDoc(node)) {
961
                parent = node.parentNode;
962
                if (parent) {
963
                    root = parent;
964
                } else { // only use frag when no parent to query
965
                    frag = node[OWNER_DOCUMENT].createDocumentFragment();
966
                    frag.appendChild(node);
967
                    root = frag;
968
                    useFrag = true;
969
                }
970
            }
971
            root = root || node[OWNER_DOCUMENT];
972
 
973
            if (!node.id) {
974
                node.id = Y_guid();
975
            }
976
            for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
977
                group += '[id="' + node.id + '"]';
978
                items = Selector.query(group, root);
979
 
980
                for (j = 0; item = items[j++];) {
981
                    if (item === node) {
982
                        ret = true;
983
                        break;
984
                    }
985
                }
986
                if (ret) {
987
                    break;
988
                }
989
            }
990
 
991
            if (useFrag) { // cleanup
992
                frag.removeChild(node);
993
            }
994
        }
995
 
996
        return ret;
997
    }
998
 
999
};
1000
 
1001
YAHOO.util.Selector = Selector;
1002
/**
1003
 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
1004
 * @module dom
1005
 * @submodule selector-css2
1006
 * @for Selector
1007
 */
1008
 
1009
/**
1010
 * Provides helper methods for collecting and filtering DOM elements.
1011
 */
1012
 
1013
var PARENT_NODE = 'parentNode',
1014
    TAG_NAME = 'tagName',
1015
    ATTRIBUTES = 'attributes',
1016
    COMBINATOR = 'combinator',
1017
    PSEUDOS = 'pseudos',
1018
 
1019
    SelectorCSS2 = {
1020
        _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
1021
        SORT_RESULTS: true,
1022
        _children: function(node, tag) {
1023
            var ret = node.children,
1024
                i,
1025
                children = [],
1026
                childNodes,
1027
                child;
1028
 
1029
            if (node.children && tag && node.children.tags) {
1030
                children = node.children.tags(tag);
1031
            } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
1032
                childNodes = ret || node.childNodes;
1033
                ret = [];
1034
                for (i = 0; (child = childNodes[i++]);) {
1035
                    if (child.tagName) {
1036
                        if (!tag || tag === child.tagName) {
1037
                            ret.push(child);
1038
                        }
1039
                    }
1040
                }
1041
            }
1042
 
1043
            return ret || [];
1044
        },
1045
 
1046
        _re: {
1047
            //attr: /(\[.*\])/g,
1048
            attr: /(\[[^\]]*\])/g,
1049
            //esc: /\\[:\[][\w\d\]]*/gi,
1050
            esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
1051
            //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i
1052
            pseudos: /(\([^\)]*\))/g
1053
        },
1054
 
1055
        /**
1056
         * Mapping of shorthand tokens to corresponding attribute selector
1057
         * @property shorthand
1058
         * @type object
1059
         */
1060
        shorthand: {
1061
            //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]',
1062
            '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]',
1063
            //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]',
1064
            //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]'
1065
            '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
1066
        },
1067
 
1068
        /**
1069
         * List of operators and corresponding boolean functions.
1070
         * These functions are passed the attribute and the current node's value of the attribute.
1071
         * @property operators
1072
         * @type object
1073
         */
1074
        operators: {
1075
            '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute
1076
            //'': '.+',
1077
            //'=': '^{val}$', // equality
1078
            '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
1079
            '|=': '^{val}(?:-|$)' // optional hyphen-delimited
1080
        },
1081
 
1082
        pseudos: {
1083
           'first-child': function(node) {
1084
                return Selector._children(node[PARENT_NODE])[0] === node;
1085
            }
1086
        },
1087
 
1088
        _bruteQuery: function(selector, root, firstOnly) {
1089
            var ret = [],
1090
                nodes = [],
1091
                tokens = Selector._tokenize(selector),
1092
                token = tokens[tokens.length - 1],
1093
                rootDoc = Y_getDoc(root),
1094
                child,
1095
                id,
1096
                className,
1097
                tagName;
1098
 
1099
 
1100
            // if we have an initial ID, set to root when in document
1101
            /*
1102
            if (tokens[0] && rootDoc === root &&
1103
                    (id = tokens[0].id) &&
1104
                    rootDoc.getElementById(id)) {
1105
                root = rootDoc.getElementById(id);
1106
            }
1107
            */
1108
 
1109
            if (token) {
1110
                // prefilter nodes
1111
                id = token.id;
1112
                className = token.className;
1113
                tagName = token.tagName || '*';
1114
 
1115
                if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
1116
                    // try ID first, unless no root.all && root not in document
1117
                    // (root.all works off document, but not getElementById)
1118
                    // TODO: move to allById?
1119
                    if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) {
1120
                        nodes = Y_DOM_allById(id, root);
1121
                    // try className
1122
                    } else if (className) {
1123
                        nodes = root.getElementsByClassName(className);
1124
                    } else { // default to tagName
1125
                        nodes = root.getElementsByTagName(tagName);
1126
                    }
1127
 
1128
                } else { // brute getElementsByTagName('*')
1129
                    child = root.firstChild;
1130
                    while (child) {
1131
                        if (child.tagName) { // only collect HTMLElements
1132
                            nodes.push(child);
1133
                        }
1134
                        child = child.nextSilbing || child.firstChild;
1135
                    }
1136
                }
1137
                if (nodes.length) {
1138
                    ret = Selector._filterNodes(nodes, tokens, firstOnly);
1139
                }
1140
            }
1141
 
1142
            return ret;
1143
        },
1144
 
1145
        _filterNodes: function(nodes, tokens, firstOnly) {
1146
            var i = 0,
1147
                j,
1148
                len = tokens.length,
1149
                n = len - 1,
1150
                result = [],
1151
                node = nodes[0],
1152
                tmpNode = node,
1153
                getters = Selector.getters,
1154
                operator,
1155
                combinator,
1156
                token,
1157
                path,
1158
                pass,
1159
                //FUNCTION = 'function',
1160
                value,
1161
                tests,
1162
                test;
1163
 
1164
            //do {
1165
            for (i = 0; (tmpNode = node = nodes[i++]);) {
1166
                n = len - 1;
1167
                path = null;
1168
 
1169
                testLoop:
1170
                while (tmpNode && tmpNode.tagName) {
1171
                    token = tokens[n];
1172
                    tests = token.tests;
1173
                    j = tests.length;
1174
                    if (j && !pass) {
1175
                        while ((test = tests[--j])) {
1176
                            operator = test[1];
1177
                            if (getters[test[0]]) {
1178
                                value = getters[test[0]](tmpNode, test[0]);
1179
                            } else {
1180
                                value = tmpNode[test[0]];
1181
                                // use getAttribute for non-standard attributes
1182
                                if (value === undefined && tmpNode.getAttribute) {
1183
                                    value = tmpNode.getAttribute(test[0]);
1184
                                }
1185
                            }
1186
 
1187
                            if ((operator === '=' && value !== test[2]) ||  // fast path for equality
1188
                                (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
1189
                                operator.test && !operator.test(value)) ||  // regex test
1190
                                (!operator.test && // protect against RegExp as function (webkit)
1191
                                        typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
1192
 
1193
                                // skip non element nodes or non-matching tags
1194
                                if ((tmpNode = tmpNode[path])) {
1195
                                    while (tmpNode &&
1196
                                        (!tmpNode.tagName ||
1197
                                            (token.tagName && token.tagName !== tmpNode.tagName))
1198
                                    ) {
1199
                                        tmpNode = tmpNode[path];
1200
                                    }
1201
                                }
1202
                                continue testLoop;
1203
                            }
1204
                        }
1205
                    }
1206
 
1207
                    n--; // move to next token
1208
                    // now that we've passed the test, move up the tree by combinator
1209
                    if (!pass && (combinator = token.combinator)) {
1210
                        path = combinator.axis;
1211
                        tmpNode = tmpNode[path];
1212
 
1213
                        // skip non element nodes
1214
                        while (tmpNode && !tmpNode.tagName) {
1215
                            tmpNode = tmpNode[path];
1216
                        }
1217
 
1218
                        if (combinator.direct) { // one pass only
1219
                            path = null;
1220
                        }
1221
 
1222
                    } else { // success if we made it this far
1223
                        result.push(node);
1224
                        if (firstOnly) {
1225
                            return result;
1226
                        }
1227
                        break;
1228
                    }
1229
                }
1230
            }// while (tmpNode = node = nodes[++i]);
1231
            node = tmpNode = null;
1232
            return result;
1233
        },
1234
 
1235
        combinators: {
1236
            ' ': {
1237
                axis: 'parentNode'
1238
            },
1239
 
1240
            '>': {
1241
                axis: 'parentNode',
1242
                direct: true
1243
            },
1244
 
1245
 
1246
            '+': {
1247
                axis: 'previousSibling',
1248
                direct: true
1249
            }
1250
        },
1251
 
1252
        _parsers: [
1253
            {
1254
                name: ATTRIBUTES,
1255
                //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
1256
                re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
1257
                fn: function(match, token) {
1258
                    var operator = match[2] || '',
1259
                        operators = Selector.operators,
1260
                        escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
1261
                        test;
1262
 
1263
                    // add prefiltering for ID and CLASS
1264
                    if ((match[1] === 'id' && operator === '=') ||
1265
                            (match[1] === 'className' &&
1266
                            Y_DOCUMENT_ELEMENT.getElementsByClassName &&
1267
                            (operator === '~=' || operator === '='))) {
1268
                        token.prefilter = match[1];
1269
 
1270
 
1271
                        match[3] = escVal;
1272
 
1273
                        // escape all but ID for prefilter, which may run through QSA (via Dom.allById)
1274
                        token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
1275
 
1276
                    }
1277
 
1278
                    // add tests
1279
                    if (operator in operators) {
1280
                        test = operators[operator];
1281
                        if (typeof test === 'string') {
1282
                            match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
1283
                            test = new RegExp(test.replace('{val}', match[3]));
1284
                        }
1285
                        match[2] = test;
1286
                    }
1287
                    if (!token.last || token.prefilter !== match[1]) {
1288
                        return match.slice(1);
1289
                    }
1290
                }
1291
 
1292
            },
1293
            {
1294
                name: TAG_NAME,
1295
                re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
1296
                fn: function(match, token) {
1297
                    var tag = match[1].toUpperCase();
1298
                    token.tagName = tag;
1299
 
1300
                    if (tag !== '*' && (!token.last || token.prefilter)) {
1301
                        return [TAG_NAME, '=', tag];
1302
                    }
1303
                    if (!token.prefilter) {
1304
                        token.prefilter = 'tagName';
1305
                    }
1306
                }
1307
            },
1308
            {
1309
                name: COMBINATOR,
1310
                re: /^\s*([>+~]|\s)\s*/,
1311
                fn: function(match, token) {
1312
                }
1313
            },
1314
            {
1315
                name: PSEUDOS,
1316
                re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
1317
                fn: function(match, token) {
1318
                    var test = Selector[PSEUDOS][match[1]];
1319
                    if (test) { // reorder match array and unescape special chars for tests
1320
                        if (match[2]) {
1321
                            match[2] = match[2].replace(/\\/g, '');
1322
                        }
1323
                        return [match[2], test];
1324
                    } else { // selector token not supported (possibly missing CSS3 module)
1325
                        return false;
1326
                    }
1327
                }
1328
            }
1329
            ],
1330
 
1331
        _getToken: function(token) {
1332
            return {
1333
                tagName: null,
1334
                id: null,
1335
                className: null,
1336
                attributes: {},
1337
                combinator: null,
1338
                tests: []
1339
            };
1340
        },
1341
 
1342
        /**
1343
            Break selector into token units per simple selector.
1344
            Combinator is attached to the previous token.
1345
         */
1346
        _tokenize: function(selector) {
1347
            selector = selector || '';
1348
            selector = Selector._replaceShorthand(Y_Lang.trim(selector));
1349
            var token = Selector._getToken(),     // one token per simple selector (left selector holds combinator)
1350
                query = selector, // original query for debug report
1351
                tokens = [],    // array of tokens
1352
                found = false,  // whether or not any matches were found this pass
1353
                match,         // the regex match
1354
                test,
1355
                i, parser;
1356
 
1357
            /*
1358
                Search for selector patterns, store, and strip them from the selector string
1359
                until no patterns match (invalid selector) or we run out of chars.
1360
 
1361
                Multiple attributes and pseudos are allowed, in any order.
1362
                for example:
1363
                    'form:first-child[type=button]:not(button)[lang|=en]'
1364
            */
1365
 
1366
            outer:
1367
            do {
1368
                found = false; // reset after full pass
1369
 
1370
                for (i = 0; (parser = Selector._parsers[i++]);) {
1371
                    if ( (match = parser.re.exec(selector)) ) { // note assignment
1372
                        if (parser.name !== COMBINATOR ) {
1373
                            token.selector = selector;
1374
                        }
1375
                        selector = selector.replace(match[0], ''); // strip current match from selector
1376
                        if (!selector.length) {
1377
                            token.last = true;
1378
                        }
1379
 
1380
                        if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
1381
                            match[1] = Selector._attrFilters[match[1]];
1382
                        }
1383
 
1384
                        test = parser.fn(match, token);
1385
                        if (test === false) { // selector not supported
1386
                            found = false;
1387
                            break outer;
1388
                        } else if (test) {
1389
                            token.tests.push(test);
1390
                        }
1391
 
1392
                        if (!selector.length || parser.name === COMBINATOR) {
1393
                            tokens.push(token);
1394
                            token = Selector._getToken(token);
1395
                            if (parser.name === COMBINATOR) {
1396
                                token.combinator = Selector.combinators[match[1]];
1397
                            }
1398
                        }
1399
                        found = true;
1400
 
1401
 
1402
                    }
1403
                }
1404
            } while (found && selector.length);
1405
 
1406
            if (!found || selector.length) { // not fully parsed
1407
                Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
1408
                tokens = [];
1409
            }
1410
            return tokens;
1411
        },
1412
 
1413
        _replaceShorthand: function(selector) {
1414
            var shorthand = Selector.shorthand,
1415
                esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
1416
                attrs,
1417
                pseudos,
1418
                re, i, len;
1419
 
1420
            if (esc) {
1421
                selector = selector.replace(Selector._re.esc, '\uE000');
1422
            }
1423
 
1424
            attrs = selector.match(Selector._re.attr);
1425
            pseudos = selector.match(Selector._re.pseudos);
1426
 
1427
            if (attrs) {
1428
                selector = selector.replace(Selector._re.attr, '\uE001');
1429
            }
1430
 
1431
            if (pseudos) {
1432
                selector = selector.replace(Selector._re.pseudos, '\uE002');
1433
            }
1434
 
1435
 
1436
            for (re in shorthand) {
1437
                if (shorthand.hasOwnProperty(re)) {
1438
                    selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
1439
                }
1440
            }
1441
 
1442
            if (attrs) {
1443
                for (i = 0, len = attrs.length; i < len; ++i) {
1444
                    selector = selector.replace(/\uE001/, attrs[i]);
1445
                }
1446
            }
1447
 
1448
            if (pseudos) {
1449
                for (i = 0, len = pseudos.length; i < len; ++i) {
1450
                    selector = selector.replace(/\uE002/, pseudos[i]);
1451
                }
1452
            }
1453
 
1454
            selector = selector.replace(/\[/g, '\uE003');
1455
            selector = selector.replace(/\]/g, '\uE004');
1456
 
1457
            selector = selector.replace(/\(/g, '\uE005');
1458
            selector = selector.replace(/\)/g, '\uE006');
1459
 
1460
            if (esc) {
1461
                for (i = 0, len = esc.length; i < len; ++i) {
1462
                    selector = selector.replace('\uE000', esc[i]);
1463
                }
1464
            }
1465
 
1466
            return selector;
1467
        },
1468
 
1469
        _attrFilters: {
1470
            'class': 'className',
1471
            'for': 'htmlFor'
1472
        },
1473
 
1474
        getters: {
1475
            href: function(node, attr) {
1476
                return Y_DOM.getAttribute(node, attr);
1477
            }
1478
        }
1479
    };
1480
 
1481
Y_mix(Selector, SelectorCSS2, true);
1482
Selector.getters.src = Selector.getters.rel = Selector.getters.href;
1483
 
1484
// IE wants class with native queries
1485
if (Selector.useNative && Y_DOC.querySelector) {
1486
    Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]';
1487
}
1488
 
1489
/**
1490
 * The selector css3 module provides support for css3 selectors.
1491
 * @module dom
1492
 * @submodule selector-css3
1493
 * @for Selector
1494
 */
1495
 
1496
/*
1497
    an+b = get every _a_th node starting at the _b_th
1498
    0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
1499
    1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
1500
    an+0 = get every _a_th element, "0" may be omitted
1501
*/
1502
 
1503
Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
1504
 
1505
Selector._getNth = function(node, expr, tag, reverse) {
1506
    Selector._reNth.test(expr);
1507
    var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
1508
        n = RegExp.$2, // "n"
1509
        oddeven = RegExp.$3, // "odd" or "even"
1510
        b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
1511
        result = [],
1512
        siblings = Selector._children(node.parentNode, tag),
1513
        op;
1514
 
1515
    if (oddeven) {
1516
        a = 2; // always every other
1517
        op = '+';
1518
        n = 'n';
1519
        b = (oddeven === 'odd') ? 1 : 0;
1520
    } else if ( isNaN(a) ) {
1521
        a = (n) ? 1 : 0; // start from the first or no repeat
1522
    }
1523
 
1524
    if (a === 0) { // just the first
1525
        if (reverse) {
1526
            b = siblings.length - b + 1;
1527
        }
1528
 
1529
        if (siblings[b - 1] === node) {
1530
            return true;
1531
        } else {
1532
            return false;
1533
        }
1534
 
1535
    } else if (a < 0) {
1536
        reverse = !!reverse;
1537
        a = Math.abs(a);
1538
    }
1539
 
1540
    if (!reverse) {
1541
        for (var i = b - 1, len = siblings.length; i < len; i += a) {
1542
            if ( i >= 0 && siblings[i] === node ) {
1543
                return true;
1544
            }
1545
        }
1546
    } else {
1547
        for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
1548
            if ( i < len && siblings[i] === node ) {
1549
                return true;
1550
            }
1551
        }
1552
    }
1553
    return false;
1554
};
1555
 
1556
Y_mix(Selector.pseudos, {
1557
    'root': function(node) {
1558
        return node === node.ownerDocument.documentElement;
1559
    },
1560
 
1561
    'nth-child': function(node, expr) {
1562
        return Selector._getNth(node, expr);
1563
    },
1564
 
1565
    'nth-last-child': function(node, expr) {
1566
        return Selector._getNth(node, expr, null, true);
1567
    },
1568
 
1569
    'nth-of-type': function(node, expr) {
1570
        return Selector._getNth(node, expr, node.tagName);
1571
    },
1572
 
1573
    'nth-last-of-type': function(node, expr) {
1574
        return Selector._getNth(node, expr, node.tagName, true);
1575
    },
1576
 
1577
    'last-child': function(node) {
1578
        var children = Selector._children(node.parentNode);
1579
        return children[children.length - 1] === node;
1580
    },
1581
 
1582
    'first-of-type': function(node) {
1583
        return Selector._children(node.parentNode, node.tagName)[0] === node;
1584
    },
1585
 
1586
    'last-of-type': function(node) {
1587
        var children = Selector._children(node.parentNode, node.tagName);
1588
        return children[children.length - 1] === node;
1589
    },
1590
 
1591
    'only-child': function(node) {
1592
        var children = Selector._children(node.parentNode);
1593
        return children.length === 1 && children[0] === node;
1594
    },
1595
 
1596
    'only-of-type': function(node) {
1597
        var children = Selector._children(node.parentNode, node.tagName);
1598
        return children.length === 1 && children[0] === node;
1599
    },
1600
 
1601
    'empty': function(node) {
1602
        return node.childNodes.length === 0;
1603
    },
1604
 
1605
    'not': function(node, expr) {
1606
        return !Selector.test(node, expr);
1607
    },
1608
 
1609
    'contains': function(node, expr) {
1610
        var text = node.innerText || node.textContent || '';
1611
        return text.indexOf(expr) > -1;
1612
    },
1613
 
1614
    'checked': function(node) {
1615
        return (node.checked === true || node.selected === true);
1616
    },
1617
 
1618
    enabled: function(node) {
1619
        return (node.disabled !== undefined && !node.disabled);
1620
    },
1621
 
1622
    disabled: function(node) {
1623
        return (node.disabled);
1624
    }
1625
});
1626
 
1627
Y_mix(Selector.operators, {
1628
    '^=': '^{val}', // Match starts with value
1629
    '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value
1630
    '$=': '{val}$', // Match ends with value
1631
    '*=': '{val}' // Match contains value as substring
1632
});
1633
 
1634
Selector.combinators['~'] = {
1635
    axis: 'previousSibling'
1636
};
1637
YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"});
1638
 
1639
 
1640
 
1641
/****************************************************************************/
1642
/****************************************************************************/
1643
/****************************************************************************/
1644
 
1645
var Dom = YAHOO.util.Dom;
1646
 
1647
/**
1648
 * The ColumnSet class defines and manages a DataTable's Columns,
1649
 * including nested hierarchies and access to individual Column instances.
1650
 *
1651
 * @namespace YAHOO.widget
1652
 * @class ColumnSet
1653
 * @uses YAHOO.util.EventProvider
1654
 * @constructor
1655
 * @param aDefinitions {Object[]} Array of object literals that define cells in
1656
 * the THEAD.
1657
 */
1658
YAHOO.widget.ColumnSet = function(aDefinitions) {
1659
    this._sId = Dom.generateId(null, "yui-cs"); // "yui-cs" + YAHOO.widget.ColumnSet._nCount;
1660
 
1661
    // First clone the defs
1662
    aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions);
1663
    this._init(aDefinitions);
1664
 
1665
    YAHOO.widget.ColumnSet._nCount++;
1666
    YAHOO.log("ColumnSet initialized", "info", this.toString());
1667
};
1668
 
1669
/////////////////////////////////////////////////////////////////////////////
1670
//
1671
// Private member variables
1672
//
1673
/////////////////////////////////////////////////////////////////////////////
1674
 
1675
/**
1676
 * Internal class variable to index multiple ColumnSet instances.
1677
 *
1678
 * @property ColumnSet._nCount
1679
 * @type Number
1680
 * @private
1681
 * @static
1682
 */
1683
YAHOO.widget.ColumnSet._nCount = 0;
1684
 
1685
YAHOO.widget.ColumnSet.prototype = {
1686
    /**
1687
     * Unique instance name.
1688
     *
1689
     * @property _sId
1690
     * @type String
1691
     * @private
1692
     */
1693
    _sId : null,
1694
 
1695
    /**
1696
     * Array of object literal Column definitions passed to the constructor.
1697
     *
1698
     * @property _aDefinitions
1699
     * @type Object[]
1700
     * @private
1701
     */
1702
    _aDefinitions : null,
1703
 
1704
    /////////////////////////////////////////////////////////////////////////////
1705
    //
1706
    // Public member variables
1707
    //
1708
    /////////////////////////////////////////////////////////////////////////////
1709
 
1710
    /**
1711
     * Top-down tree representation of Column hierarchy.
1712
     *
1713
     * @property tree
1714
     * @type YAHOO.widget.Column[]
1715
     */
1716
    tree : null,
1717
 
1718
    /**
1719
     * Flattened representation of all Columns.
1720
     *
1721
     * @property flat
1722
     * @type YAHOO.widget.Column[]
1723
     * @default []
1724
     */
1725
    flat : null,
1726
 
1727
    /**
1728
     * Array of Columns that map one-to-one to a table column.
1729
     *
1730
     * @property keys
1731
     * @type YAHOO.widget.Column[]
1732
     * @default []
1733
     */
1734
    keys : null,
1735
 
1736
    /**
1737
     * ID index of nested parent hierarchies for HEADERS accessibility attribute.
1738
     *
1739
     * @property headers
1740
     * @type String[]
1741
     * @default []
1742
     */
1743
    headers : null,
1744
 
1745
    /////////////////////////////////////////////////////////////////////////////
1746
    //
1747
    // Private methods
1748
    //
1749
    /////////////////////////////////////////////////////////////////////////////
1750
 
1751
    /**
1752
     * Initializes ColumnSet instance with data from Column definitions.
1753
     *
1754
     * @method _init
1755
     * @param aDefinitions {Object[]} Array of object literals that define cells in
1756
     * the THEAD .
1757
     * @private
1758
     */
1759
 
1760
    _init : function(aDefinitions) {
1761
        // DOM tree representation of all Columns
1762
        var tree = [];
1763
        // Flat representation of all Columns
1764
        var flat = [];
1765
        // Flat representation of only Columns that are meant to display data
1766
        var keys = [];
1767
        // Array of HEADERS attribute values for all keys in the "keys" array
1768
        var headers = [];
1769
 
1770
        // Tracks current node list depth being tracked
1771
        var nodeDepth = -1;
1772
 
1773
        // Internal recursive function to define Column instances
1774
        var parseColumns = function(nodeList, parent) {
1775
            // One level down
1776
            nodeDepth++;
1777
 
1778
            // Create corresponding tree node if not already there for this depth
1779
            if(!tree[nodeDepth]) {
1780
                tree[nodeDepth] = [];
1781
            }
1782
 
1783
 
1784
            // Parse each node at this depth for attributes and any children
1785
            for(var j=0; j<nodeList.length; j++) {
1786
                var currentNode = nodeList[j];
1787
 
1788
                // Instantiate a new Column for each node
1789
                var oColumn = new YAHOO.widget.Column(currentNode);
1790
 
1791
                // Cross-reference Column ID back to the original object literal definition
1792
                currentNode.yuiColumnId = oColumn._sId;
1793
 
1794
                // Add the new Column to the flat list
1795
                flat.push(oColumn);
1796
 
1797
                // Assign its parent as an attribute, if applicable
1798
                if(parent) {
1799
                    oColumn._oParent = parent;
1800
                }
1801
 
1802
                // The Column has descendants
1803
                if(YAHOO.lang.isArray(currentNode.children)) {
1804
                    oColumn.children = currentNode.children;
1805
 
1806
                    // Determine COLSPAN value for this Column
1807
                    var terminalChildNodes = 0;
1808
                    var countTerminalChildNodes = function(ancestor) {
1809
                        var descendants = ancestor.children;
1810
                        // Drill down each branch and count terminal nodes
1811
                        for(var k=0; k<descendants.length; k++) {
1812
                            // Keep drilling down
1813
                            if(YAHOO.lang.isArray(descendants[k].children)) {
1814
                                countTerminalChildNodes(descendants[k]);
1815
                            }
1816
                            // Reached branch terminus
1817
                            else {
1818
                                terminalChildNodes++;
1819
                            }
1820
                        }
1821
                    };
1822
                    countTerminalChildNodes(currentNode);
1823
                    oColumn._nColspan = terminalChildNodes;
1824
 
1825
                    // Cascade certain properties to children if not defined on their own
1826
                    var currentChildren = currentNode.children;
1827
                    for(var k=0; k<currentChildren.length; k++) {
1828
                        var child = currentChildren[k];
1829
                        if(oColumn.className && (child.className === undefined)) {
1830
                            child.className = oColumn.className;
1831
                        }
1832
                        if(oColumn.editor && (child.editor === undefined)) {
1833
                            child.editor = oColumn.editor;
1834
                        }
1835
                        //TODO: Deprecated
1836
                        if(oColumn.editorOptions && (child.editorOptions === undefined)) {
1837
                            child.editorOptions = oColumn.editorOptions;
1838
                        }
1839
                        if(oColumn.formatter && (child.formatter === undefined)) {
1840
                            child.formatter = oColumn.formatter;
1841
                        }
1842
                        if(oColumn.resizeable && (child.resizeable === undefined)) {
1843
                            child.resizeable = oColumn.resizeable;
1844
                        }
1845
                        if(oColumn.sortable && (child.sortable === undefined)) {
1846
                            child.sortable = oColumn.sortable;
1847
                        }
1848
                        if(oColumn.hidden) {
1849
                            child.hidden = true;
1850
                        }
1851
                        if(oColumn.width && (child.width === undefined)) {
1852
                            child.width = oColumn.width;
1853
                        }
1854
                        if(oColumn.minWidth && (child.minWidth === undefined)) {
1855
                            child.minWidth = oColumn.minWidth;
1856
                        }
1857
                        if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) {
1858
                            child.maxAutoWidth = oColumn.maxAutoWidth;
1859
                        }
1860
                        // Backward compatibility
1861
                        if(oColumn.type && (child.type === undefined)) {
1862
                            child.type = oColumn.type;
1863
                        }
1864
                        if(oColumn.type && !oColumn.formatter) {
1865
                            YAHOO.log("The property type has been" +
1866
                            " deprecated in favor of formatter", "warn", oColumn.toString());
1867
                            oColumn.formatter = oColumn.type;
1868
                        }
1869
                        if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
1870
                            YAHOO.log("The property text has been" +
1871
                            " deprecated in favor of label", "warn", oColumn.toString());
1872
                            oColumn.label = oColumn.text;
1873
                        }
1874
                        if(oColumn.parser) {
1875
                            YAHOO.log("The property parser is no longer supported",
1876
                            "warn", this.toString());
1877
                        }
1878
                        if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
1879
                                (oColumn.sortOptions.descFunction))) {
1880
                            YAHOO.log("The properties sortOptions.ascFunction and " +
1881
                            " sortOptions.descFunction have been deprecated in favor " +
1882
                            " of sortOptions.sortFunction", "warn", oColumn.toString());
1883
                        }
1884
                    }
1885
 
1886
                    // The children themselves must also be parsed for Column instances
1887
                    if(!tree[nodeDepth+1]) {
1888
                        tree[nodeDepth+1] = [];
1889
                    }
1890
                    parseColumns(currentChildren, oColumn);
1891
                }
1892
                // This Column does not have any children
1893
                else {
1894
                    oColumn._nKeyIndex = keys.length;
1895
                    oColumn._nColspan = 1;
1896
                    keys.push(oColumn);
1897
                }
1898
 
1899
                // Add the Column to the top-down tree
1900
                tree[nodeDepth].push(oColumn);
1901
            }
1902
            nodeDepth--;
1903
        };
1904
 
1905
        // Parse out Column instances from the array of object literals
1906
        if(YAHOO.lang.isArray(aDefinitions)) {
1907
            parseColumns(aDefinitions);
1908
 
1909
            // Store the array
1910
            this._aDefinitions = aDefinitions;
1911
        }
1912
        else {
1913
            YAHOO.log("Could not initialize ColumnSet due to invalid definitions","error");
1914
            return null;
1915
        }
1916
 
1917
        var i;
1918
 
1919
        // Determine ROWSPAN value for each Column in the tree
1920
        var parseTreeForRowspan = function(tree) {
1921
            var maxRowDepth = 1;
1922
            var currentRow;
1923
            var currentColumn;
1924
 
1925
            // Calculate the max depth of descendants for this row
1926
            var countMaxRowDepth = function(row, tmpRowDepth) {
1927
                tmpRowDepth = tmpRowDepth || 1;
1928
 
1929
                for(var n=0; n<row.length; n++) {
1930
                    var col = row[n];
1931
                    // Column has children, so keep counting
1932
                    if(YAHOO.lang.isArray(col.children)) {
1933
                        tmpRowDepth++;
1934
                        countMaxRowDepth(col.children, tmpRowDepth);
1935
                        tmpRowDepth--;
1936
                    }
1937
                    // No children, is it the max depth?
1938
                    else {
1939
                        if(tmpRowDepth > maxRowDepth) {
1940
                            maxRowDepth = tmpRowDepth;
1941
                        }
1942
                    }
1943
 
1944
                }
1945
            };
1946
 
1947
            // Count max row depth for each row
1948
            for(var m=0; m<tree.length; m++) {
1949
                currentRow = tree[m];
1950
                countMaxRowDepth(currentRow);
1951
 
1952
                // Assign the right ROWSPAN values to each Column in the row
1953
                for(var p=0; p<currentRow.length; p++) {
1954
                    currentColumn = currentRow[p];
1955
                    if(!YAHOO.lang.isArray(currentColumn.children)) {
1956
                        currentColumn._nRowspan = maxRowDepth;
1957
                    }
1958
                    else {
1959
                        currentColumn._nRowspan = 1;
1960
                    }
1961
                }
1962
 
1963
                // Reset counter for next row
1964
                maxRowDepth = 1;
1965
            }
1966
        };
1967
        parseTreeForRowspan(tree);
1968
 
1969
        // Store tree index values
1970
        for(i=0; i<tree[0].length; i++) {
1971
            tree[0][i]._nTreeIndex = i;
1972
        }
1973
 
1974
        // Store header relationships in an array for HEADERS attribute
1975
        var recurseAncestorsForHeaders = function(i, oColumn) {
1976
            headers[i].push(oColumn.getSanitizedKey());
1977
            if(oColumn._oParent) {
1978
                recurseAncestorsForHeaders(i, oColumn._oParent);
1979
            }
1980
        };
1981
        for(i=0; i<keys.length; i++) {
1982
            headers[i] = [];
1983
            recurseAncestorsForHeaders(i, keys[i]);
1984
            headers[i] = headers[i].reverse();
1985
        }
1986
 
1987
        // Save to the ColumnSet instance
1988
        this.tree = tree;
1989
        this.flat = flat;
1990
        this.keys = keys;
1991
        this.headers = headers;
1992
    },
1993
 
1994
    /////////////////////////////////////////////////////////////////////////////
1995
    //
1996
    // Public methods
1997
    //
1998
    /////////////////////////////////////////////////////////////////////////////
1999
 
2000
    /**
2001
     * Returns unique name of the ColumnSet instance.
2002
     *
2003
     * @method getId
2004
     * @return {String} Unique name of the ColumnSet instance.
2005
     */
2006
 
2007
    getId : function() {
2008
        return this._sId;
2009
    },
2010
 
2011
    /**
2012
     * ColumnSet instance name, for logging.
2013
     *
2014
     * @method toString
2015
     * @return {String} Unique name of the ColumnSet instance.
2016
     */
2017
 
2018
    toString : function() {
2019
        return "ColumnSet instance " + this._sId;
2020
    },
2021
 
2022
    /**
2023
     * Public accessor to the definitions array.
2024
     *
2025
     * @method getDefinitions
2026
     * @return {Object[]} Array of object literal Column definitions.
2027
     */
2028
 
2029
    getDefinitions : function() {
2030
        var aDefinitions = this._aDefinitions;
2031
 
2032
        // Internal recursive function to define Column instances
2033
        var parseColumns = function(nodeList, oSelf) {
2034
            // Parse each node at this depth for attributes and any children
2035
            for(var j=0; j<nodeList.length; j++) {
2036
                var currentNode = nodeList[j];
2037
 
2038
                // Get the Column for each node
2039
                var oColumn = oSelf.getColumnById(currentNode.yuiColumnId);
2040
 
2041
                if(oColumn) {
2042
                    // Update the current values
2043
                    var oDefinition = oColumn.getDefinition();
2044
                    for(var name in oDefinition) {
2045
                        if(YAHOO.lang.hasOwnProperty(oDefinition, name)) {
2046
                            currentNode[name] = oDefinition[name];
2047
                        }
2048
                    }
2049
                }
2050
 
2051
                // The Column has descendants
2052
                if(YAHOO.lang.isArray(currentNode.children)) {
2053
                    // The children themselves must also be parsed for Column instances
2054
                    parseColumns(currentNode.children, oSelf);
2055
                }
2056
            }
2057
        };
2058
 
2059
        parseColumns(aDefinitions, this);
2060
        this._aDefinitions = aDefinitions;
2061
        return aDefinitions;
2062
    },
2063
 
2064
    /**
2065
     * Returns Column instance with given ID.
2066
     *
2067
     * @method getColumnById
2068
     * @param column {String} Column ID.
2069
     * @return {YAHOO.widget.Column} Column instance.
2070
     */
2071
 
2072
    getColumnById : function(column) {
2073
        if(YAHOO.lang.isString(column)) {
2074
            var allColumns = this.flat;
2075
            for(var i=allColumns.length-1; i>-1; i--) {
2076
                if(allColumns[i]._sId === column) {
2077
                    return allColumns[i];
2078
                }
2079
            }
2080
        }
2081
        return null;
2082
    },
2083
 
2084
    /**
2085
     * Returns Column instance with given key or ColumnSet key index.
2086
     *
2087
     * @method getColumn
2088
     * @param column {String | Number} Column key or ColumnSet key index.
2089
     * @return {YAHOO.widget.Column} Column instance.
2090
     */
2091
 
2092
    getColumn : function(column) {
2093
        if(YAHOO.lang.isNumber(column) && this.keys[column]) {
2094
            return this.keys[column];
2095
        }
2096
        else if(YAHOO.lang.isString(column)) {
2097
            var allColumns = this.flat;
2098
            var aColumns = [];
2099
            for(var i=0; i<allColumns.length; i++) {
2100
                if(allColumns[i].key === column) {
2101
                    aColumns.push(allColumns[i]);
2102
                }
2103
            }
2104
            if(aColumns.length === 1) {
2105
                return aColumns[0];
2106
            }
2107
            else if(aColumns.length > 1) {
2108
                return aColumns;
2109
            }
2110
        }
2111
        return null;
2112
    },
2113
 
2114
    /**
2115
     * Public accessor returns array of given Column's desendants (if any), including itself.
2116
     *
2117
     * @method getDescendants
2118
     * @parem {YAHOO.widget.Column} Column instance.
2119
     * @return {Array} Array including the Column itself and all descendants (if any).
2120
     */
2121
    getDescendants : function(oColumn) {
2122
        var oSelf = this;
2123
        var allDescendants = [];
2124
        var i;
2125
 
2126
        // Recursive function to loop thru all children
2127
        var parse = function(oParent) {
2128
            allDescendants.push(oParent);
2129
            // This Column has children
2130
            if(oParent.children) {
2131
                for(i=0; i<oParent.children.length; i++) {
2132
                    parse(oSelf.getColumn(oParent.children[i].key));
2133
                }
2134
            }
2135
        };
2136
        parse(oColumn);
2137
 
2138
        return allDescendants;
2139
    }
2140
};
2141
 
2142
/****************************************************************************/
2143
/****************************************************************************/
2144
/****************************************************************************/
2145
 
2146
/**
2147
 * The Column class defines and manages attributes of DataTable Columns
2148
 *
2149
 * @namespace YAHOO.widget
2150
 * @class Column
2151
 * @constructor
2152
 * @param oConfigs {Object} Object literal of definitions.
2153
 */
2154
YAHOO.widget.Column = function(oConfigs) {
2155
    this._sId = Dom.generateId(null, "yui-col"); // "yui-col" + YAHOO.widget.Column._nCount;
2156
 
2157
    // Object literal defines Column attributes
2158
    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
2159
        for(var sConfig in oConfigs) {
2160
            if(sConfig) {
2161
                this[sConfig] = oConfigs[sConfig];
2162
            }
2163
        }
2164
    }
2165
 
2166
    // Assign a key if not found
2167
    if(!YAHOO.lang.isValue(this.key)) {
2168
        this.key = Dom.generateId(null, "yui-dt-col"); //"yui-dt-col" + YAHOO.widget.Column._nCount;
2169
    }
2170
 
2171
    // Assign a field if not found, defaults to key
2172
    if(!YAHOO.lang.isValue(this.field)) {
2173
        this.field = this.key;
2174
    }
2175
 
2176
    // Increment counter
2177
    YAHOO.widget.Column._nCount++;
2178
 
2179
    // Backward compatibility
2180
    if(this.width && !YAHOO.lang.isNumber(this.width)) {
2181
        this.width = null;
2182
        YAHOO.log("The Column property width must be a number", "warn", this.toString());
2183
    }
2184
    if(this.editor && YAHOO.lang.isString(this.editor)) {
2185
        this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions);
2186
        YAHOO.log("The Column property editor must be an instance of YAHOO.widget.CellEditor", "warn", this.toString());
2187
    }
2188
};
2189
 
2190
/////////////////////////////////////////////////////////////////////////////
2191
//
2192
// Private member variables
2193
//
2194
/////////////////////////////////////////////////////////////////////////////
2195
 
2196
YAHOO.lang.augmentObject(YAHOO.widget.Column, {
2197
    /**
2198
     * Internal class variable to index multiple Column instances.
2199
     *
2200
     * @property Column._nCount
2201
     * @type Number
2202
     * @private
2203
     * @static
2204
     */
2205
    _nCount : 0,
2206
 
2207
    formatCheckbox : function(elCell, oRecord, oColumn, oData) {
2208
        YAHOO.log("The method YAHOO.widget.Column.formatCheckbox() has been" +
2209
        " deprecated in favor of YAHOO.widget.DataTable.formatCheckbox()", "warn",
2210
        "YAHOO.widget.Column.formatCheckbox");
2211
        YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
2212
    },
2213
 
2214
    formatCurrency : function(elCell, oRecord, oColumn, oData) {
2215
        YAHOO.log("The method YAHOO.widget.Column.formatCurrency() has been" +
2216
        " deprecated in favor of YAHOO.widget.DataTable.formatCurrency()", "warn",
2217
        "YAHOO.widget.Column.formatCurrency");
2218
        YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
2219
    },
2220
 
2221
    formatDate : function(elCell, oRecord, oColumn, oData) {
2222
        YAHOO.log("The method YAHOO.widget.Column.formatDate() has been" +
2223
        " deprecated in favor of YAHOO.widget.DataTable.formatDate()", "warn",
2224
        "YAHOO.widget.Column.formatDate");
2225
        YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
2226
    },
2227
 
2228
    formatEmail : function(elCell, oRecord, oColumn, oData) {
2229
        YAHOO.log("The method YAHOO.widget.Column.formatEmail() has been" +
2230
        " deprecated in favor of YAHOO.widget.DataTable.formatEmail()", "warn",
2231
        "YAHOO.widget.Column.formatEmail");
2232
        YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
2233
    },
2234
 
2235
    formatLink : function(elCell, oRecord, oColumn, oData) {
2236
        YAHOO.log("The method YAHOO.widget.Column.formatLink() has been" +
2237
        " deprecated in favor of YAHOO.widget.DataTable.formatLink()", "warn",
2238
        "YAHOO.widget.Column.formatLink");
2239
        YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
2240
    },
2241
 
2242
    formatNumber : function(elCell, oRecord, oColumn, oData) {
2243
        YAHOO.log("The method YAHOO.widget.Column.formatNumber() has been" +
2244
        " deprecated in favor of YAHOO.widget.DataTable.formatNumber()", "warn",
2245
        "YAHOO.widget.Column.formatNumber");
2246
        YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
2247
    },
2248
 
2249
    formatSelect : function(elCell, oRecord, oColumn, oData) {
2250
        YAHOO.log("The method YAHOO.widget.Column.formatSelect() has been" +
2251
        " deprecated in favor of YAHOO.widget.DataTable.formatDropdown()", "warn",
2252
        "YAHOO.widget.Column.formatSelect");
2253
        YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
2254
    }
2255
});
2256
 
2257
YAHOO.widget.Column.prototype = {
2258
    /**
2259
     * Unique String identifier assigned at instantiation.
2260
     *
2261
     * @property _sId
2262
     * @type String
2263
     * @private
2264
     */
2265
    _sId : null,
2266
 
2267
    /**
2268
     * Reference to Column's current position index within its ColumnSet's keys
2269
     * array, if applicable. This property only applies to non-nested and bottom-
2270
     * level child Columns.
2271
     *
2272
     * @property _nKeyIndex
2273
     * @type Number
2274
     * @private
2275
     */
2276
    _nKeyIndex : null,
2277
 
2278
    /**
2279
     * Reference to Column's current position index within its ColumnSet's tree
2280
     * array, if applicable. This property only applies to non-nested and top-
2281
     * level parent Columns.
2282
     *
2283
     * @property _nTreeIndex
2284
     * @type Number
2285
     * @private
2286
     */
2287
    _nTreeIndex : null,
2288
 
2289
    /**
2290
     * Number of table cells the Column spans.
2291
     *
2292
     * @property _nColspan
2293
     * @type Number
2294
     * @private
2295
     */
2296
    _nColspan : 1,
2297
 
2298
    /**
2299
     * Number of table rows the Column spans.
2300
     *
2301
     * @property _nRowspan
2302
     * @type Number
2303
     * @private
2304
     */
2305
    _nRowspan : 1,
2306
 
2307
    /**
2308
     * Column's parent Column instance, or null.
2309
     *
2310
     * @property _oParent
2311
     * @type YAHOO.widget.Column
2312
     * @private
2313
     */
2314
    _oParent : null,
2315
 
2316
    /**
2317
     * The DOM reference to the associated TH element.
2318
     *
2319
     * @property _elTh
2320
     * @type HTMLElement
2321
     * @private
2322
     */
2323
    _elTh : null,
2324
 
2325
    /**
2326
     * The DOM reference to the associated TH element's liner DIV element.
2327
     *
2328
     * @property _elThLiner
2329
     * @type HTMLElement
2330
     * @private
2331
     */
2332
    _elThLiner : null,
2333
 
2334
    /**
2335
     * The DOM reference to the associated TH element's label SPAN element.
2336
     *
2337
     * @property _elThLabel
2338
     * @type HTMLElement
2339
     * @private
2340
     */
2341
    _elThLabel : null,
2342
 
2343
    /**
2344
     * The DOM reference to the associated resizerelement (if any).
2345
     *
2346
     * @property _elResizer
2347
     * @type HTMLElement
2348
     * @private
2349
     */
2350
    _elResizer : null,
2351
 
2352
    /**
2353
     * Internal width tracker.
2354
     *
2355
     * @property _nWidth
2356
     * @type Number
2357
     * @private
2358
     */
2359
    _nWidth : null,
2360
 
2361
    /**
2362
     * For unreg() purposes, a reference to the Column's DragDrop instance.
2363
     *
2364
     * @property _dd
2365
     * @type YAHOO.util.DragDrop
2366
     * @private
2367
     */
2368
    _dd : null,
2369
 
2370
    /**
2371
     * For unreg() purposes, a reference to the Column resizer's DragDrop instance.
2372
     *
2373
     * @property _ddResizer
2374
     * @type YAHOO.util.DragDrop
2375
     * @private
2376
     */
2377
    _ddResizer : null,
2378
 
2379
    /////////////////////////////////////////////////////////////////////////////
2380
    //
2381
    // Public member variables
2382
    //
2383
    /////////////////////////////////////////////////////////////////////////////
2384
 
2385
    /**
2386
     * Unique name, required. If "label" property is not provided, the "key"
2387
     * value will be treated as markup and inserted into the DOM as innerHTML.
2388
     *
2389
     * @property key
2390
     * @type String|HTML
2391
     */
2392
    key : null,
2393
 
2394
    /**
2395
     * Associated database field, or null.
2396
     *
2397
     * @property field
2398
     * @type String
2399
     */
2400
    field : null,
2401
 
2402
    /**
2403
     * Value displayed as Column header in the TH element. String value is
2404
     * treated as markup and inserted into the DOM as innerHTML.
2405
     *
2406
     * @property label
2407
     * @type HTML
2408
     */
2409
    label : null,
2410
 
2411
    /**
2412
     * Column head cell ABBR for accessibility.
2413
     *
2414
     * @property abbr
2415
     * @type String
2416
     */
2417
    abbr : null,
2418
 
2419
    /**
2420
     * Array of object literals that define children (nested headers) of a Column.
2421
     *
2422
     * @property children
2423
     * @type Object[]
2424
     */
2425
    children : null,
2426
 
2427
    /**
2428
     * Column width (in pixels).
2429
     *
2430
     * @property width
2431
     * @type Number
2432
     */
2433
    width : null,
2434
 
2435
    /**
2436
     * Minimum Column width (in pixels).
2437
     *
2438
     * @property minWidth
2439
     * @type Number
2440
     * @default null
2441
     */
2442
    minWidth : null,
2443
 
2444
    /**
2445
     * When a width is not defined for a Column, maxAutoWidth defines an upper
2446
     * limit that the Column should be auto-sized to. If resizeable is enabled,
2447
     * users may still resize to a greater width. Most useful for Columns intended
2448
     * to hold long unbroken, unwrapped Strings, such as URLs, to prevent very
2449
     * wide Columns from disrupting visual readability by inducing truncation.
2450
     *
2451
     * @property maxAutoWidth
2452
     * @type Number
2453
     * @default null
2454
     */
2455
    maxAutoWidth : null,
2456
 
2457
    /**
2458
     * True if Column is in hidden state.
2459
     *
2460
     * @property hidden
2461
     * @type Boolean
2462
     * @default false
2463
     */
2464
    hidden : false,
2465
 
2466
    /**
2467
     * True if Column is in selected state.
2468
     *
2469
     * @property selected
2470
     * @type Boolean
2471
     * @default false
2472
     */
2473
    selected : false,
2474
 
2475
    /**
2476
     * Custom CSS class or array of classes to be applied to every cell in the Column.
2477
     *
2478
     * @property className
2479
     * @type String || String[]
2480
     */
2481
    className : null,
2482
 
2483
    /**
2484
     * Cell formatter function, or a shortcut pointer to a function in the
2485
     * DataTable.Formatter object. The function, called from the DataTable's
2486
     * formatCell method, renders markup into the cell liner
2487
     * element and accepts the following arguments:
2488
     * <dl>
2489
     *    <dt>elLiner</dt>
2490
     *    <dd>The element to write innerHTML to.</dd>
2491
     *    <dt>oRecord</dt>
2492
     *    <dd>The associated Record for the row.</dd>
2493
     *    <dt>oColumn</dt>
2494
     *    <dd>The Column instance for the cell.</dd>
2495
     *    <dt>oData</dt>
2496
     *    <dd>The data value for the cell.</dd>
2497
     * </dl>
2498
     *
2499
     * @property formatter
2500
     * @type String || HTMLFunction
2501
     */
2502
    formatter : null,
2503
 
2504
    /**
2505
     * Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
2506
     *
2507
     * @property currencyOptions
2508
     * @type Object
2509
     * @default null
2510
     */
2511
    currencyOptions : null,
2512
 
2513
    /**
2514
     * Config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
2515
     *
2516
     * @property dateOptions
2517
     * @type Object
2518
     * @default null
2519
     */
2520
    dateOptions : null,
2521
 
2522
    /**
2523
     * Array of dropdown values for formatter:"dropdown" cases. Can either be a
2524
     * simple array (e.g., ["Alabama","Alaska","Arizona","Arkansas"]) or a an
2525
     * array of objects (e.g., [{label:"Alabama", value:"AL"},
2526
     * {label:"Alaska", value:"AK"}, {label:"Arizona", value:"AZ"},
2527
     * {label:"Arkansas", value:"AR"}]). String values are treated as markup and
2528
     * inserted into the DOM as innerHTML.
2529
     *
2530
     * @property dropdownOptions
2531
     * @type HTML[] | Object[]
2532
     */
2533
    dropdownOptions : null,
2534
 
2535
    /**
2536
     * A CellEditor instance, otherwise Column is not editable.
2537
     *
2538
     * @property editor
2539
     * @type YAHOO.widget.CellEditor
2540
     */
2541
    editor : null,
2542
 
2543
    /**
2544
     * True if Column is resizeable, false otherwise. The Drag & Drop Utility is
2545
     * required to enable this feature. Only bottom-level and non-nested Columns are
2546
     * resizeble.
2547
     *
2548
     * @property resizeable
2549
     * @type Boolean
2550
     * @default false
2551
     */
2552
    resizeable : false,
2553
 
2554
    /**
2555
     * True if Column is sortable, false otherwise.
2556
     *
2557
     * @property sortable
2558
     * @type Boolean
2559
     * @default false
2560
     */
2561
    sortable : false,
2562
 
2563
    /**
2564
     * @property sortOptions.defaultOrder
2565
     * @deprecated Use sortOptions.defaultDir.
2566
     */
2567
    /**
2568
     * Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC.
2569
     *
2570
     * @property sortOptions.defaultDir
2571
     * @type String
2572
     * @default null
2573
     */
2574
    /**
2575
     * Custom field to sort on.
2576
     *
2577
     * @property sortOptions.field
2578
     * @type String
2579
     * @default null
2580
     */
2581
    /**
2582
     * Custom sort handler. Signature: sortFunction(a, b, desc, field) where field is the sortOptions.field value
2583
     *
2584
     * @property sortOptions.sortFunction
2585
     * @type Function
2586
     * @default null
2587
     */
2588
    sortOptions : null,
2589
 
2590
 
2591
 
2592
 
2593
 
2594
 
2595
 
2596
 
2597
 
2598
 
2599
 
2600
 
2601
 
2602
 
2603
 
2604
    /////////////////////////////////////////////////////////////////////////////
2605
    //
2606
    // Public methods
2607
    //
2608
    /////////////////////////////////////////////////////////////////////////////
2609
 
2610
    /**
2611
     * Returns unique ID string.
2612
     *
2613
     * @method getId
2614
     * @return {String} Unique ID string.
2615
     */
2616
    getId : function() {
2617
        return this._sId;
2618
    },
2619
 
2620
    /**
2621
     * Column instance name, for logging.
2622
     *
2623
     * @method toString
2624
     * @return {String} Column's unique name.
2625
     */
2626
    toString : function() {
2627
        return "Column instance " + this._sId;
2628
    },
2629
 
2630
    /**
2631
     * Returns object literal definition.
2632
     *
2633
     * @method getDefinition
2634
     * @return {Object} Object literal definition.
2635
     */
2636
    getDefinition : function() {
2637
        var oDefinition = {};
2638
 
2639
        // Update the definition
2640
        oDefinition.abbr = this.abbr;
2641
        oDefinition.className = this.className;
2642
        oDefinition.editor = this.editor;
2643
        oDefinition.editorOptions = this.editorOptions; //TODO: deprecated
2644
        oDefinition.field = this.field;
2645
        oDefinition.formatter = this.formatter;
2646
        oDefinition.hidden = this.hidden;
2647
        oDefinition.key = this.key;
2648
        oDefinition.label = this.label;
2649
        oDefinition.minWidth = this.minWidth;
2650
        oDefinition.maxAutoWidth = this.maxAutoWidth;
2651
        oDefinition.resizeable = this.resizeable;
2652
        oDefinition.selected = this.selected;
2653
        oDefinition.sortable = this.sortable;
2654
        oDefinition.sortOptions = this.sortOptions;
2655
        oDefinition.width = this.width;
2656
 
2657
        // Bug 2529147
2658
        oDefinition._calculatedWidth = this._calculatedWidth;
2659
 
2660
        return oDefinition;
2661
    },
2662
 
2663
    /**
2664
     * Returns unique Column key.
2665
     *
2666
     * @method getKey
2667
     * @return {String} Column key.
2668
     */
2669
    getKey : function() {
2670
        return this.key;
2671
    },
2672
 
2673
    /**
2674
     * Returns field.
2675
     *
2676
     * @method getField
2677
     * @return {String} Column field.
2678
     */
2679
    getField : function() {
2680
        return this.field;
2681
    },
2682
 
2683
    /**
2684
     * Returns Column key which has been sanitized for DOM (class and ID) usage
2685
     * starts with letter, contains only letters, numbers, hyphen, or period.
2686
     *
2687
     * @method getSanitizedKey
2688
     * @return {String} Sanitized Column key.
2689
     */
2690
    getSanitizedKey : function() {
2691
        return this.getKey().replace(/[^\w\-]/g,"");
2692
    },
2693
 
2694
    /**
2695
     * Public accessor returns Column's current position index within its
2696
     * ColumnSet's keys array, if applicable. Only non-nested and bottom-level
2697
     * child Columns will return a value.
2698
     *
2699
     * @method getKeyIndex
2700
     * @return {Number} Position index, or null.
2701
     */
2702
    getKeyIndex : function() {
2703
        return this._nKeyIndex;
2704
    },
2705
 
2706
    /**
2707
     * Public accessor returns Column's current position index within its
2708
     * ColumnSet's tree array, if applicable. Only non-nested and top-level parent
2709
     * Columns will return a value;
2710
     *
2711
     * @method getTreeIndex
2712
     * @return {Number} Position index, or null.
2713
     */
2714
    getTreeIndex : function() {
2715
        return this._nTreeIndex;
2716
    },
2717
 
2718
    /**
2719
     * Public accessor returns Column's parent instance if any, or null otherwise.
2720
     *
2721
     * @method getParent
2722
     * @return {YAHOO.widget.Column} Column's parent instance.
2723
     */
2724
    getParent : function() {
2725
        return this._oParent;
2726
    },
2727
 
2728
    /**
2729
     * Public accessor returns Column's calculated COLSPAN value.
2730
     *
2731
     * @method getColspan
2732
     * @return {Number} Column's COLSPAN value.
2733
     */
2734
    getColspan : function() {
2735
        return this._nColspan;
2736
    },
2737
    // Backward compatibility
2738
    getColSpan : function() {
2739
        YAHOO.log("The method getColSpan() has been" +
2740
        " deprecated in favor of getColspan()", "warn", this.toString());
2741
        return this.getColspan();
2742
    },
2743
 
2744
    /**
2745
     * Public accessor returns Column's calculated ROWSPAN value.
2746
     *
2747
     * @method getRowspan
2748
     * @return {Number} Column's ROWSPAN value.
2749
     */
2750
    getRowspan : function() {
2751
        return this._nRowspan;
2752
    },
2753
 
2754
    /**
2755
     * Returns DOM reference to the key TH element.
2756
     *
2757
     * @method getThEl
2758
     * @return {HTMLElement} TH element.
2759
     */
2760
    getThEl : function() {
2761
        return this._elTh;
2762
    },
2763
 
2764
    /**
2765
     * Returns DOM reference to the TH's liner DIV element. Introduced since
2766
     * resizeable Columns may have an extra resizer liner, making the DIV liner
2767
     * not reliably the TH element's first child.
2768
     *
2769
     * @method getThLInerEl
2770
     * @return {HTMLElement} TH element.
2771
     */
2772
    getThLinerEl : function() {
2773
        return this._elThLiner;
2774
    },
2775
 
2776
    /**
2777
     * Returns DOM reference to the resizer element, or null.
2778
     *
2779
     * @method getResizerEl
2780
     * @return {HTMLElement} DIV element.
2781
     */
2782
    getResizerEl : function() {
2783
        return this._elResizer;
2784
    },
2785
 
2786
    // Backward compatibility
2787
    /**
2788
     * @method getColEl
2789
     * @deprecated Use getThEl
2790
     */
2791
    getColEl : function() {
2792
        YAHOO.log("The method getColEl() has been" +
2793
        " deprecated in favor of getThEl()", "warn",
2794
        this.toString());
2795
        return this.getThEl();
2796
    },
2797
    getIndex : function() {
2798
        YAHOO.log("The method getIndex() has been" +
2799
        " deprecated in favor of getKeyIndex()", "warn",
2800
        this.toString());
2801
        return this.getKeyIndex();
2802
    },
2803
    format : function() {
2804
        YAHOO.log("The method format() has been deprecated in favor of the " +
2805
        "DataTable method formatCell()", "error", this.toString());
2806
    }
2807
};
2808
 
2809
/****************************************************************************/
2810
/****************************************************************************/
2811
/****************************************************************************/
2812
 
2813
/**
2814
 * Sort static utility to support Column sorting.
2815
 *
2816
 * @namespace YAHOO.util
2817
 * @class Sort
2818
 * @static
2819
 */
2820
YAHOO.util.Sort = {
2821
    /////////////////////////////////////////////////////////////////////////////
2822
    //
2823
    // Public methods
2824
    //
2825
    /////////////////////////////////////////////////////////////////////////////
2826
 
2827
    /**
2828
     * Comparator function for simple case-insensitive string sorting.
2829
     *
2830
     * @method compare
2831
     * @param a {Object} First sort argument.
2832
     * @param b {Object} Second sort argument.
2833
     * @param desc {Boolean} True if sort direction is descending, false if
2834
     * sort direction is ascending.
2835
     * @return {Boolean} Return -1 when a < b. Return 0 when a = b.
2836
     * Return 1 when a > b.
2837
     */
2838
    compare: function(a, b, desc) {
2839
        if((a === null) || (typeof a == "undefined")) {
2840
            if((b === null) || (typeof b == "undefined")) {
2841
                return 0;
2842
            }
2843
            else {
2844
                return 1;
2845
            }
2846
        }
2847
        else if((b === null) || (typeof b == "undefined")) {
2848
            return -1;
2849
        }
2850
 
2851
        if(a.constructor == String) {
2852
            a = a.toLowerCase();
2853
        }
2854
        if(b.constructor == String) {
2855
            b = b.toLowerCase();
2856
        }
2857
        if(a < b) {
2858
            return (desc) ? 1 : -1;
2859
        }
2860
        else if (a > b) {
2861
            return (desc) ? -1 : 1;
2862
        }
2863
        else {
2864
            return 0;
2865
        }
2866
    }
2867
};
2868
 
2869
/****************************************************************************/
2870
/****************************************************************************/
2871
/****************************************************************************/
2872
 
2873
/**
2874
 * ColumnDD subclasses DragDrop to support rearrangeable Columns.
2875
 *
2876
 * @namespace YAHOO.util
2877
 * @class ColumnDD
2878
 * @extends YAHOO.util.DDProxy
2879
 * @constructor
2880
 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
2881
 * @param oColumn {YAHOO.widget.Column} Column instance.
2882
 * @param elTh {HTMLElement} TH element reference.
2883
 * @param elTarget {HTMLElement} Drag target element.
2884
 */
2885
YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) {
2886
    if(oDataTable && oColumn && elTh && elTarget) {
2887
        this.datatable = oDataTable;
2888
        this.table = oDataTable.getTableEl();
2889
        this.column = oColumn;
2890
        this.headCell = elTh;
2891
        this.pointer = elTarget;
2892
        this.newIndex = null;
2893
        this.init(elTh);
2894
        this.initFrame(); // Needed for DDProxy
2895
        this.invalidHandleTypes = {};
2896
 
2897
        // Set top/bottom padding to account for children of nested columns
2898
        this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0);
2899
 
2900
        YAHOO.util.Event.on(window, 'resize', function() {
2901
            this.initConstraints();
2902
        }, this, true);
2903
    }
2904
    else {
2905
        YAHOO.log("Column dragdrop could not be created","warn",oDataTable.toString());
2906
    }
2907
};
2908
 
2909
if(YAHOO.util.DDProxy) {
2910
    YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, {
2911
        initConstraints: function() {
2912
            //Get the top, right, bottom and left positions
2913
            var region = YAHOO.util.Dom.getRegion(this.table),
2914
                //Get the element we are working on
2915
                el = this.getEl(),
2916
                //Get the xy position of it
2917
                xy = YAHOO.util.Dom.getXY(el),
2918
                //Get the width and height
2919
                width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10),
2920
                height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10),
2921
                //Set left to x minus left
2922
                left = ((xy[0] - region.left) + 15), //Buffer of 15px
2923
                //Set right to right minus x minus width
2924
                right = ((region.right - xy[0] - width) + 15);
2925
 
2926
            //Set the constraints based on the above calculations
2927
            this.setXConstraint(left, right);
2928
            this.setYConstraint(10, 10);
2929
        },
2930
        _resizeProxy: function() {
2931
            YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this, arguments);
2932
            var dragEl = this.getDragEl(),
2933
                el = this.getEl();
2934
 
2935
            YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px');
2936
            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block');
2937
            var xy = YAHOO.util.Dom.getXY(el);
2938
            YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
2939
 
2940
            YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px");
2941
            YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
2942
            YAHOO.util.Dom.setXY(this.dragEl, xy);
2943
        },
2944
        onMouseDown: function() {
2945
                this.initConstraints();
2946
                this.resetConstraints();
2947
        },
2948
        clickValidator: function(e) {
2949
            if(!this.column.hidden) {
2950
                var target = YAHOO.util.Event.getTarget(e);
2951
                return ( this.isValidHandleChild(target) &&
2952
                            (this.id == this.handleElId ||
2953
                                this.DDM.handleWasClicked(target, this.id)) );
2954
            }
2955
        },
2956
        onDragOver: function(ev, id) {
2957
            // Validate target as a Column
2958
            var target = this.datatable.getColumn(id);
2959
            if(target) {
2960
                // Validate target as a top-level parent
2961
                var targetIndex = target.getTreeIndex();
2962
                while((targetIndex === null) && target.getParent()) {
2963
                    target = target.getParent();
2964
                    targetIndex = target.getTreeIndex();
2965
                }
2966
                if(targetIndex !== null) {
2967
                    // Are we placing to left or right of target?
2968
                    var elTarget = target.getThEl();
2969
                    var newIndex = targetIndex;
2970
                    var mouseX = YAHOO.util.Event.getPageX(ev),
2971
                        targetX = YAHOO.util.Dom.getX(elTarget),
2972
                        midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2),
2973
                        currentIndex =  this.column.getTreeIndex();
2974
 
2975
                    if (mouseX < midX) {
2976
                       YAHOO.util.Dom.setX(this.pointer, targetX);
2977
                    } else {
2978
                        var targetWidth = parseInt(elTarget.offsetWidth, 10);
2979
                        YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth));
2980
                        newIndex++;
2981
                    }
2982
                    if (targetIndex > currentIndex) {
2983
                        newIndex--;
2984
                    }
2985
                    if(newIndex < 0) {
2986
                        newIndex = 0;
2987
                    }
2988
                    else if(newIndex > this.datatable.getColumnSet().tree[0].length) {
2989
                        newIndex = this.datatable.getColumnSet().tree[0].length;
2990
                    }
2991
                    this.newIndex = newIndex;
2992
                }
2993
            }
2994
        },
2995
        onDragDrop: function() {
2996
            this.datatable.reorderColumn(this.column, this.newIndex);
2997
        },
2998
        endDrag: function() {
2999
            this.newIndex = null;
3000
            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none');
3001
        }
3002
    });
3003
}
3004
 
3005
/****************************************************************************/
3006
/****************************************************************************/
3007
/****************************************************************************/
3008
 
3009
/**
3010
 * ColumnResizer subclasses DragDrop to support resizeable Columns.
3011
 *
3012
 * @namespace YAHOO.util
3013
 * @class ColumnResizer
3014
 * @extends YAHOO.util.DDProxy
3015
 * @constructor
3016
 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
3017
 * @param oColumn {YAHOO.widget.Column} Column instance.
3018
 * @param elTh {HTMLElement} TH element reference.
3019
 * @param sHandleElId {String} DOM ID of the handle element that causes the resize.
3020
 * @param elProxy {HTMLElement} Resizer proxy element.
3021
 */
3022
YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) {
3023
    if(oDataTable && oColumn && elTh && sHandleId) {
3024
        this.datatable = oDataTable;
3025
        this.column = oColumn;
3026
        this.headCell = elTh;
3027
        this.headCellLiner = oColumn.getThLinerEl();
3028
        this.resizerLiner = elTh.firstChild;
3029
        this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id});
3030
        this.initFrame(); // Needed for proxy
3031
        this.resetResizerEl(); // Needed when rowspan > 0
3032
 
3033
        // Set right padding for bug 1858462
3034
        this.setPadding(0, 1, 0, 0);
3035
    }
3036
    else {
3037
        YAHOO.log("Column resizer could not be created","warn",oDataTable.toString());
3038
    }
3039
};
3040
 
3041
if(YAHOO.util.DD) {
3042
    YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, {
3043
        /////////////////////////////////////////////////////////////////////////////
3044
        //
3045
        // Public methods
3046
        //
3047
        /////////////////////////////////////////////////////////////////////////////
3048
        /**
3049
         * Resets resizer element.
3050
         *
3051
         * @method resetResizerEl
3052
         */
3053
        resetResizerEl : function() {
3054
            var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style;
3055
            resizerStyle.left = "auto";
3056
            resizerStyle.right = 0;
3057
            resizerStyle.top = "auto";
3058
            resizerStyle.bottom = 0;
3059
            resizerStyle.height = this.headCell.offsetHeight+"px";
3060
        },
3061
 
3062
        /////////////////////////////////////////////////////////////////////////////
3063
        //
3064
        // Public DOM event handlers
3065
        //
3066
        /////////////////////////////////////////////////////////////////////////////
3067
 
3068
        /**
3069
         * Handles mouseup events on the Column resizer.
3070
         *
3071
         * @method onMouseUp
3072
         * @param e {string} The mouseup event
3073
         */
3074
        onMouseUp : function(e) {
3075
            // Reset height of all resizer els in case TH's have changed height
3076
            var allKeys = this.datatable.getColumnSet().keys,
3077
                col;
3078
            for(var i=0, len=allKeys.length; i<len; i++) {
3079
                col = allKeys[i];
3080
                if(col._ddResizer) {
3081
                    col._ddResizer.resetResizerEl();
3082
                }
3083
            }
3084
            this.resetResizerEl();
3085
 
3086
            var el = this.headCellLiner;
3087
            var newWidth = el.offsetWidth -
3088
                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) -
3089
                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0);
3090
 
3091
            this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth});
3092
        },
3093
 
3094
        /**
3095
         * Handles mousedown events on the Column resizer.
3096
         *
3097
         * @method onMouseDown
3098
         * @param e {string} The mousedown event
3099
         */
3100
        onMouseDown : function(e) {
3101
            this.startWidth = this.headCellLiner.offsetWidth;
3102
            this.startX = YAHOO.util.Event.getXY(e)[0];
3103
            this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) +
3104
                    (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
3105
        },
3106
 
3107
        /**
3108
         * Custom clickValidator to ensure Column is not in hidden state.
3109
         *
3110
         * @method clickValidator
3111
         * @param {Event} e
3112
         * @private
3113
         */
3114
        clickValidator : function(e) {
3115
            if(!this.column.hidden) {
3116
                var target = YAHOO.util.Event.getTarget(e);
3117
                return ( this.isValidHandleChild(target) &&
3118
                            (this.id == this.handleElId ||
3119
                                this.DDM.handleWasClicked(target, this.id)) );
3120
            }
3121
        },
3122
 
3123
        /**
3124
         * Handles start drag on the Column resizer.
3125
         *
3126
         * @method startDrag
3127
         * @param e {string} The drag event
3128
         */
3129
        startDrag : function() {
3130
            // Shrinks height of all resizer els to not hold open TH els
3131
            var allKeys = this.datatable.getColumnSet().keys,
3132
                thisKey = this.column.getKeyIndex(),
3133
                col;
3134
            for(var i=0, len=allKeys.length; i<len; i++) {
3135
                col = allKeys[i];
3136
                if(col._ddResizer) {
3137
                    YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em";
3138
                }
3139
            }
3140
        },
3141
 
3142
        /**
3143
         * Handles drag events on the Column resizer.
3144
         *
3145
         * @method onDrag
3146
         * @param e {string} The drag event
3147
         */
3148
        onDrag : function(e) {
3149
            var newX = YAHOO.util.Event.getXY(e)[0];
3150
            if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) {
3151
                var offsetX = newX - this.startX;
3152
                var newWidth = this.startWidth + offsetX - this.nLinerPadding;
3153
                if(newWidth > 0) {
3154
                    this.datatable.setColumnWidth(this.column, newWidth);
3155
                }
3156
            }
3157
        }
3158
    });
3159
}
3160
 
3161
/////////////////////////////////////////////////////////////////////////////
3162
//
3163
// Deprecated
3164
//
3165
/////////////////////////////////////////////////////////////////////////////
3166
 
3167
/**
3168
 * @property editorOptions
3169
 * @deprecated Pass configs directly to CellEditor constructor.
3170
 */
3171
 
3172
 
3173
(function () {
3174
 
3175
var lang   = YAHOO.lang,
3176
    util   = YAHOO.util,
3177
    widget = YAHOO.widget,
3178
 
3179
    Dom    = util.Dom,
3180
    Ev     = util.Event,
3181
    DT     = widget.DataTable;
3182
 
3183
/****************************************************************************/
3184
/****************************************************************************/
3185
/****************************************************************************/
3186
 
3187
/**
3188
 * A RecordSet defines and manages a set of Records.
3189
 *
3190
 * @namespace YAHOO.widget
3191
 * @class RecordSet
3192
 * @param data {Object || Object[]} An object literal or an array of data.
3193
 * @constructor
3194
 */
3195
YAHOO.widget.RecordSet = function(data) {
3196
    this._init(data);
3197
};
3198
 
3199
var RS = widget.RecordSet;
3200
 
3201
/**
3202
 * Internal class variable to name multiple Recordset instances.
3203
 *
3204
 * @property RecordSet._nCount
3205
 * @type Number
3206
 * @private
3207
 * @static
3208
 */
3209
RS._nCount = 0;
3210
 
3211
RS.prototype = {
3212
 
3213
    /////////////////////////////////////////////////////////////////////////////
3214
    //
3215
    // Private member variables
3216
    //
3217
    /////////////////////////////////////////////////////////////////////////////
3218
    /**
3219
     * Unique String identifier assigned at instantiation.
3220
     *
3221
     * @property _sId
3222
     * @type String
3223
     * @private
3224
     */
3225
    _sId : null,
3226
 
3227
    /**
3228
     * Internal counter of how many Records are in the RecordSet.
3229
     *
3230
     * @property _length
3231
     * @type Number
3232
     * @private
3233
     * @deprecated No longer used
3234
     */
3235
    //_length : null,
3236
 
3237
    /////////////////////////////////////////////////////////////////////////////
3238
    //
3239
    // Private methods
3240
    //
3241
    /////////////////////////////////////////////////////////////////////////////
3242
 
3243
    /**
3244
     * Initializer.
3245
     *
3246
     * @method _init
3247
     * @param data {Object || Object[]} An object literal or an array of data.
3248
     * @private
3249
     */
3250
    _init : function(data) {
3251
        // Internal variables
3252
        this._sId = Dom.generateId(null, "yui-rs");// "yui-rs" + widget.RecordSet._nCount;
3253
        widget.RecordSet._nCount++;
3254
        this._records = [];
3255
        //this._length = 0;
3256
 
3257
        this._initEvents();
3258
 
3259
        if(data) {
3260
            if(lang.isArray(data)) {
3261
                this.addRecords(data);
3262
            }
3263
            else if(lang.isObject(data)) {
3264
                this.addRecord(data);
3265
            }
3266
        }
3267
 
3268
        YAHOO.log("RecordSet initialized", "info", this.toString());
3269
    },
3270
 
3271
    /**
3272
     * Initializes custom events.
3273
     *
3274
     * @method _initEvents
3275
     * @private
3276
     */
3277
    _initEvents : function() {
3278
        this.createEvent("recordAddEvent");
3279
        this.createEvent("recordsAddEvent");
3280
        this.createEvent("recordSetEvent");
3281
        this.createEvent("recordsSetEvent");
3282
        this.createEvent("recordUpdateEvent");
3283
        this.createEvent("recordDeleteEvent");
3284
        this.createEvent("recordsDeleteEvent");
3285
        this.createEvent("resetEvent");
3286
        this.createEvent("recordValueUpdateEvent");
3287
    },
3288
 
3289
    /**
3290
     * Adds one Record to the RecordSet at the given index. If index is null,
3291
     * then adds the Record to the end of the RecordSet.
3292
     *
3293
     * @method _addRecord
3294
     * @param oData {Object} An object literal of data.
3295
     * @param index {Number} (optional) Position index.
3296
     * @return {YAHOO.widget.Record} A Record instance.
3297
     * @private
3298
     */
3299
    _addRecord : function(oData, index) {
3300
        var oRecord = new YAHOO.widget.Record(oData);
3301
 
3302
        if(YAHOO.lang.isNumber(index) && (index > -1)) {
3303
            this._records.splice(index,0,oRecord);
3304
        }
3305
        else {
3306
            //index = this.getLength();
3307
            //this._records[index] = oRecord;
3308
            this._records[this._records.length] = oRecord;
3309
        }
3310
        //this._length++;
3311
        return oRecord;
3312
    },
3313
 
3314
    /**
3315
     * Sets/replaces one Record to the RecordSet at the given index.  Existing
3316
     * Records with higher indexes are not shifted.  If no index specified, the
3317
     * Record is added to the end of the RecordSet.
3318
     *
3319
     * @method _setRecord
3320
     * @param oData {Object} An object literal of data.
3321
     * @param index {Number} (optional) Position index.
3322
     * @return {YAHOO.widget.Record} A Record instance.
3323
     * @private
3324
     */
3325
    _setRecord : function(oData, index) {
3326
        if (!lang.isNumber(index) || index < 0) {
3327
            index = this._records.length;
3328
        }
3329
        return (this._records[index] = new widget.Record(oData));
3330
        /*
3331
        if(lang.isNumber(index) && (index > -1)) {
3332
            this._records[index] = oRecord;
3333
            if((index+1) > this.getLength()) {
3334
                this._length = index+1;
3335
            }
3336
        }
3337
        else {
3338
            this._records[this.getLength()] = oRecord;
3339
            this._length++;
3340
        }
3341
        return oRecord;
3342
        */
3343
    },
3344
 
3345
    /**
3346
     * Deletes Records from the RecordSet at the given index. If range is null,
3347
     * then only one Record is deleted.
3348
     *
3349
     * @method _deleteRecord
3350
     * @param index {Number} Position index.
3351
     * @param range {Number} (optional) How many Records to delete
3352
     * @private
3353
     */
3354
    _deleteRecord : function(index, range) {
3355
        if(!lang.isNumber(range) || (range < 0)) {
3356
            range = 1;
3357
        }
3358
        this._records.splice(index, range);
3359
        //this._length = this._length - range;
3360
    },
3361
 
3362
    /////////////////////////////////////////////////////////////////////////////
3363
    //
3364
    // Public methods
3365
    //
3366
    /////////////////////////////////////////////////////////////////////////////
3367
 
3368
    /**
3369
     * Returns unique name of the RecordSet instance.
3370
     *
3371
     * @method getId
3372
     * @return {String} Unique name of the RecordSet instance.
3373
     */
3374
    getId : function() {
3375
        return this._sId;
3376
    },
3377
 
3378
    /**
3379
     * Public accessor to the unique name of the RecordSet instance.
3380
     *
3381
     * @method toString
3382
     * @return {String} Unique name of the RecordSet instance.
3383
     */
3384
    toString : function() {
3385
        return "RecordSet instance " + this._sId;
3386
    },
3387
 
3388
    /**
3389
     * Returns the number of Records held in the RecordSet.
3390
     *
3391
     * @method getLength
3392
     * @return {Number} Number of records in the RecordSet.
3393
     */
3394
    getLength : function() {
3395
            //return this._length;
3396
            return this._records.length;
3397
    },
3398
 
3399
    /**
3400
     * Returns Record by ID or RecordSet position index.
3401
     *
3402
     * @method getRecord
3403
     * @param record {YAHOO.widget.Record | Number | String} Record instance,
3404
     * RecordSet position index, or Record ID.
3405
     * @return {YAHOO.widget.Record} Record object.
3406
     */
3407
    getRecord : function(record) {
3408
        var i;
3409
        if(record instanceof widget.Record) {
3410
            for(i=0; i<this._records.length; i++) {
3411
                if(this._records[i] && (this._records[i]._sId === record._sId)) {
3412
                    return record;
3413
                }
3414
            }
3415
        }
3416
        else if(lang.isNumber(record)) {
3417
            if((record > -1) && (record < this.getLength())) {
3418
                return this._records[record];
3419
            }
3420
        }
3421
        else if(lang.isString(record)) {
3422
            for(i=0; i<this._records.length; i++) {
3423
                if(this._records[i] && (this._records[i]._sId === record)) {
3424
                    return this._records[i];
3425
                }
3426
            }
3427
        }
3428
        // Not a valid Record for this RecordSet
3429
        return null;
3430
 
3431
    },
3432
 
3433
    /**
3434
     * Returns an array of Records from the RecordSet.
3435
     *
3436
     * @method getRecords
3437
     * @param index {Number} (optional) Recordset position index of which Record to
3438
     * start at.
3439
     * @param range {Number} (optional) Number of Records to get.
3440
     * @return {YAHOO.widget.Record[]} Array of Records starting at given index and
3441
     * length equal to given range. If index is not given, all Records are returned.
3442
     */
3443
    getRecords : function(index, range) {
3444
        if(!lang.isNumber(index)) {
3445
            return this._records;
3446
        }
3447
        if(!lang.isNumber(range)) {
3448
            return this._records.slice(index);
3449
        }
3450
        return this._records.slice(index, index+range);
3451
    },
3452
 
3453
    /**
3454
     * Returns a boolean indicating whether Records exist in the RecordSet at the
3455
     * specified index range.  Returns true if and only if a Record exists at each
3456
     * index in the range.
3457
     * @method hasRecords
3458
     * @param index
3459
     * @param range
3460
     * @return {Boolean} true if all indices are populated in the RecordSet
3461
     */
3462
    hasRecords : function (index, range) {
3463
        var recs = this.getRecords(index,range);
3464
        for (var i = 0; i < range; ++i) {
3465
            if (typeof recs[i] === 'undefined') {
3466
                return false;
3467
            }
3468
        }
3469
        return true;
3470
    },
3471
 
3472
    /**
3473
     * Returns current position index for the given Record.
3474
     *
3475
     * @method getRecordIndex
3476
     * @param oRecord {YAHOO.widget.Record} Record instance.
3477
     * @return {Number} Record's RecordSet position index.
3478
     */
3479
 
3480
    getRecordIndex : function(oRecord) {
3481
        if(oRecord) {
3482
            for(var i=this._records.length-1; i>-1; i--) {
3483
                if(this._records[i] && oRecord.getId() === this._records[i].getId()) {
3484
                    return i;
3485
                }
3486
            }
3487
        }
3488
        return null;
3489
 
3490
    },
3491
 
3492
    /**
3493
     * Adds one Record to the RecordSet at the given index. If index is null,
3494
     * then adds the Record to the end of the RecordSet.
3495
     *
3496
     * @method addRecord
3497
     * @param oData {Object} An object literal of data.
3498
     * @param index {Number} (optional) Position index.
3499
     * @return {YAHOO.widget.Record} A Record instance.
3500
     */
3501
    addRecord : function(oData, index) {
3502
        if(lang.isObject(oData)) {
3503
            var oRecord = this._addRecord(oData, index);
3504
            this.fireEvent("recordAddEvent",{record:oRecord,data:oData});
3505
            YAHOO.log("Added Record at index " + index +
3506
                    " with data " + lang.dump(oData), "info", this.toString());
3507
            return oRecord;
3508
        }
3509
        else {
3510
            YAHOO.log("Could not add Record with data" +
3511
                    lang.dump(oData), "info", this.toString());
3512
            return null;
3513
        }
3514
    },
3515
 
3516
    /**
3517
     * Adds multiple Records at once to the RecordSet at the given index with the
3518
     * given object literal data. If index is null, then the new Records are
3519
     * added to the end of the RecordSet.
3520
     *
3521
     * @method addRecords
3522
     * @param aData {Object[]} An object literal data or an array of data object literals.
3523
     * @param index {Number} (optional) Position index.
3524
     * @return {YAHOO.widget.Record[]} An array of Record instances.
3525
     */
3526
    addRecords : function(aData, index) {
3527
        if(lang.isArray(aData)) {
3528
            var newRecords = [],
3529
                idx,i,len;
3530
 
3531
            index = lang.isNumber(index) ? index : this._records.length;
3532
            idx = index;
3533
 
3534
            // Can't go backwards bc we need to preserve order
3535
            for(i=0,len=aData.length; i<len; ++i) {
3536
                if(lang.isObject(aData[i])) {
3537
                    var record = this._addRecord(aData[i], idx++);
3538
                    newRecords.push(record);
3539
                }
3540
           }
3541
            this.fireEvent("recordsAddEvent",{records:newRecords,data:aData});
3542
            YAHOO.log("Added " + newRecords.length + " Record(s) at index " + index +
3543
                    " with data " + lang.dump(aData), "info", this.toString());
3544
           return newRecords;
3545
        }
3546
        else if(lang.isObject(aData)) {
3547
            var oRecord = this._addRecord(aData);
3548
            this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
3549
            YAHOO.log("Added 1 Record at index " + index +
3550
                    " with data " + lang.dump(aData), "info", this.toString());
3551
            return oRecord;
3552
        }
3553
        else {
3554
            YAHOO.log("Could not add Records with data " +
3555
                    lang.dump(aData), "info", this.toString());
3556
            return null;
3557
        }
3558
    },
3559
 
3560
    /**
3561
     * Sets or replaces one Record to the RecordSet at the given index. Unlike
3562
     * addRecord, an existing Record at that index is not shifted to preserve it.
3563
     * If no index is specified, it adds the Record to the end of the RecordSet.
3564
     *
3565
     * @method setRecord
3566
     * @param oData {Object} An object literal of data.
3567
     * @param index {Number} (optional) Position index.
3568
     * @return {YAHOO.widget.Record} A Record instance.
3569
     */
3570
    setRecord : function(oData, index) {
3571
        if(lang.isObject(oData)) {
3572
            var oRecord = this._setRecord(oData, index);
3573
            this.fireEvent("recordSetEvent",{record:oRecord,data:oData});
3574
            YAHOO.log("Set Record at index " + index +
3575
                    " with data " + lang.dump(oData), "info", this.toString());
3576
            return oRecord;
3577
        }
3578
        else {
3579
            YAHOO.log("Could not set Record with data" +
3580
                    lang.dump(oData), "info", this.toString());
3581
            return null;
3582
        }
3583
    },
3584
 
3585
    /**
3586
     * Sets or replaces multiple Records at once to the RecordSet with the given
3587
     * data, starting at the given index. If index is not specified, then the new
3588
     * Records are added to the end of the RecordSet.
3589
     *
3590
     * @method setRecords
3591
     * @param aData {Object[]} An array of object literal data.
3592
     * @param index {Number} (optional) Position index.
3593
     * @return {YAHOO.widget.Record[]} An array of Record instances.
3594
     */
3595
    setRecords : function(aData, index) {
3596
        var Rec   = widget.Record,
3597
            a     = lang.isArray(aData) ? aData : [aData],
3598
            added = [],
3599
            i = 0, l = a.length, j = 0;
3600
 
3601
        index = parseInt(index,10)|0;
3602
 
3603
        for(; i < l; ++i) {
3604
            if (typeof a[i] === 'object' && a[i]) {
3605
                added[j++] = this._records[index + i] = new Rec(a[i]);
3606
            }
3607
        }
3608
 
3609
        this.fireEvent("recordsSetEvent",{records:added,data:aData});
3610
        // Backward compatibility for bug 1918245
3611
        this.fireEvent("recordsSet",{records:added,data:aData});
3612
        YAHOO.log("Set "+j+" Record(s) at index "+index, "info",
3613
                  this.toString());
3614
 
3615
        if (a.length && !added.length) {
3616
            YAHOO.log("Could not set Records with data " +
3617
                    lang.dump(aData), "info", this.toString());
3618
        }
3619
 
3620
        return added;
3621
    },
3622
 
3623
    /**
3624
     * Updates given Record with given data.
3625
     *
3626
     * @method updateRecord
3627
     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
3628
     * a RecordSet position index, or a Record ID.
3629
     * @param oData {Object} Object literal of new data.
3630
     * @return {YAHOO.widget.Record} Updated Record, or null.
3631
     */
3632
    updateRecord : function(record, oData) {
3633
        var oRecord = this.getRecord(record);
3634
        if(oRecord && lang.isObject(oData)) {
3635
            // Copy data from the Record for the event that gets fired later
3636
            var oldData = {};
3637
            for(var key in oRecord._oData) {
3638
                if(lang.hasOwnProperty(oRecord._oData, key)) {
3639
                    oldData[key] = oRecord._oData[key];
3640
                }
3641
            }
3642
            oRecord._oData = oData;
3643
            this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData});
3644
            YAHOO.log("Record at index " + this.getRecordIndex(oRecord) +
3645
                    " updated with data " + lang.dump(oData), "info", this.toString());
3646
            return oRecord;
3647
        }
3648
        else {
3649
            YAHOO.log("Could not update Record " + record, "error", this.toString());
3650
            return null;
3651
        }
3652
    },
3653
 
3654
    /**
3655
     * @method updateKey
3656
     * @deprecated Use updateRecordValue
3657
     */
3658
    updateKey : function(record, sKey, oData) {
3659
        this.updateRecordValue(record, sKey, oData);
3660
    },
3661
    /**
3662
     * Sets given Record at given key to given data.
3663
     *
3664
     * @method updateRecordValue
3665
     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
3666
     * a RecordSet position index, or a Record ID.
3667
     * @param sKey {String} Key name.
3668
     * @param oData {Object} New data.
3669
     */
3670
    updateRecordValue : function(record, sKey, oData) {
3671
        var oRecord = this.getRecord(record);
3672
        if(oRecord) {
3673
            var oldData = null;
3674
            var keyValue = oRecord._oData[sKey];
3675
            // Copy data from the Record for the event that gets fired later
3676
            if(keyValue && lang.isObject(keyValue)) {
3677
                oldData = {};
3678
                for(var key in keyValue)  {
3679
                    if(lang.hasOwnProperty(keyValue, key)) {
3680
                        oldData[key] = keyValue[key];
3681
                    }
3682
                }
3683
            }
3684
            // Copy by value
3685
            else {
3686
                oldData = keyValue;
3687
            }
3688
 
3689
            oRecord._oData[sKey] = oData;
3690
            this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
3691
            this.fireEvent("recordValueUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
3692
            YAHOO.log("Key \"" + sKey +
3693
                    "\" for Record at index " + this.getRecordIndex(oRecord) +
3694
                    " updated to \"" + lang.dump(oData) + "\"", "info", this.toString());
3695
        }
3696
        else {
3697
            YAHOO.log("Could not update key " + sKey + " for Record " + record, "error", this.toString());
3698
        }
3699
    },
3700
 
3701
    /**
3702
     * Replaces all Records in RecordSet with new object literal data.
3703
     *
3704
     * @method replaceRecords
3705
     * @param data {Object || Object[]} An object literal of data or an array of
3706
     * data object literals.
3707
     * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or
3708
     * an array of Records.
3709
     */
3710
    replaceRecords : function(data) {
3711
        this.reset();
3712
        return this.addRecords(data);
3713
    },
3714
 
3715
    /**
3716
     * Sorts all Records by given function. Records keep their unique IDs but will
3717
     * have new RecordSet position indexes.
3718
     *
3719
     * @method sortRecords
3720
     * @param fnSort {Function} Reference to a sort function.
3721
     * @param desc {Boolean} True if sort direction is descending, false if sort
3722
     * direction is ascending.
3723
     * @param field {String} The field to sort by, from sortOptions.field
3724
     * @return {YAHOO.widget.Record[]} Sorted array of Records.
3725
     */
3726
    sortRecords : function(fnSort, desc, field) {
3727
        return this._records.sort(function(a, b) {return fnSort(a, b, desc, field);});
3728
    },
3729
 
3730
    /**
3731
     * Reverses all Records, so ["one", "two", "three"] becomes ["three", "two", "one"].
3732
     *
3733
     * @method reverseRecords
3734
     * @return {YAHOO.widget.Record[]} Reverse-sorted array of Records.
3735
     */
3736
    reverseRecords : function() {
3737
        return this._records.reverse();
3738
    },
3739
 
3740
    /**
3741
     * Removes the Record at the given position index from the RecordSet. If a range
3742
     * is also provided, removes that many Records, starting from the index. Length
3743
     * of RecordSet is correspondingly shortened.
3744
     *
3745
     * @method deleteRecord
3746
     * @param index {Number} Record's RecordSet position index.
3747
     * @return {Object} A copy of the data held by the deleted Record.
3748
     */
3749
    deleteRecord : function(index) {
3750
        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
3751
            var oData = this.getRecord(index).getData();
3752
 
3753
            this._deleteRecord(index);
3754
            this.fireEvent("recordDeleteEvent",{data:oData,index:index});
3755
            YAHOO.log("Record deleted at index " + index +
3756
                    " and containing data " + lang.dump(oData), "info", this.toString());
3757
            return oData;
3758
        }
3759
        else {
3760
            YAHOO.log("Could not delete Record at index " + index, "error", this.toString());
3761
            return null;
3762
        }
3763
    },
3764
 
3765
    /**
3766
     * Removes the Record at the given position index from the RecordSet. If a range
3767
     * is also provided, removes that many Records, starting from the index. Length
3768
     * of RecordSet is correspondingly shortened.
3769
     *
3770
     * @method deleteRecords
3771
     * @param index {Number} Record's RecordSet position index.
3772
     * @param range {Number} (optional) How many Records to delete.
3773
     * @return {Object[]} An array of copies of the data held by the deleted Records.
3774
     */
3775
    deleteRecords : function(index, range) {
3776
        if(!lang.isNumber(range)) {
3777
            range = 1;
3778
        }
3779
        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
3780
            var recordsToDelete = this.getRecords(index, range);
3781
            var deletedData = [], // this mistakenly held Records, not data
3782
                deletedObjects = []; // this passes data only
3783
 
3784
            for(var i=0; i<recordsToDelete.length; i++) {
3785
                deletedData[deletedData.length] = recordsToDelete[i]; // backward compatibility
3786
                deletedObjects[deletedObjects.length] = recordsToDelete[i].getData();
3787
            }
3788
            this._deleteRecord(index, range);
3789
 
3790
            this.fireEvent("recordsDeleteEvent",{data:deletedData,deletedData:deletedObjects,index:index});
3791
            YAHOO.log(range + "Record(s) deleted at index " + index +
3792
                    " and containing data " + lang.dump(deletedObjects), "info", this.toString());
3793
 
3794
            return deletedData;
3795
        }
3796
        else {
3797
            YAHOO.log("Could not delete Records at index " + index, "error", this.toString());
3798
            return null;
3799
        }
3800
    },
3801
 
3802
    /**
3803
     * Deletes all Records from the RecordSet.
3804
     *
3805
     * @method reset
3806
     */
3807
    reset : function() {
3808
        this._records = [];
3809
        //this._length = 0;
3810
        this.fireEvent("resetEvent");
3811
        YAHOO.log("All Records deleted from RecordSet", "info", this.toString());
3812
    }
3813
};
3814
 
3815
/////////////////////////////////////////////////////////////////////////////
3816
//
3817
// Custom Events
3818
//
3819
/////////////////////////////////////////////////////////////////////////////
3820
 
3821
// RecordSet uses EventProvider
3822
lang.augmentProto(RS, util.EventProvider);
3823
 
3824
/**
3825
 * Fired when a new Record is added to the RecordSet.
3826
 *
3827
 * @event recordAddEvent
3828
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3829
 * @param oArgs.data {Object} Data added.
3830
 */
3831
 
3832
/**
3833
 * Fired when multiple Records are added to the RecordSet at once.
3834
 *
3835
 * @event recordsAddEvent
3836
 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
3837
 * @param oArgs.data {Object[]} Data added.
3838
 */
3839
 
3840
/**
3841
 * Fired when a Record is set in the RecordSet.
3842
 *
3843
 * @event recordSetEvent
3844
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3845
 * @param oArgs.data {Object} Data added.
3846
 */
3847
 
3848
/**
3849
 * Fired when multiple Records are set in the RecordSet at once.
3850
 *
3851
 * @event recordsSetEvent
3852
 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
3853
 * @param oArgs.data {Object[]} Data added.
3854
 */
3855
 
3856
/**
3857
 * Fired when a Record is updated with new data.
3858
 *
3859
 * @event recordUpdateEvent
3860
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3861
 * @param oArgs.newData {Object} New data.
3862
 * @param oArgs.oldData {Object} Old data.
3863
 */
3864
 
3865
/**
3866
 * Fired when a Record is deleted from the RecordSet.
3867
 *
3868
 * @event recordDeleteEvent
3869
 * @param oArgs.data {Object} The data held by the deleted Record,
3870
 * or an array of data object literals if multiple Records were deleted at once.
3871
 * @param oArgs.index {Object} Index of the deleted Record.
3872
 */
3873
 
3874
/**
3875
 * Fired when multiple Records are deleted from the RecordSet at once.
3876
 *
3877
 * @event recordsDeleteEvent
3878
 * @param oArgs.data {Object[]} An array of deleted Records.
3879
 * @param oArgs.deletedData {Object[]} An array of deleted data.
3880
 * @param oArgs.index {Object} Index of the first deleted Record.
3881
 */
3882
 
3883
/**
3884
 * Fired when all Records are deleted from the RecordSet at once.
3885
 *
3886
 * @event resetEvent
3887
 */
3888
 
3889
/**
3890
 * @event keyUpdateEvent
3891
 * @deprecated Use recordValueUpdateEvent
3892
 */
3893
 
3894
/**
3895
 * Fired when a Record value is updated with new data.
3896
 *
3897
 * @event recordValueUpdateEvent
3898
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3899
 * @param oArgs.key {String} The updated key.
3900
 * @param oArgs.newData {Object} New data.
3901
 * @param oArgs.oldData {Object} Old data.
3902
 *
3903
 */
3904
 
3905
 
3906
/****************************************************************************/
3907
/****************************************************************************/
3908
/****************************************************************************/
3909
 
3910
/**
3911
 * The Record class defines a DataTable record.
3912
 *
3913
 * @namespace YAHOO.widget
3914
 * @class Record
3915
 * @constructor
3916
 * @param oConfigs {Object} (optional) Object literal of key/value pairs.
3917
 */
3918
YAHOO.widget.Record = function(oLiteral) {
3919
    this._nCount = widget.Record._nCount;
3920
    this._sId = Dom.generateId(null, "yui-rec");//"yui-rec" + this._nCount;
3921
    widget.Record._nCount++;
3922
    this._oData = {};
3923
    if(lang.isObject(oLiteral)) {
3924
        for(var sKey in oLiteral) {
3925
            if(lang.hasOwnProperty(oLiteral, sKey)) {
3926
                this._oData[sKey] = oLiteral[sKey];
3927
            }
3928
        }
3929
    }
3930
};
3931
 
3932
/////////////////////////////////////////////////////////////////////////////
3933
//
3934
// Private member variables
3935
//
3936
/////////////////////////////////////////////////////////////////////////////
3937
 
3938
/**
3939
 * Internal class variable to give unique IDs to Record instances.
3940
 *
3941
 * @property Record._nCount
3942
 * @type Number
3943
 * @private
3944
 */
3945
YAHOO.widget.Record._nCount = 0;
3946
 
3947
YAHOO.widget.Record.prototype = {
3948
    /**
3949
     * Immutable unique count assigned at instantiation. Remains constant while a
3950
     * Record's position index can change from sorting.
3951
     *
3952
     * @property _nCount
3953
     * @type Number
3954
     * @private
3955
     */
3956
    _nCount : null,
3957
 
3958
    /**
3959
     * Immutable unique ID assigned at instantiation. Remains constant while a
3960
     * Record's position index can change from sorting.
3961
     *
3962
     * @property _sId
3963
     * @type String
3964
     * @private
3965
     */
3966
    _sId : null,
3967
 
3968
    /**
3969
     * Holds data for the Record in an object literal.
3970
     *
3971
     * @property _oData
3972
     * @type Object
3973
     * @private
3974
     */
3975
    _oData : null,
3976
 
3977
    /////////////////////////////////////////////////////////////////////////////
3978
    //
3979
    // Public member variables
3980
    //
3981
    /////////////////////////////////////////////////////////////////////////////
3982
 
3983
    /////////////////////////////////////////////////////////////////////////////
3984
    //
3985
    // Public methods
3986
    //
3987
    /////////////////////////////////////////////////////////////////////////////
3988
 
3989
    /**
3990
     * Returns unique count assigned at instantiation.
3991
     *
3992
     * @method getCount
3993
     * @return Number
3994
     */
3995
    getCount : function() {
3996
        return this._nCount;
3997
    },
3998
 
3999
    /**
4000
     * Returns unique ID assigned at instantiation.
4001
     *
4002
     * @method getId
4003
     * @return String
4004
     */
4005
    getId : function() {
4006
        return this._sId;
4007
    },
4008
 
4009
    /**
4010
     * Returns data for the Record for a field if given, or the entire object
4011
     * literal otherwise.
4012
     *
4013
     * @method getData
4014
     * @param sField {String} (Optional) The field from which to retrieve data value.
4015
     * @return Object
4016
     */
4017
    getData : function(sField) {
4018
        if(lang.isString(sField)) {
4019
            return this._oData[sField];
4020
        }
4021
        else {
4022
            return this._oData;
4023
        }
4024
    },
4025
 
4026
    /**
4027
     * Sets given data at the given key. Use the RecordSet method updateRecordValue to trigger
4028
     * events.
4029
     *
4030
     * @method setData
4031
     * @param sKey {String} The key of the new value.
4032
     * @param oData {MIXED} The new value.
4033
     */
4034
    setData : function(sKey, oData) {
4035
        this._oData[sKey] = oData;
4036
    }
4037
};
4038
 
4039
})();
4040
 
4041
(function () {
4042
 
4043
var lang   = YAHOO.lang,
4044
    util   = YAHOO.util,
4045
    widget = YAHOO.widget,
4046
    ua     = YAHOO.env.ua,
4047
 
4048
    Dom    = util.Dom,
4049
    Ev     = util.Event,
4050
    DS     = util.DataSourceBase;
4051
 
4052
/**
4053
 * The DataTable widget provides a progressively enhanced DHTML control for
4054
 * displaying tabular data across A-grade browsers.
4055
 *
4056
 * @module datatable
4057
 * @requires yahoo, dom, event, element, datasource
4058
 * @optional dragdrop, dragdrop
4059
 * @title DataTable Widget
4060
 */
4061
 
4062
/****************************************************************************/
4063
/****************************************************************************/
4064
/****************************************************************************/
4065
 
4066
/**
4067
 * DataTable class for the YUI DataTable widget.
4068
 *
4069
 * @namespace YAHOO.widget
4070
 * @class DataTable
4071
 * @extends YAHOO.util.Element
4072
 * @constructor
4073
 * @param elContainer {HTMLElement} Container element for the TABLE.
4074
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
4075
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
4076
 * @param oConfigs {object} (optional) Object literal of configuration values.
4077
 */
4078
YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
4079
    var DT = widget.DataTable;
4080
 
4081
    ////////////////////////////////////////////////////////////////////////////
4082
    // Backward compatibility for SDT, but prevent infinite loops
4083
 
4084
    if(oConfigs && oConfigs.scrollable) {
4085
        return new YAHOO.widget.ScrollingDataTable(elContainer,aColumnDefs,oDataSource,oConfigs);
4086
    }
4087
 
4088
    ////////////////////////////////////////////////////////////////////////////
4089
    // Initialization
4090
 
4091
    // Internal vars
4092
    this._nIndex = DT._nCount;
4093
    this._sId = Dom.generateId(null, "yui-dt");// "yui-dt"+this._nIndex;
4094
    this._oChainRender = new YAHOO.util.Chain();
4095
    this._oChainRender.subscribe("end",this._onRenderChainEnd, this, true);
4096
 
4097
    // Initialize configs
4098
    this._initConfigs(oConfigs);
4099
 
4100
    // Initialize DataSource
4101
    this._initDataSource(oDataSource);
4102
    if(!this._oDataSource) {
4103
        YAHOO.log("Could not instantiate DataTable due to an invalid DataSource", "error", this.toString());
4104
        return;
4105
    }
4106
 
4107
    // Initialize ColumnSet
4108
    this._initColumnSet(aColumnDefs);
4109
    if(!this._oColumnSet) {
4110
        YAHOO.log("Could not instantiate DataTable due to an invalid ColumnSet", "error", this.toString());
4111
        return;
4112
    }
4113
 
4114
    // Initialize RecordSet
4115
    this._initRecordSet();
4116
    if(!this._oRecordSet) {
4117
    }
4118
 
4119
    // Initialize Attributes
4120
    DT.superclass.constructor.call(this, elContainer, this.configs);
4121
 
4122
    // Initialize DOM elements
4123
    var okDom = this._initDomElements(elContainer);
4124
    if(!okDom) {
4125
        YAHOO.log("Could not instantiate DataTable due to an invalid DOM element", "error", this.toString());
4126
        return;
4127
    }
4128
 
4129
    // Show message as soon as config is available
4130
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
4131
 
4132
    ////////////////////////////////////////////////////////////////////////////
4133
    // Once per instance
4134
    this._initEvents();
4135
 
4136
    DT._nCount++;
4137
    DT._nCurrentCount++;
4138
 
4139
    ////////////////////////////////////////////////////////////////////////////
4140
    // Data integration
4141
 
4142
    // Send a simple initial request
4143
    var oCallback = {
4144
        success : this.onDataReturnSetRows,
4145
        failure : this.onDataReturnSetRows,
4146
        scope   : this,
4147
        argument: this.getState()
4148
    };
4149
 
4150
    var initialLoad = this.get("initialLoad");
4151
    if(initialLoad === true) {
4152
        this._oDataSource.sendRequest(this.get("initialRequest"), oCallback);
4153
    }
4154
    // Do not send an initial request at all
4155
    else if(initialLoad === false) {
4156
        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
4157
    }
4158
    // Send an initial request with a custom payload
4159
    else {
4160
        var oCustom = initialLoad || {};
4161
        oCallback.argument = oCustom.argument || {};
4162
        this._oDataSource.sendRequest(oCustom.request, oCallback);
4163
    }
4164
};
4165
 
4166
var DT = widget.DataTable;
4167
 
4168
/////////////////////////////////////////////////////////////////////////////
4169
//
4170
// Public constants
4171
//
4172
/////////////////////////////////////////////////////////////////////////////
4173
 
4174
lang.augmentObject(DT, {
4175
 
4176
    /**
4177
     * Class name assigned to outer DataTable container.
4178
     *
4179
     * @property DataTable.CLASS_DATATABLE
4180
     * @type String
4181
     * @static
4182
     * @final
4183
     * @default "yui-dt"
4184
     */
4185
    CLASS_DATATABLE : "yui-dt",
4186
 
4187
    /**
4188
     * Class name assigned to liner DIV elements.
4189
     *
4190
     * @property DataTable.CLASS_LINER
4191
     * @type String
4192
     * @static
4193
     * @final
4194
     * @default "yui-dt-liner"
4195
     */
4196
    CLASS_LINER : "yui-dt-liner",
4197
 
4198
    /**
4199
     * Class name assigned to display label elements.
4200
     *
4201
     * @property DataTable.CLASS_LABEL
4202
     * @type String
4203
     * @static
4204
     * @final
4205
     * @default "yui-dt-label"
4206
     */
4207
    CLASS_LABEL : "yui-dt-label",
4208
 
4209
    /**
4210
     * Class name assigned to messaging elements.
4211
     *
4212
     * @property DataTable.CLASS_MESSAGE
4213
     * @type String
4214
     * @static
4215
     * @final
4216
     * @default "yui-dt-message"
4217
     */
4218
    CLASS_MESSAGE : "yui-dt-message",
4219
 
4220
    /**
4221
     * Class name assigned to mask element when DataTable is disabled.
4222
     *
4223
     * @property DataTable.CLASS_MASK
4224
     * @type String
4225
     * @static
4226
     * @final
4227
     * @default "yui-dt-mask"
4228
     */
4229
    CLASS_MASK : "yui-dt-mask",
4230
 
4231
    /**
4232
     * Class name assigned to data elements.
4233
     *
4234
     * @property DataTable.CLASS_DATA
4235
     * @type String
4236
     * @static
4237
     * @final
4238
     * @default "yui-dt-data"
4239
     */
4240
    CLASS_DATA : "yui-dt-data",
4241
 
4242
    /**
4243
     * Class name assigned to Column drag target.
4244
     *
4245
     * @property DataTable.CLASS_COLTARGET
4246
     * @type String
4247
     * @static
4248
     * @final
4249
     * @default "yui-dt-coltarget"
4250
     */
4251
    CLASS_COLTARGET : "yui-dt-coltarget",
4252
 
4253
    /**
4254
     * Class name assigned to resizer handle elements.
4255
     *
4256
     * @property DataTable.CLASS_RESIZER
4257
     * @type String
4258
     * @static
4259
     * @final
4260
     * @default "yui-dt-resizer"
4261
     */
4262
    CLASS_RESIZER : "yui-dt-resizer",
4263
 
4264
    /**
4265
     * Class name assigned to resizer liner elements.
4266
     *
4267
     * @property DataTable.CLASS_RESIZERLINER
4268
     * @type String
4269
     * @static
4270
     * @final
4271
     * @default "yui-dt-resizerliner"
4272
     */
4273
    CLASS_RESIZERLINER : "yui-dt-resizerliner",
4274
 
4275
    /**
4276
     * Class name assigned to resizer proxy elements.
4277
     *
4278
     * @property DataTable.CLASS_RESIZERPROXY
4279
     * @type String
4280
     * @static
4281
     * @final
4282
     * @default "yui-dt-resizerproxy"
4283
     */
4284
    CLASS_RESIZERPROXY : "yui-dt-resizerproxy",
4285
 
4286
    /**
4287
     * Class name assigned to CellEditor container elements.
4288
     *
4289
     * @property DataTable.CLASS_EDITOR
4290
     * @type String
4291
     * @static
4292
     * @final
4293
     * @default "yui-dt-editor"
4294
     */
4295
    CLASS_EDITOR : "yui-dt-editor",
4296
 
4297
    /**
4298
     * Class name assigned to CellEditor container shim.
4299
     *
4300
     * @property DataTable.CLASS_EDITOR_SHIM
4301
     * @type String
4302
     * @static
4303
     * @final
4304
     * @default "yui-dt-editor-shim"
4305
     */
4306
    CLASS_EDITOR_SHIM : "yui-dt-editor-shim",
4307
 
4308
    /**
4309
     * Class name assigned to paginator container elements.
4310
     *
4311
     * @property DataTable.CLASS_PAGINATOR
4312
     * @type String
4313
     * @static
4314
     * @final
4315
     * @default "yui-dt-paginator"
4316
     */
4317
    CLASS_PAGINATOR : "yui-dt-paginator",
4318
 
4319
    /**
4320
     * Class name assigned to page number indicators.
4321
     *
4322
     * @property DataTable.CLASS_PAGE
4323
     * @type String
4324
     * @static
4325
     * @final
4326
     * @default "yui-dt-page"
4327
     */
4328
    CLASS_PAGE : "yui-dt-page",
4329
 
4330
    /**
4331
     * Class name assigned to default indicators.
4332
     *
4333
     * @property DataTable.CLASS_DEFAULT
4334
     * @type String
4335
     * @static
4336
     * @final
4337
     * @default "yui-dt-default"
4338
     */
4339
    CLASS_DEFAULT : "yui-dt-default",
4340
 
4341
    /**
4342
     * Class name assigned to previous indicators.
4343
     *
4344
     * @property DataTable.CLASS_PREVIOUS
4345
     * @type String
4346
     * @static
4347
     * @final
4348
     * @default "yui-dt-previous"
4349
     */
4350
    CLASS_PREVIOUS : "yui-dt-previous",
4351
 
4352
    /**
4353
     * Class name assigned next indicators.
4354
     *
4355
     * @property DataTable.CLASS_NEXT
4356
     * @type String
4357
     * @static
4358
     * @final
4359
     * @default "yui-dt-next"
4360
     */
4361
    CLASS_NEXT : "yui-dt-next",
4362
 
4363
    /**
4364
     * Class name assigned to first elements.
4365
     *
4366
     * @property DataTable.CLASS_FIRST
4367
     * @type String
4368
     * @static
4369
     * @final
4370
     * @default "yui-dt-first"
4371
     */
4372
    CLASS_FIRST : "yui-dt-first",
4373
 
4374
    /**
4375
     * Class name assigned to last elements.
4376
     *
4377
     * @property DataTable.CLASS_LAST
4378
     * @type String
4379
     * @static
4380
     * @final
4381
     * @default "yui-dt-last"
4382
     */
4383
    CLASS_LAST : "yui-dt-last",
4384
 
4385
    /**
4386
     * Class name assigned to Record elements.
4387
     *
4388
     * @property DataTable.CLASS_REC
4389
     * @type String
4390
     * @static
4391
     * @final
4392
     * @default "yui-dt-rec"
4393
     */
4394
    CLASS_REC : "yui-dt-rec",
4395
 
4396
    /**
4397
     * Class name assigned to even elements.
4398
     *
4399
     * @property DataTable.CLASS_EVEN
4400
     * @type String
4401
     * @static
4402
     * @final
4403
     * @default "yui-dt-even"
4404
     */
4405
    CLASS_EVEN : "yui-dt-even",
4406
 
4407
    /**
4408
     * Class name assigned to odd elements.
4409
     *
4410
     * @property DataTable.CLASS_ODD
4411
     * @type String
4412
     * @static
4413
     * @final
4414
     * @default "yui-dt-odd"
4415
     */
4416
    CLASS_ODD : "yui-dt-odd",
4417
 
4418
    /**
4419
     * Class name assigned to selected elements.
4420
     *
4421
     * @property DataTable.CLASS_SELECTED
4422
     * @type String
4423
     * @static
4424
     * @final
4425
     * @default "yui-dt-selected"
4426
     */
4427
    CLASS_SELECTED : "yui-dt-selected",
4428
 
4429
    /**
4430
     * Class name assigned to highlighted elements.
4431
     *
4432
     * @property DataTable.CLASS_HIGHLIGHTED
4433
     * @type String
4434
     * @static
4435
     * @final
4436
     * @default "yui-dt-highlighted"
4437
     */
4438
    CLASS_HIGHLIGHTED : "yui-dt-highlighted",
4439
 
4440
    /**
4441
     * Class name assigned to hidden elements.
4442
     *
4443
     * @property DataTable.CLASS_HIDDEN
4444
     * @type String
4445
     * @static
4446
     * @final
4447
     * @default "yui-dt-hidden"
4448
     */
4449
    CLASS_HIDDEN : "yui-dt-hidden",
4450
 
4451
    /**
4452
     * Class name assigned to disabled elements.
4453
     *
4454
     * @property DataTable.CLASS_DISABLED
4455
     * @type String
4456
     * @static
4457
     * @final
4458
     * @default "yui-dt-disabled"
4459
     */
4460
    CLASS_DISABLED : "yui-dt-disabled",
4461
 
4462
    /**
4463
     * Class name assigned to empty indicators.
4464
     *
4465
     * @property DataTable.CLASS_EMPTY
4466
     * @type String
4467
     * @static
4468
     * @final
4469
     * @default "yui-dt-empty"
4470
     */
4471
    CLASS_EMPTY : "yui-dt-empty",
4472
 
4473
    /**
4474
     * Class name assigned to loading indicatorx.
4475
     *
4476
     * @property DataTable.CLASS_LOADING
4477
     * @type String
4478
     * @static
4479
     * @final
4480
     * @default "yui-dt-loading"
4481
     */
4482
    CLASS_LOADING : "yui-dt-loading",
4483
 
4484
    /**
4485
     * Class name assigned to error indicators.
4486
     *
4487
     * @property DataTable.CLASS_ERROR
4488
     * @type String
4489
     * @static
4490
     * @final
4491
     * @default "yui-dt-error"
4492
     */
4493
    CLASS_ERROR : "yui-dt-error",
4494
 
4495
    /**
4496
     * Class name assigned to editable elements.
4497
     *
4498
     * @property DataTable.CLASS_EDITABLE
4499
     * @type String
4500
     * @static
4501
     * @final
4502
     * @default "yui-dt-editable"
4503
     */
4504
    CLASS_EDITABLE : "yui-dt-editable",
4505
 
4506
    /**
4507
     * Class name assigned to draggable elements.
4508
     *
4509
     * @property DataTable.CLASS_DRAGGABLE
4510
     * @type String
4511
     * @static
4512
     * @final
4513
     * @default "yui-dt-draggable"
4514
     */
4515
    CLASS_DRAGGABLE : "yui-dt-draggable",
4516
 
4517
    /**
4518
     * Class name assigned to resizeable elements.
4519
     *
4520
     * @property DataTable.CLASS_RESIZEABLE
4521
     * @type String
4522
     * @static
4523
     * @final
4524
     * @default "yui-dt-resizeable"
4525
     */
4526
    CLASS_RESIZEABLE : "yui-dt-resizeable",
4527
 
4528
    /**
4529
     * Class name assigned to scrollable elements.
4530
     *
4531
     * @property DataTable.CLASS_SCROLLABLE
4532
     * @type String
4533
     * @static
4534
     * @final
4535
     * @default "yui-dt-scrollable"
4536
     */
4537
    CLASS_SCROLLABLE : "yui-dt-scrollable",
4538
 
4539
    /**
4540
     * Class name assigned to sortable elements.
4541
     *
4542
     * @property DataTable.CLASS_SORTABLE
4543
     * @type String
4544
     * @static
4545
     * @final
4546
     * @default "yui-dt-sortable"
4547
     */
4548
    CLASS_SORTABLE : "yui-dt-sortable",
4549
 
4550
    /**
4551
     * Class name assigned to ascending elements.
4552
     *
4553
     * @property DataTable.CLASS_ASC
4554
     * @type String
4555
     * @static
4556
     * @final
4557
     * @default "yui-dt-asc"
4558
     */
4559
    CLASS_ASC : "yui-dt-asc",
4560
 
4561
    /**
4562
     * Class name assigned to descending elements.
4563
     *
4564
     * @property DataTable.CLASS_DESC
4565
     * @type String
4566
     * @static
4567
     * @final
4568
     * @default "yui-dt-desc"
4569
     */
4570
    CLASS_DESC : "yui-dt-desc",
4571
 
4572
    /**
4573
     * Class name assigned to BUTTON elements and/or container elements.
4574
     *
4575
     * @property DataTable.CLASS_BUTTON
4576
     * @type String
4577
     * @static
4578
     * @final
4579
     * @default "yui-dt-button"
4580
     */
4581
    CLASS_BUTTON : "yui-dt-button",
4582
 
4583
    /**
4584
     * Class name assigned to INPUT TYPE=CHECKBOX elements and/or container elements.
4585
     *
4586
     * @property DataTable.CLASS_CHECKBOX
4587
     * @type String
4588
     * @static
4589
     * @final
4590
     * @default "yui-dt-checkbox"
4591
     */
4592
    CLASS_CHECKBOX : "yui-dt-checkbox",
4593
 
4594
    /**
4595
     * Class name assigned to SELECT elements and/or container elements.
4596
     *
4597
     * @property DataTable.CLASS_DROPDOWN
4598
     * @type String
4599
     * @static
4600
     * @final
4601
     * @default "yui-dt-dropdown"
4602
     */
4603
    CLASS_DROPDOWN : "yui-dt-dropdown",
4604
 
4605
    /**
4606
     * Class name assigned to INPUT TYPE=RADIO elements and/or container elements.
4607
     *
4608
     * @property DataTable.CLASS_RADIO
4609
     * @type String
4610
     * @static
4611
     * @final
4612
     * @default "yui-dt-radio"
4613
     */
4614
    CLASS_RADIO : "yui-dt-radio",
4615
 
4616
    /////////////////////////////////////////////////////////////////////////
4617
    //
4618
    // Private static properties
4619
    //
4620
    /////////////////////////////////////////////////////////////////////////
4621
 
4622
    /**
4623
     * Internal class variable for indexing multiple DataTable instances.
4624
     *
4625
     * @property DataTable._nCount
4626
     * @type Number
4627
     * @private
4628
     * @static
4629
     */
4630
    _nCount : 0,
4631
 
4632
    /**
4633
     * Internal class variable tracking current number of DataTable instances,
4634
     * so that certain class values can be reset when all instances are destroyed.
4635
     *
4636
     * @property DataTable._nCurrentCount
4637
     * @type Number
4638
     * @private
4639
     * @static
4640
     */
4641
    _nCurrentCount : 0,
4642
 
4643
    /**
4644
     * Reference to the STYLE node that is dynamically created and updated
4645
     * in order to manage Column widths.
4646
     *
4647
     * @property DataTable._elDynStyleNode
4648
     * @type HTMLElement
4649
     * @private
4650
     * @static
4651
     */
4652
    _elDynStyleNode : null,
4653
 
4654
    /**
4655
     * Set to true if _elDynStyleNode cannot be populated due to browser incompatibility.
4656
     *
4657
     * @property DataTable._bDynStylesFallback
4658
     * @type boolean
4659
     * @private
4660
     * @static
4661
     */
4662
    _bDynStylesFallback : (ua.ie) ? true : false,
4663
 
4664
    /**
4665
     * Object literal hash of Columns and their dynamically create style rules.
4666
     *
4667
     * @property DataTable._oDynStyles
4668
     * @type Object
4669
     * @private
4670
     * @static
4671
     */
4672
    _oDynStyles : {},
4673
 
4674
    /////////////////////////////////////////////////////////////////////////
4675
    //
4676
    // Private static methods
4677
    //
4678
    /////////////////////////////////////////////////////////////////////////
4679
 
4680
    /**
4681
     * Clones object literal or array of object literals.
4682
     *
4683
     * @method DataTable._cloneObject
4684
     * @param o {Object} Object.
4685
     * @private
4686
     * @static
4687
     */
4688
    _cloneObject: function(o) {
4689
        if(!lang.isValue(o)) {
4690
            return o;
4691
        }
4692
 
4693
        var copy = {};
4694
 
4695
        if(o instanceof YAHOO.widget.BaseCellEditor) {
4696
            copy = o;
4697
        }
4698
        else if(Object.prototype.toString.apply(o) === "[object RegExp]") {
4699
            copy = o;
4700
        }
4701
        else if(lang.isFunction(o)) {
4702
            copy = o;
4703
        }
4704
        else if(lang.isArray(o)) {
4705
            var array = [];
4706
            for(var i=0,len=o.length;i<len;i++) {
4707
                array[i] = DT._cloneObject(o[i]);
4708
            }
4709
            copy = array;
4710
        }
4711
        else if(lang.isObject(o)) {
4712
            for (var x in o){
4713
                if(lang.hasOwnProperty(o, x)) {
4714
                    if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
4715
                        copy[x] = DT._cloneObject(o[x]);
4716
                    }
4717
                    else {
4718
                        copy[x] = o[x];
4719
                    }
4720
                }
4721
            }
4722
        }
4723
        else {
4724
            copy = o;
4725
        }
4726
 
4727
        return copy;
4728
    },
4729
 
4730
    /**
4731
     * Formats a BUTTON element.
4732
     *
4733
     * @method DataTable.formatButton
4734
     * @param el {HTMLElement} The element to format with markup.
4735
     * @param oRecord {YAHOO.widget.Record} Record instance.
4736
     * @param oColumn {YAHOO.widget.Column} Column instance.
4737
     * @param oData {HTML} Data value for the cell. By default, the value
4738
     * is what gets written to the BUTTON. String values are treated as markup
4739
     * and inserted into the DOM with innerHTML.
4740
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4741
     * @static
4742
     */
4743
    formatButton : function(el, oRecord, oColumn, oData, oDataTable) {
4744
        var sValue = lang.isValue(oData) ? oData : "Click";
4745
        //TODO: support YAHOO.widget.Button
4746
        //if(YAHOO.widget.Button) {
4747
 
4748
        //}
4749
        //else {
4750
            el.innerHTML = "<button type=\"button\" class=\""+
4751
                    DT.CLASS_BUTTON + "\">" + sValue + "</button>";
4752
        //}
4753
    },
4754
 
4755
    /**
4756
     * Formats a CHECKBOX element.
4757
     *
4758
     * @method DataTable.formatCheckbox
4759
     * @param el {HTMLElement} The element to format with markup.
4760
     * @param oRecord {YAHOO.widget.Record} Record instance.
4761
     * @param oColumn {YAHOO.widget.Column} Column instance.
4762
     * @param oData {Object | Boolean | HTML} Data value for the cell. Can be a simple
4763
     * Boolean to indicate whether checkbox is checked or not. Can be object literal
4764
     * {checked:bBoolean, label:sLabel}. String values are treated as markup
4765
     * and inserted into the DOM with innerHTML.
4766
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4767
     * @static
4768
     */
4769
    formatCheckbox : function(el, oRecord, oColumn, oData, oDataTable) {
4770
        var bChecked = oData;
4771
        bChecked = (bChecked) ? " checked=\"checked\"" : "";
4772
        el.innerHTML = "<input type=\"checkbox\"" + bChecked +
4773
                " class=\"" + DT.CLASS_CHECKBOX + "\" />";
4774
    },
4775
 
4776
    /**
4777
     * Formats currency. Default unit is USD.
4778
     *
4779
     * @method DataTable.formatCurrency
4780
     * @param el {HTMLElement} The element to format with markup.
4781
     * @param oRecord {YAHOO.widget.Record} Record instance.
4782
     * @param oColumn {YAHOO.widget.Column} Column instance.
4783
     * @param oData {Number} Data value for the cell.
4784
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4785
     * @static
4786
     */
4787
    formatCurrency : function(el, oRecord, oColumn, oData, oDataTable) {
4788
        var oDT = oDataTable || this;
4789
        el.innerHTML = util.Number.format(oData, oColumn.currencyOptions || oDT.get("currencyOptions"));
4790
    },
4791
 
4792
    /**
4793
     * Formats JavaScript Dates.
4794
     *
4795
     * @method DataTable.formatDate
4796
     * @param el {HTMLElement} The element to format with markup.
4797
     * @param oRecord {YAHOO.widget.Record} Record instance.
4798
     * @param oColumn {YAHOO.widget.Column} Column instance.
4799
     * @param oData {Object} Data value for the cell, or null. String values are
4800
     * treated as markup and inserted into the DOM with innerHTML.
4801
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4802
     * @static
4803
     */
4804
    formatDate : function(el, oRecord, oColumn, oData, oDataTable) {
4805
        var oDT = oDataTable || this,
4806
            oConfig = oColumn.dateOptions || oDT.get("dateOptions");
4807
        el.innerHTML = util.Date.format(oData, oConfig, oConfig.locale);
4808
    },
4809
 
4810
    /**
4811
     * Formats SELECT elements.
4812
     *
4813
     * @method DataTable.formatDropdown
4814
     * @param el {HTMLElement} The element to format with markup.
4815
     * @param oRecord {YAHOO.widget.Record} Record instance.
4816
     * @param oColumn {YAHOO.widget.Column} Column instance.
4817
     * @param oData {Object} Data value for the cell, or null. String values may
4818
     * be treated as markup and inserted into the DOM with innerHTML as element
4819
     * label.
4820
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4821
     * @static
4822
     */
4823
    formatDropdown : function(el, oRecord, oColumn, oData, oDataTable) {
4824
        var oDT = oDataTable || this,
4825
            selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field),
4826
            options = (lang.isArray(oColumn.dropdownOptions)) ?
4827
                oColumn.dropdownOptions : null,
4828
 
4829
            selectEl,
4830
            collection = el.getElementsByTagName("select");
4831
 
4832
        // Create the form element only once, so we can attach the onChange listener
4833
        if(collection.length === 0) {
4834
            // Create SELECT element
4835
            selectEl = document.createElement("select");
4836
            selectEl.className = DT.CLASS_DROPDOWN;
4837
            selectEl = el.appendChild(selectEl);
4838
 
4839
            // Add event listener
4840
            Ev.addListener(selectEl,"change",oDT._onDropdownChange,oDT);
4841
        }
4842
 
4843
        selectEl = collection[0];
4844
 
4845
        // Update the form element
4846
        if(selectEl) {
4847
            // Clear out previous options
4848
            selectEl.innerHTML = "";
4849
 
4850
            // We have options to populate
4851
            if(options) {
4852
                // Create OPTION elements
4853
                for(var i=0; i<options.length; i++) {
4854
                    var option = options[i];
4855
                    var optionEl = document.createElement("option");
4856
                    optionEl.value = (lang.isValue(option.value)) ?
4857
                            option.value : option;
4858
                    // Bug 2334323: Support legacy text, support label for consistency with DropdownCellEditor
4859
                    optionEl.innerHTML = (lang.isValue(option.text)) ?
4860
                            option.text : (lang.isValue(option.label)) ? option.label : option;
4861
                    optionEl = selectEl.appendChild(optionEl);
4862
                    if (optionEl.value == selectedValue) {
4863
                        optionEl.selected = true;
4864
                    }
4865
                }
4866
            }
4867
            // Selected value is our only option
4868
            else {
4869
                selectEl.innerHTML = "<option selected value=\"" + selectedValue + "\">" + selectedValue + "</option>";
4870
            }
4871
        }
4872
        else {
4873
            el.innerHTML = lang.isValue(oData) ? oData : "";
4874
        }
4875
    },
4876
 
4877
    /**
4878
     * Formats emails.
4879
     *
4880
     * @method DataTable.formatEmail
4881
     * @param el {HTMLElement} The element to format with markup.
4882
     * @param oRecord {YAHOO.widget.Record} Record instance.
4883
     * @param oColumn {YAHOO.widget.Column} Column instance.
4884
     * @param oData {String} Data value for the cell, or null. Values are
4885
     * HTML-escaped.
4886
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4887
     * @static
4888
     */
4889
    formatEmail : function(el, oRecord, oColumn, oData, oDataTable) {
4890
        if(lang.isString(oData)) {
4891
            oData = lang.escapeHTML(oData);
4892
            el.innerHTML = "<a href=\"mailto:" + oData + "\">" + oData + "</a>";
4893
        }
4894
        else {
4895
            el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : "";
4896
        }
4897
    },
4898
 
4899
    /**
4900
     * Formats links.
4901
     *
4902
     * @method DataTable.formatLink
4903
     * @param el {HTMLElement} The element to format with markup.
4904
     * @param oRecord {YAHOO.widget.Record} Record instance.
4905
     * @param oColumn {YAHOO.widget.Column} Column instance.
4906
     * @param oData {String} Data value for the cell, or null. Values are
4907
     * HTML-escaped
4908
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4909
     * @static
4910
     */
4911
    formatLink : function(el, oRecord, oColumn, oData, oDataTable) {
4912
        if(lang.isString(oData)) {
4913
            oData = lang.escapeHTML(oData);
4914
            el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>";
4915
        }
4916
        else {
4917
            el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : "";
4918
        }
4919
    },
4920
 
4921
    /**
4922
     * Formats numbers.
4923
     *
4924
     * @method DataTable.formatNumber
4925
     * @param el {HTMLElement} The element to format with markup.
4926
     * @param oRecord {YAHOO.widget.Record} Record instance.
4927
     * @param oColumn {YAHOO.widget.Column} Column instance.
4928
     * @param oData {Object} Data value for the cell, or null.
4929
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4930
     * @static
4931
     */
4932
    formatNumber : function(el, oRecord, oColumn, oData, oDataTable) {
4933
        var oDT = oDataTable || this;
4934
        el.innerHTML = util.Number.format(oData, oColumn.numberOptions || oDT.get("numberOptions"));
4935
    },
4936
 
4937
    /**
4938
     * Formats INPUT TYPE=RADIO elements.
4939
     *
4940
     * @method DataTable.formatRadio
4941
     * @param el {HTMLElement} The element to format with markup.
4942
     * @param oRecord {YAHOO.widget.Record} Record instance.
4943
     * @param oColumn {YAHOO.widget.Column} Column instance.
4944
     * @param oData {Object} (Optional) Data value for the cell.
4945
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4946
     * @static
4947
     */
4948
    formatRadio : function(el, oRecord, oColumn, oData, oDataTable) {
4949
        var oDT = oDataTable || this,
4950
            bChecked = oData;
4951
        bChecked = (bChecked) ? " checked=\"checked\"" : "";
4952
        el.innerHTML = "<input type=\"radio\"" + bChecked +
4953
                " name=\""+oDT.getId()+"-col-" + oColumn.getSanitizedKey() + "\"" +
4954
                " class=\"" + DT.CLASS_RADIO+ "\" />";
4955
    },
4956
 
4957
    /**
4958
     * Formats text strings.
4959
     *
4960
     * @method DataTable.formatText
4961
     * @param el {HTMLElement} The element to format with markup.
4962
     * @param oRecord {YAHOO.widget.Record} Record instance.
4963
     * @param oColumn {YAHOO.widget.Column} Column instance.
4964
     * @param oData {String} (Optional) Data value for the cell. Values are
4965
     * HTML-escaped.
4966
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4967
     * @static
4968
     */
4969
    formatText : function(el, oRecord, oColumn, oData, oDataTable) {
4970
        var value = (lang.isValue(oData)) ? oData : "";
4971
        el.innerHTML = lang.escapeHTML(value.toString());
4972
    },
4973
 
4974
    /**
4975
     * Formats TEXTAREA elements.
4976
     *
4977
     * @method DataTable.formatTextarea
4978
     * @param el {HTMLElement} The element to format with markup.
4979
     * @param oRecord {YAHOO.widget.Record} Record instance.
4980
     * @param oColumn {YAHOO.widget.Column} Column instance.
4981
     * @param oData {Object} (Optional) Data value for the cell. Values are
4982
     * HTML-escaped.
4983
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4984
     * @static
4985
     */
4986
    formatTextarea : function(el, oRecord, oColumn, oData, oDataTable) {
4987
        var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "",
4988
            markup = "<textarea>" + value + "</textarea>";
4989
        el.innerHTML = markup;
4990
    },
4991
 
4992
    /**
4993
     * Formats INPUT TYPE=TEXT elements.
4994
     *
4995
     * @method DataTable.formatTextbox
4996
     * @param el {HTMLElement} The element to format with markup.
4997
     * @param oRecord {YAHOO.widget.Record} Record instance.
4998
     * @param oColumn {YAHOO.widget.Column} Column instance.
4999
     * @param oData {Object} (Optional) Data value for the cell. Values are
5000
     * HTML-escaped.
5001
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
5002
     * @static
5003
     */
5004
    formatTextbox : function(el, oRecord, oColumn, oData, oDataTable) {
5005
        var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "",
5006
            markup = "<input type=\"text\" value=\"" + value + "\" />";
5007
        el.innerHTML = markup;
5008
    },
5009
 
5010
    /**
5011
     * Default cell formatter
5012
     *
5013
     * @method DataTable.formatDefault
5014
     * @param el {HTMLElement} The element to format with markup.
5015
     * @param oRecord {YAHOO.widget.Record} Record instance.
5016
     * @param oColumn {YAHOO.widget.Column} Column instance.
5017
     * @param oData {HTML} (Optional) Data value for the cell. String values are
5018
     * treated as markup and inserted into the DOM with innerHTML.
5019
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
5020
     * @static
5021
     */
5022
    formatDefault : function(el, oRecord, oColumn, oData, oDataTable) {
5023
        el.innerHTML = (lang.isValue(oData) && oData !== "") ? oData.toString() : "&#160;";
5024
    },
5025
 
5026
    /**
5027
     * Validates data value to type Number, doing type conversion as
5028
     * necessary. A valid Number value is return, else null is returned
5029
     * if input value does not validate.
5030
     *
5031
     *
5032
     * @method DataTable.validateNumber
5033
     * @param oData {Object} Data to validate.
5034
     * @static
5035
    */
5036
    validateNumber : function(oData) {
5037
        //Convert to number
5038
        var number = oData * 1;
5039
 
5040
        // Validate
5041
        if(lang.isNumber(number)) {
5042
            return number;
5043
        }
5044
        else {
5045
            YAHOO.log("Could not validate data " + lang.dump(oData) + " to type Number", "warn", this.toString());
5046
            return undefined;
5047
        }
5048
    }
5049
});
5050
 
5051
// Done in separate step so referenced functions are defined.
5052
/**
5053
 * Registry of cell formatting functions, enables shortcut pointers in Column
5054
 * definition formatter value (i.e., {key:"myColumn", formatter:"date"}).
5055
 * @property DataTable.Formatter
5056
 * @type Object
5057
 * @static
5058
 */
5059
DT.Formatter = {
5060
    button   : DT.formatButton,
5061
    checkbox : DT.formatCheckbox,
5062
    currency : DT.formatCurrency,
5063
    "date"   : DT.formatDate,
5064
    dropdown : DT.formatDropdown,
5065
    email    : DT.formatEmail,
5066
    link     : DT.formatLink,
5067
    "number" : DT.formatNumber,
5068
    radio    : DT.formatRadio,
5069
    text     : DT.formatText,
5070
    textarea : DT.formatTextarea,
5071
    textbox  : DT.formatTextbox,
5072
 
5073
    defaultFormatter : DT.formatDefault
5074
};
5075
 
5076
lang.extend(DT, util.Element, {
5077
 
5078
/////////////////////////////////////////////////////////////////////////////
5079
//
5080
// Superclass methods
5081
//
5082
/////////////////////////////////////////////////////////////////////////////
5083
 
5084
/**
5085
 * Implementation of Element's abstract method. Sets up config values.
5086
 *
5087
 * @method initAttributes
5088
 * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
5089
 * @private
5090
 */
5091
 
5092
initAttributes : function(oConfigs) {
5093
    oConfigs = oConfigs || {};
5094
    DT.superclass.initAttributes.call(this, oConfigs);
5095
 
5096
    /**
5097
    * @attribute summary
5098
    * @description String value for the SUMMARY attribute.
5099
    * @type String
5100
    * @default ""
5101
    */
5102
    this.setAttributeConfig("summary", {
5103
        value: "",
5104
        validator: lang.isString,
5105
        method: function(sSummary) {
5106
            if(this._elTable) {
5107
                this._elTable.summary = sSummary;
5108
            }
5109
        }
5110
    });
5111
 
5112
    /**
5113
    * @attribute selectionMode
5114
    * @description Specifies row or cell selection mode. Accepts the following strings:
5115
    *    <dl>
5116
    *      <dt>"standard"</dt>
5117
    *      <dd>Standard row selection with support for modifier keys to enable
5118
    *      multiple selections.</dd>
5119
    *
5120
    *      <dt>"single"</dt>
5121
    *      <dd>Row selection with modifier keys disabled to not allow
5122
    *      multiple selections.</dd>
5123
    *
5124
    *      <dt>"singlecell"</dt>
5125
    *      <dd>Cell selection with modifier keys disabled to not allow
5126
    *      multiple selections.</dd>
5127
    *
5128
    *      <dt>"cellblock"</dt>
5129
    *      <dd>Cell selection with support for modifier keys to enable multiple
5130
    *      selections in a block-fashion, like a spreadsheet.</dd>
5131
    *
5132
    *      <dt>"cellrange"</dt>
5133
    *      <dd>Cell selection with support for modifier keys to enable multiple
5134
    *      selections in a range-fashion, like a calendar.</dd>
5135
    *    </dl>
5136
    *
5137
    * @default "standard"
5138
    * @type String
5139
    */
5140
    this.setAttributeConfig("selectionMode", {
5141
        value: "standard",
5142
        validator: lang.isString
5143
    });
5144
 
5145
    /**
5146
    * @attribute sortedBy
5147
    * @description Object literal provides metadata for initial sort values if
5148
    * data will arrive pre-sorted:
5149
    * <dl>
5150
    *     <dt>sortedBy.key</dt>
5151
    *     <dd>{String} Key of sorted Column</dd>
5152
    *     <dt>sortedBy.dir</dt>
5153
    *     <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
5154
    * </dl>
5155
    * @type Object | null
5156
    */
5157
    this.setAttributeConfig("sortedBy", {
5158
        value: null,
5159
        // TODO: accepted array for nested sorts
5160
        validator: function(oNewSortedBy) {
5161
            if(oNewSortedBy) {
5162
                return (lang.isObject(oNewSortedBy) && oNewSortedBy.key);
5163
            }
5164
            else {
5165
                return (oNewSortedBy === null);
5166
            }
5167
        },
5168
        method: function(oNewSortedBy) {
5169
            // Stash the previous value
5170
            var oOldSortedBy = this.get("sortedBy");
5171
 
5172
            // Workaround for bug 1827195
5173
            this._configs.sortedBy.value = oNewSortedBy;
5174
 
5175
            // Remove ASC/DESC from TH
5176
            var oOldColumn,
5177
                nOldColumnKeyIndex,
5178
                oNewColumn,
5179
                nNewColumnKeyIndex;
5180
 
5181
            if(this._elThead) {
5182
                if(oOldSortedBy && oOldSortedBy.key && oOldSortedBy.dir) {
5183
                    oOldColumn = this._oColumnSet.getColumn(oOldSortedBy.key);
5184
                    nOldColumnKeyIndex = oOldColumn.getKeyIndex();
5185
 
5186
                    // Remove previous UI from THEAD
5187
                    var elOldTh = oOldColumn.getThEl();
5188
                    Dom.removeClass(elOldTh, oOldSortedBy.dir);
5189
                    this.formatTheadCell(oOldColumn.getThLinerEl().firstChild, oOldColumn, oNewSortedBy);
5190
                }
5191
                if(oNewSortedBy) {
5192
                    oNewColumn = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key);
5193
                    nNewColumnKeyIndex = oNewColumn.getKeyIndex();
5194
 
5195
                    // Update THEAD with new UI
5196
                    var elNewTh = oNewColumn.getThEl();
5197
                    // Backward compatibility
5198
                    if(oNewSortedBy.dir && ((oNewSortedBy.dir == "asc") ||  (oNewSortedBy.dir == "desc"))) {
5199
                        var newClass = (oNewSortedBy.dir == "desc") ?
5200
                                DT.CLASS_DESC :
5201
                                DT.CLASS_ASC;
5202
                        Dom.addClass(elNewTh, newClass);
5203
                    }
5204
                    else {
5205
                         var sortClass = oNewSortedBy.dir || DT.CLASS_ASC;
5206
                         Dom.addClass(elNewTh, sortClass);
5207
                    }
5208
                    this.formatTheadCell(oNewColumn.getThLinerEl().firstChild, oNewColumn, oNewSortedBy);
5209
                }
5210
            }
5211
 
5212
            if(this._elTbody) {
5213
                // Update TBODY UI
5214
                this._elTbody.style.display = "none";
5215
                var allRows = this._elTbody.rows,
5216
                    allCells;
5217
                for(var i=allRows.length-1; i>-1; i--) {
5218
                    allCells = allRows[i].childNodes;
5219
                    if(allCells[nOldColumnKeyIndex]) {
5220
                        Dom.removeClass(allCells[nOldColumnKeyIndex], oOldSortedBy.dir);
5221
                    }
5222
                    if(allCells[nNewColumnKeyIndex]) {
5223
                        Dom.addClass(allCells[nNewColumnKeyIndex], oNewSortedBy.dir);
5224
                    }
5225
                }
5226
                this._elTbody.style.display = "";
5227
            }
5228
 
5229
            this._clearTrTemplateEl();
5230
        }
5231
    });
5232
 
5233
    /**
5234
    * @attribute paginator
5235
    * @description An instance of YAHOO.widget.Paginator.
5236
    * @default null
5237
    * @type {Object|YAHOO.widget.Paginator}
5238
    */
5239
    this.setAttributeConfig("paginator", {
5240
        value : null,
5241
        validator : function (val) {
5242
            return val === null || val instanceof widget.Paginator;
5243
        },
5244
        method : function () { this._updatePaginator.apply(this,arguments); }
5245
    });
5246
 
5247
    /**
5248
    * @attribute caption
5249
    * @description Value for the CAPTION element. String values are treated as
5250
    * markup and inserted into the DOM with innerHTML. NB: Not supported in
5251
    * ScrollingDataTable.
5252
    * @type HTML
5253
    */
5254
    this.setAttributeConfig("caption", {
5255
        value: null,
5256
        validator: lang.isString,
5257
        method: function(sCaption) {
5258
            this._initCaptionEl(sCaption);
5259
        }
5260
    });
5261
 
5262
    /**
5263
    * @attribute draggableColumns
5264
    * @description True if Columns are draggable to reorder, false otherwise.
5265
    * The Drag & Drop Utility is required to enable this feature. Only top-level
5266
    * and non-nested Columns are draggable. Write once.
5267
    * @default false
5268
    * @type Boolean
5269
    */
5270
    this.setAttributeConfig("draggableColumns", {
5271
        value: false,
5272
        validator: lang.isBoolean,
5273
        method: function(oParam) {
5274
            if(this._elThead) {
5275
                if(oParam) {
5276
                    this._initDraggableColumns();
5277
                }
5278
                else {
5279
                    this._destroyDraggableColumns();
5280
                }
5281
            }
5282
        }
5283
    });
5284
 
5285
    /**
5286
    * @attribute renderLoopSize
5287
    * @description A value greater than 0 enables DOM rendering of rows to be
5288
    * executed from a non-blocking timeout queue and sets how many rows to be
5289
    * rendered per timeout. Recommended for very large data sets.
5290
    * @type Number
5291
    * @default 0
5292
    */
5293
     this.setAttributeConfig("renderLoopSize", {
5294
         value: 0,
5295
         validator: lang.isNumber
5296
     });
5297
 
5298
    /**
5299
    * @attribute sortFunction
5300
    * @description Default Column sort function, receives the following args:
5301
    *    <dl>
5302
    *      <dt>a {Object}</dt>
5303
    *      <dd>First sort argument.</dd>
5304
    *      <dt>b {Object}</dt>
5305
    *      <dd>Second sort argument.</dd>
5306
 
5307
    *      <dt>desc {Boolean}</dt>
5308
    *      <dd>True if sort direction is descending, false if
5309
    * sort direction is ascending.</dd>
5310
    *      <dt>field {String}</dt>
5311
    *      <dd>The field to sort by, from sortOptions.field</dd>
5312
    *   </dl>
5313
    * @type function
5314
    */
5315
    this.setAttributeConfig("sortFunction", {
5316
        value: function(a, b, desc, field) {
5317
            var compare = YAHOO.util.Sort.compare,
5318
                sorted = compare(a.getData(field),b.getData(field), desc);
5319
            if(sorted === 0) {
5320
                return compare(a.getCount(),b.getCount(), desc); // Bug 1932978
5321
            }
5322
            else {
5323
                return sorted;
5324
            }
5325
        }
5326
    });
5327
 
5328
    /**
5329
    * @attribute formatRow
5330
    * @description A function that accepts a TR element and its associated Record
5331
    * for custom formatting. The function must return TRUE in order to automatically
5332
    * continue formatting of child TD elements, else TD elements will not be
5333
    * automatically formatted.
5334
    * @type function
5335
    * @default null
5336
    */
5337
    this.setAttributeConfig("formatRow", {
5338
        value: null,
5339
        validator: lang.isFunction
5340
    });
5341
 
5342
    /**
5343
    * @attribute generateRequest
5344
    * @description A function that converts an object literal of desired DataTable
5345
    * states into a request value which is then passed to the DataSource's
5346
    * sendRequest method in order to retrieve data for those states. This
5347
    * function is passed an object literal of state data and a reference to the
5348
    * DataTable instance:
5349
    *
5350
    * <dl>
5351
    *   <dt>pagination<dt>
5352
    *   <dd>
5353
    *         <dt>offsetRecord</dt>
5354
    *         <dd>{Number} Index of the first Record of the desired page</dd>
5355
    *         <dt>rowsPerPage</dt>
5356
    *         <dd>{Number} Number of rows per page</dd>
5357
    *   </dd>
5358
    *   <dt>sortedBy</dt>
5359
    *   <dd>
5360
    *         <dt>key</dt>
5361
    *         <dd>{String} Key of sorted Column</dd>
5362
    *         <dt>dir</dt>
5363
    *         <dd>{String} Sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
5364
    *   </dd>
5365
    *   <dt>self</dt>
5366
    *   <dd>The DataTable instance</dd>
5367
    * </dl>
5368
    *
5369
    * and by default returns a String of syntax:
5370
    * "sort={sortColumn}&dir={sortDir}&startIndex={pageStartIndex}&results={rowsPerPage}"
5371
    * @type function
5372
    * @default HTMLFunction
5373
    */
5374
    this.setAttributeConfig("generateRequest", {
5375
        value: function(oState, oSelf) {
5376
            // Set defaults
5377
            oState = oState || {pagination:null, sortedBy:null};
5378
            var sort = encodeURIComponent((oState.sortedBy) ? oState.sortedBy.key : oSelf.getColumnSet().keys[0].getKey());
5379
            var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
5380
            var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
5381
            var results = (oState.pagination) ? oState.pagination.rowsPerPage : null;
5382
 
5383
            // Build the request
5384
            return  "sort=" + sort +
5385
                    "&dir=" + dir +
5386
                    "&startIndex=" + startIndex +
5387
                    ((results !== null) ? "&results=" + results : "");
5388
        },
5389
        validator: lang.isFunction
5390
    });
5391
 
5392
    /**
5393
    * @attribute initialRequest
5394
    * @description Defines the initial request that gets sent to the DataSource
5395
    * during initialization. Value is ignored if initialLoad is set to any value
5396
    * other than true.
5397
    * @type MIXED
5398
    * @default null
5399
    */
5400
    this.setAttributeConfig("initialRequest", {
5401
        value: null
5402
    });
5403
 
5404
    /**
5405
    * @attribute initialLoad
5406
    * @description Determines whether or not to load data at instantiation. By
5407
    * default, will trigger a sendRequest() to the DataSource and pass in the
5408
    * request defined by initialRequest. If set to false, data will not load
5409
    * at instantiation. Alternatively, implementers who wish to work with a
5410
    * custom payload may pass in an object literal with the following values:
5411
    *
5412
    *    <dl>
5413
    *      <dt>request (MIXED)</dt>
5414
    *      <dd>Request value.</dd>
5415
    *
5416
    *      <dt>argument (MIXED)</dt>
5417
    *      <dd>Custom data that will be passed through to the callback function.</dd>
5418
    *    </dl>
5419
    *
5420
    *
5421
    * @type Boolean | Object
5422
    * @default true
5423
    */
5424
    this.setAttributeConfig("initialLoad", {
5425
        value: true
5426
    });
5427
 
5428
    /**
5429
    * @attribute dynamicData
5430
    * @description If true, sorting and pagination are relegated to the DataSource
5431
    * for handling, using the request returned by the "generateRequest" function.
5432
    * Each new DataSource response blows away all previous Records. False by default, so
5433
    * sorting and pagination will be handled directly on the client side, without
5434
    * causing any new requests for data from the DataSource.
5435
    * @type Boolean
5436
    * @default false
5437
    */
5438
    this.setAttributeConfig("dynamicData", {
5439
        value: false,
5440
        validator: lang.isBoolean
5441
    });
5442
 
5443
    /**
5444
     * @attribute MSG_EMPTY
5445
     * @description Message to display if DataTable has no data. String
5446
     * values are treated as markup and inserted into the DOM with innerHTML.
5447
     * @type HTML
5448
     * @default "No records found."
5449
     */
5450
     this.setAttributeConfig("MSG_EMPTY", {
5451
         value: "No records found.",
5452
         validator: lang.isString
5453
     });
5454
 
5455
    /**
5456
     * @attribute MSG_LOADING
5457
     * @description Message to display while DataTable is loading data. String
5458
     * values are treated as markup and inserted into the DOM with innerHTML.
5459
     * @type HTML
5460
     * @default "Loading..."
5461
     */
5462
     this.setAttributeConfig("MSG_LOADING", {
5463
         value: "Loading...",
5464
         validator: lang.isString
5465
     });
5466
 
5467
    /**
5468
     * @attribute MSG_ERROR
5469
     * @description Message to display while DataTable has data error. String
5470
     * values are treated as markup and inserted into the DOM with innerHTML.
5471
     * @type HTML
5472
     * @default "Data error."
5473
     */
5474
     this.setAttributeConfig("MSG_ERROR", {
5475
         value: "Data error.",
5476
         validator: lang.isString
5477
     });
5478
 
5479
    /**
5480
     * @attribute MSG_SORTASC
5481
     * @description Message to display in tooltip to sort Column in ascending
5482
     * order. String values are treated as markup and inserted into the DOM as
5483
     * innerHTML.
5484
     * @type HTML
5485
     * @default "Click to sort ascending"
5486
     */
5487
     this.setAttributeConfig("MSG_SORTASC", {
5488
         value: "Click to sort ascending",
5489
         validator: lang.isString,
5490
         method: function(sParam) {
5491
            if(this._elThead) {
5492
                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
5493
                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_ASC) {
5494
                        allKeys[i]._elThLabel.firstChild.title = sParam;
5495
                    }
5496
                }
5497
            }
5498
         }
5499
     });
5500
 
5501
    /**
5502
     * @attribute MSG_SORTDESC
5503
     * @description Message to display in tooltip to sort Column in descending
5504
     * order. String values are treated as markup and inserted into the DOM as
5505
     * innerHTML.
5506
     * @type HTML
5507
     * @default "Click to sort descending"
5508
     */
5509
     this.setAttributeConfig("MSG_SORTDESC", {
5510
         value: "Click to sort descending",
5511
         validator: lang.isString,
5512
         method: function(sParam) {
5513
            if(this._elThead) {
5514
                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
5515
                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_DESC) {
5516
                        allKeys[i]._elThLabel.firstChild.title = sParam;
5517
                    }
5518
                }
5519
            }
5520
         }
5521
     });
5522
 
5523
    /**
5524
     * @attribute currencySymbol
5525
     * @deprecated Use currencyOptions.
5526
     */
5527
    this.setAttributeConfig("currencySymbol", {
5528
        value: "$",
5529
        validator: lang.isString
5530
    });
5531
 
5532
    /**
5533
     * Default config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
5534
     * @attribute currencyOptions
5535
     * @type Object
5536
     * @default {prefix: $, decimalPlaces:2, decimalSeparator:".", thousandsSeparator:","}
5537
     */
5538
    this.setAttributeConfig("currencyOptions", {
5539
        value: {
5540
            prefix: this.get("currencySymbol"), // TODO: deprecate currencySymbol
5541
            decimalPlaces:2,
5542
            decimalSeparator:".",
5543
            thousandsSeparator:","
5544
        }
5545
    });
5546
 
5547
    /**
5548
     * Default config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
5549
     * @attribute dateOptions
5550
     * @type Object
5551
     * @default {format:"%m/%d/%Y", locale:"en"}
5552
     */
5553
    this.setAttributeConfig("dateOptions", {
5554
        value: {format:"%m/%d/%Y", locale:"en"}
5555
    });
5556
 
5557
    /**
5558
     * Default config passed to YAHOO.util.Number.format() by the 'number' Column formatter.
5559
     * @attribute numberOptions
5560
     * @type Object
5561
     * @default {decimalPlaces:0, thousandsSeparator:","}
5562
     */
5563
    this.setAttributeConfig("numberOptions", {
5564
        value: {
5565
            decimalPlaces:0,
5566
            thousandsSeparator:","
5567
        }
5568
    });
5569
 
5570
},
5571
 
5572
/////////////////////////////////////////////////////////////////////////////
5573
//
5574
// Private member variables
5575
//
5576
/////////////////////////////////////////////////////////////////////////////
5577
 
5578
/**
5579
 * True if instance is initialized, so as to fire the initEvent after render.
5580
 *
5581
 * @property _bInit
5582
 * @type Boolean
5583
 * @default true
5584
 * @private
5585
 */
5586
_bInit : true,
5587
 
5588
/**
5589
 * Index assigned to instance.
5590
 *
5591
 * @property _nIndex
5592
 * @type Number
5593
 * @private
5594
 */
5595
_nIndex : null,
5596
 
5597
/**
5598
 * Counter for IDs assigned to TR elements.
5599
 *
5600
 * @property _nTrCount
5601
 * @type Number
5602
 * @private
5603
 */
5604
_nTrCount : 0,
5605
 
5606
/**
5607
 * Counter for IDs assigned to TD elements.
5608
 *
5609
 * @property _nTdCount
5610
 * @type Number
5611
 * @private
5612
 */
5613
_nTdCount : 0,
5614
 
5615
/**
5616
 * Unique id assigned to instance "yui-dtN", useful prefix for generating unique
5617
 * DOM ID strings and log messages.
5618
 *
5619
 * @property _sId
5620
 * @type String
5621
 * @private
5622
 */
5623
_sId : null,
5624
 
5625
/**
5626
 * Render chain.
5627
 *
5628
 * @property _oChainRender
5629
 * @type YAHOO.util.Chain
5630
 * @private
5631
 */
5632
_oChainRender : null,
5633
 
5634
/**
5635
 * DOM reference to the container element for the DataTable instance into which
5636
 * all other elements get created.
5637
 *
5638
 * @property _elContainer
5639
 * @type HTMLElement
5640
 * @private
5641
 */
5642
_elContainer : null,
5643
 
5644
/**
5645
 * DOM reference to the mask element for the DataTable instance which disables it.
5646
 *
5647
 * @property _elMask
5648
 * @type HTMLElement
5649
 * @private
5650
 */
5651
_elMask : null,
5652
 
5653
/**
5654
 * DOM reference to the TABLE element for the DataTable instance.
5655
 *
5656
 * @property _elTable
5657
 * @type HTMLElement
5658
 * @private
5659
 */
5660
_elTable : null,
5661
 
5662
/**
5663
 * DOM reference to the CAPTION element for the DataTable instance.
5664
 *
5665
 * @property _elCaption
5666
 * @type HTMLElement
5667
 * @private
5668
 */
5669
_elCaption : null,
5670
 
5671
/**
5672
 * DOM reference to the COLGROUP element for the DataTable instance.
5673
 *
5674
 * @property _elColgroup
5675
 * @type HTMLElement
5676
 * @private
5677
 */
5678
_elColgroup : null,
5679
 
5680
/**
5681
 * DOM reference to the THEAD element for the DataTable instance.
5682
 *
5683
 * @property _elThead
5684
 * @type HTMLElement
5685
 * @private
5686
 */
5687
_elThead : null,
5688
 
5689
/**
5690
 * DOM reference to the primary TBODY element for the DataTable instance.
5691
 *
5692
 * @property _elTbody
5693
 * @type HTMLElement
5694
 * @private
5695
 */
5696
_elTbody : null,
5697
 
5698
/**
5699
 * DOM reference to the secondary TBODY element used to display DataTable messages.
5700
 *
5701
 * @property _elMsgTbody
5702
 * @type HTMLElement
5703
 * @private
5704
 */
5705
_elMsgTbody : null,
5706
 
5707
/**
5708
 * DOM reference to the secondary TBODY element's single TR element used to display DataTable messages.
5709
 *
5710
 * @property _elMsgTr
5711
 * @type HTMLElement
5712
 * @private
5713
 */
5714
_elMsgTr : null,
5715
 
5716
/**
5717
 * DOM reference to the secondary TBODY element's single TD element used to display DataTable messages.
5718
 *
5719
 * @property _elMsgTd
5720
 * @type HTMLElement
5721
 * @private
5722
 */
5723
_elMsgTd : null,
5724
 
5725
/**
5726
 * Element reference to shared Column drag target.
5727
 *
5728
 * @property _elColumnDragTarget
5729
 * @type HTMLElement
5730
 * @private
5731
 */
5732
_elColumnDragTarget : null,
5733
 
5734
/**
5735
 * Element reference to shared Column resizer proxy.
5736
 *
5737
 * @property _elColumnResizerProxy
5738
 * @type HTMLElement
5739
 * @private
5740
 */
5741
_elColumnResizerProxy : null,
5742
 
5743
/**
5744
 * DataSource instance for the DataTable instance.
5745
 *
5746
 * @property _oDataSource
5747
 * @type YAHOO.util.DataSource
5748
 * @private
5749
 */
5750
_oDataSource : null,
5751
 
5752
/**
5753
 * ColumnSet instance for the DataTable instance.
5754
 *
5755
 * @property _oColumnSet
5756
 * @type YAHOO.widget.ColumnSet
5757
 * @private
5758
 */
5759
_oColumnSet : null,
5760
 
5761
/**
5762
 * RecordSet instance for the DataTable instance.
5763
 *
5764
 * @property _oRecordSet
5765
 * @type YAHOO.widget.RecordSet
5766
 * @private
5767
 */
5768
_oRecordSet : null,
5769
 
5770
/**
5771
 * The active CellEditor instance for the DataTable instance.
5772
 *
5773
 * @property _oCellEditor
5774
 * @type YAHOO.widget.CellEditor
5775
 * @private
5776
 */
5777
_oCellEditor : null,
5778
 
5779
/**
5780
 * ID string of first TR element of the current DataTable page.
5781
 *
5782
 * @property _sFirstTrId
5783
 * @type String
5784
 * @private
5785
 */
5786
_sFirstTrId : null,
5787
 
5788
/**
5789
 * ID string of the last TR element of the current DataTable page.
5790
 *
5791
 * @property _sLastTrId
5792
 * @type String
5793
 * @private
5794
 */
5795
_sLastTrId : null,
5796
 
5797
/**
5798
 * Template row to create all new rows from.
5799
 * @property _elTrTemplate
5800
 * @type {HTMLElement}
5801
 * @private
5802
 */
5803
_elTrTemplate : null,
5804
 
5805
/**
5806
 * Sparse array of custom functions to set column widths for browsers that don't
5807
 * support dynamic CSS rules.  Functions are added at the index representing
5808
 * the number of rows they update.
5809
 *
5810
 * @property _aDynFunctions
5811
 * @type Array
5812
 * @private
5813
 */
5814
_aDynFunctions : [],
5815
 
5816
/**
5817
 * Disabled state.
5818
 *
5819
 * @property _disabled
5820
 * @type Boolean
5821
 * @private
5822
 */
5823
_disabled : false,
5824
 
5825
 
5826
 
5827
 
5828
 
5829
 
5830
 
5831
 
5832
 
5833
 
5834
 
5835
 
5836
 
5837
 
5838
 
5839
 
5840
 
5841
 
5842
 
5843
 
5844
 
5845
 
5846
 
5847
 
5848
 
5849
 
5850
 
5851
 
5852
/////////////////////////////////////////////////////////////////////////////
5853
//
5854
// Private methods
5855
//
5856
/////////////////////////////////////////////////////////////////////////////
5857
 
5858
/**
5859
 * Clears browser text selection. Useful to call on rowSelectEvent or
5860
 * cellSelectEvent to prevent clicks or dblclicks from selecting text in the
5861
 * browser.
5862
 *
5863
 * @method clearTextSelection
5864
 */
5865
clearTextSelection : function() {
5866
    var sel;
5867
    if(window.getSelection) {
5868
        sel = window.getSelection();
5869
    }
5870
    else if(document.getSelection) {
5871
        sel = document.getSelection();
5872
    }
5873
    else if(document.selection) {
5874
        sel = document.selection;
5875
    }
5876
    if(sel) {
5877
        if(sel.empty) {
5878
            sel.empty();
5879
        }
5880
        else if (sel.removeAllRanges) {
5881
            sel.removeAllRanges();
5882
        }
5883
        else if(sel.collapse) {
5884
            sel.collapse();
5885
        }
5886
    }
5887
},
5888
 
5889
/**
5890
 * Sets focus on the given element.
5891
 *
5892
 * @method _focusEl
5893
 * @param el {HTMLElement} Element.
5894
 * @private
5895
 */
5896
_focusEl : function(el) {
5897
    el = el || this._elTbody;
5898
    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
5899
    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
5900
    // strange unexpected things as the user clicks on buttons and other controls.
5901
    setTimeout(function() {
5902
        try {
5903
            el.focus();
5904
        }
5905
        catch(e) {
5906
        }
5907
    },0);
5908
},
5909
 
5910
/**
5911
 * Forces Gecko repaint.
5912
 *
5913
 * @method _repaintGecko
5914
 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
5915
 * @private
5916
 */
5917
_repaintGecko : (ua.gecko) ?
5918
    function(el) {
5919
        el = el || this._elContainer;
5920
        var parent = el.parentNode;
5921
        var nextSibling = el.nextSibling;
5922
        parent.insertBefore(parent.removeChild(el), nextSibling);
5923
    } : function() {},
5924
 
5925
/**
5926
 * Forces Opera repaint.
5927
 *
5928
 * @method _repaintOpera
5929
 * @private
5930
 */
5931
_repaintOpera : (ua.opera) ?
5932
    function() {
5933
        if(ua.opera) {
5934
            document.documentElement.className += " ";
5935
            document.documentElement.className = YAHOO.lang.trim(document.documentElement.className);
5936
        }
5937
    } : function() {} ,
5938
 
5939
/**
5940
 * Forces Webkit repaint.
5941
 *
5942
 * @method _repaintWebkit
5943
 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
5944
 * @private
5945
 */
5946
_repaintWebkit : (ua.webkit) ?
5947
    function(el) {
5948
        el = el || this._elContainer;
5949
        var parent = el.parentNode;
5950
        var nextSibling = el.nextSibling;
5951
        parent.insertBefore(parent.removeChild(el), nextSibling);
5952
    } : function() {},
5953
 
5954
 
5955
 
5956
 
5957
 
5958
 
5959
 
5960
 
5961
 
5962
 
5963
 
5964
 
5965
 
5966
 
5967
 
5968
 
5969
 
5970
 
5971
 
5972
 
5973
 
5974
 
5975
// INIT FUNCTIONS
5976
 
5977
/**
5978
 * Initializes object literal of config values.
5979
 *
5980
 * @method _initConfigs
5981
 * @param oConfig {Object} Object literal of config values.
5982
 * @private
5983
 */
5984
_initConfigs : function(oConfigs) {
5985
    if(!oConfigs || !lang.isObject(oConfigs)) {
5986
        oConfigs = {};
5987
    }
5988
    this.configs = oConfigs;
5989
},
5990
 
5991
/**
5992
 * Initializes ColumnSet.
5993
 *
5994
 * @method _initColumnSet
5995
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
5996
 * @private
5997
 */
5998
_initColumnSet : function(aColumnDefs) {
5999
    var oColumn, i, len;
6000
 
6001
    if(this._oColumnSet) {
6002
        // First clear _oDynStyles for existing ColumnSet and
6003
        // uregister CellEditor Custom Events
6004
        for(i=0, len=this._oColumnSet.keys.length; i<len; i++) {
6005
            oColumn = this._oColumnSet.keys[i];
6006
            DT._oDynStyles["."+this.getId()+"-col-"+oColumn.getSanitizedKey()+" ."+DT.CLASS_LINER] = undefined;
6007
            if(oColumn.editor && oColumn.editor.unsubscribeAll) { // Backward compatibility
6008
                oColumn.editor.unsubscribeAll();
6009
            }
6010
        }
6011
 
6012
        this._oColumnSet = null;
6013
        this._clearTrTemplateEl();
6014
    }
6015
 
6016
    if(lang.isArray(aColumnDefs)) {
6017
        this._oColumnSet =  new YAHOO.widget.ColumnSet(aColumnDefs);
6018
    }
6019
    // Backward compatibility
6020
    else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) {
6021
        this._oColumnSet =  aColumnDefs;
6022
        YAHOO.log("DataTable's constructor now requires an array" +
6023
        " of object literal Column definitions instead of a ColumnSet instance",
6024
        "warn", this.toString());
6025
    }
6026
 
6027
    // Register CellEditor Custom Events
6028
    var allKeys = this._oColumnSet.keys;
6029
    for(i=0, len=allKeys.length; i<len; i++) {
6030
        oColumn = allKeys[i];
6031
        if(oColumn.editor && oColumn.editor.subscribe) { // Backward incompatibility
6032
            oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
6033
            oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
6034
            oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
6035
            oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
6036
            oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
6037
            oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
6038
            oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
6039
            oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
6040
        }
6041
    }
6042
},
6043
 
6044
/**
6045
 * Initializes DataSource.
6046
 *
6047
 * @method _initDataSource
6048
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
6049
 * @private
6050
 */
6051
_initDataSource : function(oDataSource) {
6052
    this._oDataSource = null;
6053
    if(oDataSource && (lang.isFunction(oDataSource.sendRequest))) {
6054
        this._oDataSource = oDataSource;
6055
    }
6056
    // Backward compatibility
6057
    else {
6058
        var tmpTable = null;
6059
        var tmpContainer = this._elContainer;
6060
        var i=0;
6061
        //TODO: this will break if re-initing DS at runtime for SDT
6062
        // Peek in container child nodes to see if TABLE already exists
6063
        if(tmpContainer.hasChildNodes()) {
6064
            var tmpChildren = tmpContainer.childNodes;
6065
            for(i=0; i<tmpChildren.length; i++) {
6066
                if(tmpChildren[i].nodeName && tmpChildren[i].nodeName.toLowerCase() == "table") {
6067
                    tmpTable = tmpChildren[i];
6068
                    break;
6069
                }
6070
            }
6071
            if(tmpTable) {
6072
                var tmpFieldsArray = [];
6073
                for(; i<this._oColumnSet.keys.length; i++) {
6074
                    tmpFieldsArray.push({key:this._oColumnSet.keys[i].key});
6075
                }
6076
 
6077
                this._oDataSource = new DS(tmpTable);
6078
                this._oDataSource.responseType = DS.TYPE_HTMLTABLE;
6079
                this._oDataSource.responseSchema = {fields: tmpFieldsArray};
6080
                YAHOO.log("Null DataSource for progressive enhancement from" +
6081
                " markup has been deprecated", "warn", this.toString());
6082
            }
6083
        }
6084
    }
6085
},
6086
 
6087
/**
6088
 * Initializes RecordSet.
6089
 *
6090
 * @method _initRecordSet
6091
 * @private
6092
 */
6093
_initRecordSet : function() {
6094
    if(this._oRecordSet) {
6095
        this._oRecordSet.reset();
6096
    }
6097
    else {
6098
        this._oRecordSet = new YAHOO.widget.RecordSet();
6099
    }
6100
},
6101
 
6102
/**
6103
 * Initializes DOM elements.
6104
 *
6105
 * @method _initDomElements
6106
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
6107
 * return {Boolean} False in case of error, otherwise true
6108
 * @private
6109
 */
6110
_initDomElements : function(elContainer) {
6111
    // Outer container
6112
    this._initContainerEl(elContainer);
6113
    // TABLE
6114
    this._initTableEl(this._elContainer);
6115
    // COLGROUP
6116
    this._initColgroupEl(this._elTable);
6117
    // THEAD
6118
    this._initTheadEl(this._elTable);
6119
 
6120
    // Message TBODY
6121
    this._initMsgTbodyEl(this._elTable);
6122
 
6123
    // Primary TBODY
6124
    this._initTbodyEl(this._elTable);
6125
 
6126
    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody) {
6127
        return false;
6128
    }
6129
    else {
6130
        return true;
6131
    }
6132
},
6133
 
6134
/**
6135
 * Destroy's the DataTable outer container element, if available.
6136
 *
6137
 * @method _destroyContainerEl
6138
 * @param elContainer {HTMLElement} Reference to the container element.
6139
 * @private
6140
 */
6141
_destroyContainerEl : function(elContainer) {
6142
        var columns = this._oColumnSet.keys,
6143
        elements, i;
6144
 
6145
        Dom.removeClass(elContainer, DT.CLASS_DATATABLE);
6146
 
6147
    // Bug 2528783
6148
    Ev.purgeElement( elContainer );
6149
    Ev.purgeElement( this._elThead, true ); // recursive to get resize handles
6150
    Ev.purgeElement( this._elTbody );
6151
    Ev.purgeElement( this._elMsgTbody );
6152
 
6153
    // because change doesn't bubble, each select (via formatDropdown) gets
6154
    // its own subscription
6155
    elements = elContainer.getElementsByTagName( 'select' );
6156
 
6157
    if ( elements.length ) {
6158
        Ev.detachListener( elements, 'change' );
6159
    }
6160
 
6161
    for ( i = columns.length - 1; i >= 0; --i ) {
6162
        if ( columns[i].editor ) {
6163
            Ev.purgeElement( columns[i].editor._elContainer );
6164
        }
6165
    }
6166
 
6167
    elContainer.innerHTML = "";
6168
 
6169
    this._elContainer = null;
6170
    this._elColgroup = null;
6171
    this._elThead = null;
6172
    this._elTbody = null;
6173
},
6174
 
6175
/**
6176
 * Initializes the DataTable outer container element, including a mask.
6177
 *
6178
 * @method _initContainerEl
6179
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
6180
 * @private
6181
 */
6182
_initContainerEl : function(elContainer) {
6183
    // Validate container
6184
    elContainer = Dom.get(elContainer);
6185
 
6186
    if(elContainer && elContainer.nodeName && (elContainer.nodeName.toLowerCase() == "div")) {
6187
        // Destroy previous
6188
        this._destroyContainerEl(elContainer);
6189
 
6190
        Dom.addClass(elContainer, DT.CLASS_DATATABLE);
6191
        Ev.addListener(elContainer, "focus", this._onTableFocus, this);
6192
        Ev.addListener(elContainer, "dblclick", this._onTableDblclick, this);
6193
        this._elContainer = elContainer;
6194
 
6195
        var elMask = document.createElement("div");
6196
        elMask.className = DT.CLASS_MASK;
6197
        elMask.style.display = "none";
6198
        this._elMask = elContainer.appendChild(elMask);
6199
    }
6200
},
6201
 
6202
/**
6203
 * Destroy's the DataTable TABLE element, if available.
6204
 *
6205
 * @method _destroyTableEl
6206
 * @private
6207
 */
6208
_destroyTableEl : function() {
6209
    var elTable = this._elTable;
6210
    if(elTable) {
6211
        Ev.purgeElement(elTable, true);
6212
        elTable.parentNode.removeChild(elTable);
6213
        this._elCaption = null;
6214
        this._elColgroup = null;
6215
        this._elThead = null;
6216
        this._elTbody = null;
6217
    }
6218
},
6219
 
6220
/**
6221
 * Creates HTML markup CAPTION element.
6222
 *
6223
 * @method _initCaptionEl
6224
 * @param sCaption {HTML} Caption value. String values are treated as markup and
6225
 * inserted into the DOM with innerHTML.
6226
 * @private
6227
 */
6228
_initCaptionEl : function(sCaption) {
6229
    if(this._elTable && sCaption) {
6230
        // Create CAPTION element
6231
        if(!this._elCaption) {
6232
            this._elCaption = this._elTable.createCaption();
6233
        }
6234
        // Set CAPTION value
6235
        this._elCaption.innerHTML = sCaption;
6236
    }
6237
    else if(this._elCaption) {
6238
        this._elCaption.parentNode.removeChild(this._elCaption);
6239
    }
6240
},
6241
 
6242
/**
6243
 * Creates HTML markup for TABLE, COLGROUP, THEAD and TBODY elements in outer
6244
 * container element.
6245
 *
6246
 * @method _initTableEl
6247
 * @param elContainer {HTMLElement} Container element into which to create TABLE.
6248
 * @private
6249
 */
6250
_initTableEl : function(elContainer) {
6251
    if(elContainer) {
6252
        // Destroy previous
6253
        this._destroyTableEl();
6254
 
6255
        // Create TABLE
6256
        this._elTable = elContainer.appendChild(document.createElement("table"));
6257
 
6258
        // Set SUMMARY attribute
6259
        this._elTable.summary = this.get("summary");
6260
 
6261
        // Create CAPTION element
6262
        if(this.get("caption")) {
6263
            this._initCaptionEl(this.get("caption"));
6264
        }
6265
 
6266
        // Set up mouseover/mouseout events via mouseenter/mouseleave delegation
6267
        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this);
6268
        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this);
6269
        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-data>tr>td", this);
6270
        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-data>tr>td", this);
6271
        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-message>tr>td", this);
6272
        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-message>tr>td", this);
6273
    }
6274
},
6275
 
6276
/**
6277
 * Destroy's the DataTable COLGROUP element, if available.
6278
 *
6279
 * @method _destroyColgroupEl
6280
 * @private
6281
 */
6282
_destroyColgroupEl : function() {
6283
    var elColgroup = this._elColgroup;
6284
    if(elColgroup) {
6285
        var elTable = elColgroup.parentNode;
6286
        Ev.purgeElement(elColgroup, true);
6287
        elTable.removeChild(elColgroup);
6288
        this._elColgroup = null;
6289
    }
6290
},
6291
 
6292
/**
6293
 * Initializes COLGROUP and COL elements for managing minWidth.
6294
 *
6295
 * @method _initColgroupEl
6296
 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
6297
 * @private
6298
 */
6299
_initColgroupEl : function(elTable) {
6300
    if(elTable) {
6301
        // Destroy previous
6302
        this._destroyColgroupEl();
6303
 
6304
        // Add COLs to DOCUMENT FRAGMENT
6305
        var allCols = this._aColIds || [],
6306
            allKeys = this._oColumnSet.keys,
6307
            i = 0, len = allCols.length,
6308
            elCol, oColumn,
6309
            elFragment = document.createDocumentFragment(),
6310
            elColTemplate = document.createElement("col");
6311
 
6312
        for(i=0,len=allKeys.length; i<len; i++) {
6313
            oColumn = allKeys[i];
6314
            elCol = elFragment.appendChild(elColTemplate.cloneNode(false));
6315
        }
6316
 
6317
        // Create COLGROUP
6318
        var elColgroup = elTable.insertBefore(document.createElement("colgroup"), elTable.firstChild);
6319
        elColgroup.appendChild(elFragment);
6320
        this._elColgroup = elColgroup;
6321
    }
6322
},
6323
 
6324
/**
6325
 * Adds a COL element to COLGROUP at given index.
6326
 *
6327
 * @method _insertColgroupColEl
6328
 * @param index {Number} Index of new COL element.
6329
 * @private
6330
 */
6331
_insertColgroupColEl : function(index) {
6332
    if(lang.isNumber(index)&& this._elColgroup) {
6333
        var nextSibling = this._elColgroup.childNodes[index] || null;
6334
        this._elColgroup.insertBefore(document.createElement("col"), nextSibling);
6335
    }
6336
},
6337
 
6338
/**
6339
 * Removes a COL element to COLGROUP at given index.
6340
 *
6341
 * @method _removeColgroupColEl
6342
 * @param index {Number} Index of removed COL element.
6343
 * @private
6344
 */
6345
_removeColgroupColEl : function(index) {
6346
    if(lang.isNumber(index) && this._elColgroup && this._elColgroup.childNodes[index]) {
6347
        this._elColgroup.removeChild(this._elColgroup.childNodes[index]);
6348
    }
6349
},
6350
 
6351
/**
6352
 * Reorders a COL element from old index(es) to new index.
6353
 *
6354
 * @method _reorderColgroupColEl
6355
 * @param aKeyIndexes {Number[]} Array of indexes of removed COL element.
6356
 * @param newIndex {Number} New index.
6357
 * @private
6358
 */
6359
_reorderColgroupColEl : function(aKeyIndexes, newIndex) {
6360
    if(lang.isArray(aKeyIndexes) && lang.isNumber(newIndex) && this._elColgroup && (this._elColgroup.childNodes.length > aKeyIndexes[aKeyIndexes.length-1])) {
6361
        var i,
6362
            tmpCols = [];
6363
        // Remove COL
6364
        for(i=aKeyIndexes.length-1; i>-1; i--) {
6365
            tmpCols.push(this._elColgroup.removeChild(this._elColgroup.childNodes[aKeyIndexes[i]]));
6366
        }
6367
        // Insert COL
6368
        var nextSibling = this._elColgroup.childNodes[newIndex] || null;
6369
        for(i=tmpCols.length-1; i>-1; i--) {
6370
            this._elColgroup.insertBefore(tmpCols[i], nextSibling);
6371
        }
6372
    }
6373
},
6374
 
6375
/**
6376
 * Destroy's the DataTable THEAD element, if available.
6377
 *
6378
 * @method _destroyTheadEl
6379
 * @private
6380
 */
6381
_destroyTheadEl : function() {
6382
    var elThead = this._elThead;
6383
    if(elThead) {
6384
        var elTable = elThead.parentNode;
6385
        Ev.purgeElement(elThead, true);
6386
        this._destroyColumnHelpers();
6387
        elTable.removeChild(elThead);
6388
        this._elThead = null;
6389
    }
6390
},
6391
 
6392
/**
6393
 * Initializes THEAD element.
6394
 *
6395
 * @method _initTheadEl
6396
 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
6397
 * @param {HTMLElement} Initialized THEAD element.
6398
 * @private
6399
 */
6400
_initTheadEl : function(elTable) {
6401
    elTable = elTable || this._elTable;
6402
 
6403
    if(elTable) {
6404
        // Destroy previous
6405
        this._destroyTheadEl();
6406
 
6407
        //TODO: append to DOM later for performance
6408
        var elThead = (this._elColgroup) ?
6409
            elTable.insertBefore(document.createElement("thead"), this._elColgroup.nextSibling) :
6410
            elTable.appendChild(document.createElement("thead"));
6411
 
6412
        // Set up DOM events for THEAD
6413
        Ev.addListener(elThead, "focus", this._onTheadFocus, this);
6414
        Ev.addListener(elThead, "keydown", this._onTheadKeydown, this);
6415
        Ev.addListener(elThead, "mousedown", this._onTableMousedown, this);
6416
        Ev.addListener(elThead, "mouseup", this._onTableMouseup, this);
6417
        Ev.addListener(elThead, "click", this._onTheadClick, this);
6418
 
6419
        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
6420
        // delegation at the TABLE level
6421
 
6422
        // Since we can't listen for click and dblclick on the same element...
6423
        // Attach separately to THEAD and TBODY
6424
        ///Ev.addListener(elThead, "dblclick", this._onTableDblclick, this);
6425
 
6426
       var oColumnSet = this._oColumnSet,
6427
            oColumn, i,j, l;
6428
 
6429
        // Add TRs to the THEAD
6430
        var colTree = oColumnSet.tree;
6431
        var elTh;
6432
        for(i=0; i<colTree.length; i++) {
6433
            var elTheadTr = elThead.appendChild(document.createElement("tr"));
6434
 
6435
            // ...and create TH cells
6436
            for(j=0; j<colTree[i].length; j++) {
6437
                oColumn = colTree[i][j];
6438
                elTh = elTheadTr.appendChild(document.createElement("th"));
6439
                this._initThEl(elTh,oColumn);
6440
            }
6441
 
6442
                // Set FIRST/LAST on THEAD rows
6443
                if(i === 0) {
6444
                    Dom.addClass(elTheadTr, DT.CLASS_FIRST);
6445
                }
6446
                if(i === (colTree.length-1)) {
6447
                    Dom.addClass(elTheadTr, DT.CLASS_LAST);
6448
                }
6449
 
6450
        }
6451
 
6452
        // Set FIRST/LAST on edge TH elements using the values in ColumnSet headers array
6453
        var aFirstHeaders = oColumnSet.headers[0] || [];
6454
        for(i=0; i<aFirstHeaders.length; i++) {
6455
            Dom.addClass(Dom.get(this.getId() +"-th-"+aFirstHeaders[i]), DT.CLASS_FIRST);
6456
        }
6457
        var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1] || [];
6458
        for(i=0; i<aLastHeaders.length; i++) {
6459
            Dom.addClass(Dom.get(this.getId() +"-th-"+aLastHeaders[i]), DT.CLASS_LAST);
6460
        }
6461
 
6462
        YAHOO.log("TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
6463
 
6464
        ///TODO: try _repaintGecko(this._elContainer) instead
6465
        // Bug 1806891
6466
        if(ua.webkit && ua.webkit < 420) {
6467
            var oSelf = this;
6468
            setTimeout(function() {
6469
                elThead.style.display = "";
6470
            },0);
6471
            elThead.style.display = 'none';
6472
        }
6473
 
6474
        this._elThead = elThead;
6475
 
6476
        // Column helpers needs _elThead to exist
6477
        this._initColumnHelpers();
6478
    }
6479
},
6480
 
6481
/**
6482
 * Populates TH element as defined by Column.
6483
 *
6484
 * @method _initThEl
6485
 * @param elTh {HTMLElement} TH element reference.
6486
 * @param oColumn {YAHOO.widget.Column} Column object.
6487
 * @private
6488
 */
6489
_initThEl : function(elTh, oColumn) {
6490
    elTh.id = this.getId() + "-th-" + oColumn.getSanitizedKey(); // Needed for accessibility, getColumn by TH, and ColumnDD
6491
    elTh.innerHTML = "";
6492
    elTh.rowSpan = oColumn.getRowspan();
6493
    elTh.colSpan = oColumn.getColspan();
6494
    oColumn._elTh = elTh;
6495
 
6496
    var elThLiner = elTh.appendChild(document.createElement("div"));
6497
    elThLiner.id = elTh.id + "-liner"; // Needed for resizer
6498
    elThLiner.className = DT.CLASS_LINER;
6499
    oColumn._elThLiner = elThLiner;
6500
 
6501
    var elThLabel = elThLiner.appendChild(document.createElement("span"));
6502
    elThLabel.className = DT.CLASS_LABEL;
6503
 
6504
    // Assign abbr attribute
6505
    if(oColumn.abbr) {
6506
        elTh.abbr = oColumn.abbr;
6507
    }
6508
    // Clear minWidth on hidden Columns
6509
    if(oColumn.hidden) {
6510
        this._clearMinWidth(oColumn);
6511
    }
6512
 
6513
    elTh.className = this._getColumnClassNames(oColumn);
6514
 
6515
    // Set Column width...
6516
    if(oColumn.width) {
6517
        // Validate minWidth
6518
        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
6519
                oColumn.minWidth : oColumn.width;
6520
        // ...for fallback cases
6521
        if(DT._bDynStylesFallback) {
6522
            elTh.firstChild.style.overflow = 'hidden';
6523
            elTh.firstChild.style.width = nWidth + 'px';
6524
        }
6525
        // ...for non fallback cases
6526
        else {
6527
            this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden');
6528
        }
6529
    }
6530
 
6531
    this.formatTheadCell(elThLabel, oColumn, this.get("sortedBy"));
6532
    oColumn._elThLabel = elThLabel;
6533
},
6534
 
6535
/**
6536
 * Outputs markup into the given TH based on given Column.
6537
 *
6538
 * @method formatTheadCell
6539
 * @param elCellLabel {HTMLElement} The label SPAN element within the TH liner,
6540
 * not the liner DIV element.
6541
 * @param oColumn {YAHOO.widget.Column} Column instance.
6542
 * @param oSortedBy {Object} Sort state object literal.
6543
*/
6544
formatTheadCell : function(elCellLabel, oColumn, oSortedBy) {
6545
    var sKey = oColumn.getKey();
6546
    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
6547
 
6548
    // Add accessibility link for sortable Columns
6549
    if(oColumn.sortable) {
6550
        // Calculate the direction
6551
        var sSortClass = this.getColumnSortDir(oColumn, oSortedBy);
6552
        var bDesc = (sSortClass === DT.CLASS_DESC);
6553
 
6554
        // This is the sorted Column
6555
        if(oSortedBy && (oColumn.key === oSortedBy.key)) {
6556
            bDesc = !(oSortedBy.dir === DT.CLASS_DESC);
6557
        }
6558
 
6559
        // Generate a unique HREF for visited status
6560
        var sHref = this.getId() + "-href-" + oColumn.getSanitizedKey();
6561
 
6562
        // Generate a dynamic TITLE for sort status
6563
        var sTitle = (bDesc) ? this.get("MSG_SORTDESC") : this.get("MSG_SORTASC");
6564
 
6565
        // Format the element
6566
        elCellLabel.innerHTML = "<a href=\"" + sHref + "\" title=\"" + sTitle + "\" class=\"" + DT.CLASS_SORTABLE + "\">" + sLabel + "</a>";
6567
    }
6568
    // Just display the label for non-sortable Columns
6569
    else {
6570
        elCellLabel.innerHTML = sLabel;
6571
    }
6572
},
6573
 
6574
/**
6575
 * Disables DD from top-level Column TH elements.
6576
 *
6577
 * @method _destroyDraggableColumns
6578
 * @private
6579
 */
6580
_destroyDraggableColumns : function() {
6581
    var oColumn, elTh;
6582
    for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
6583
        oColumn = this._oColumnSet.tree[0][i];
6584
        if(oColumn._dd) {
6585
            oColumn._dd = oColumn._dd.unreg();
6586
            Dom.removeClass(oColumn.getThEl(), DT.CLASS_DRAGGABLE);
6587
        }
6588
    }
6589
 
6590
    // Destroy column drag proxy
6591
    this._destroyColumnDragTargetEl();
6592
},
6593
 
6594
/**
6595
 * Initializes top-level Column TH elements into DD instances.
6596
 *
6597
 * @method _initDraggableColumns
6598
 * @private
6599
 */
6600
_initDraggableColumns : function() {
6601
    this._destroyDraggableColumns();
6602
    if(util.DD) {
6603
        var oColumn, elTh, elDragTarget;
6604
        for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
6605
            oColumn = this._oColumnSet.tree[0][i];
6606
            elTh = oColumn.getThEl();
6607
            Dom.addClass(elTh, DT.CLASS_DRAGGABLE);
6608
            elDragTarget = this._initColumnDragTargetEl();
6609
            oColumn._dd = new YAHOO.widget.ColumnDD(this, oColumn, elTh, elDragTarget);
6610
        }
6611
    }
6612
    else {
6613
        YAHOO.log("Could not find DragDrop for draggable Columns", "warn", this.toString());
6614
    }
6615
},
6616
 
6617
/**
6618
 * Destroys shared Column drag target.
6619
 *
6620
 * @method _destroyColumnDragTargetEl
6621
 * @private
6622
 */
6623
_destroyColumnDragTargetEl : function() {
6624
    if(this._elColumnDragTarget) {
6625
        var el = this._elColumnDragTarget;
6626
        YAHOO.util.Event.purgeElement(el);
6627
        el.parentNode.removeChild(el);
6628
        this._elColumnDragTarget = null;
6629
    }
6630
},
6631
 
6632
/**
6633
 * Creates HTML markup for shared Column drag target.
6634
 *
6635
 * @method _initColumnDragTargetEl
6636
 * @return {HTMLElement} Reference to Column drag target.
6637
 * @private
6638
 */
6639
_initColumnDragTargetEl : function() {
6640
    if(!this._elColumnDragTarget) {
6641
        // Attach Column drag target element as first child of body
6642
        var elColumnDragTarget = document.createElement('div');
6643
        elColumnDragTarget.id = this.getId() + "-coltarget";
6644
        elColumnDragTarget.className = DT.CLASS_COLTARGET;
6645
        elColumnDragTarget.style.display = "none";
6646
        document.body.insertBefore(elColumnDragTarget, document.body.firstChild);
6647
 
6648
        // Internal tracker of Column drag target
6649
        this._elColumnDragTarget = elColumnDragTarget;
6650
 
6651
    }
6652
    return this._elColumnDragTarget;
6653
},
6654
 
6655
/**
6656
 * Disables resizeability on key Column TH elements.
6657
 *
6658
 * @method _destroyResizeableColumns
6659
 * @private
6660
 */
6661
_destroyResizeableColumns : function() {
6662
    var aKeys = this._oColumnSet.keys;
6663
    for(var i=0, len=aKeys.length; i<len; i++) {
6664
        if(aKeys[i]._ddResizer) {
6665
            aKeys[i]._ddResizer = aKeys[i]._ddResizer.unreg();
6666
            Dom.removeClass(aKeys[i].getThEl(), DT.CLASS_RESIZEABLE);
6667
        }
6668
    }
6669
 
6670
    // Destroy resizer proxy
6671
    this._destroyColumnResizerProxyEl();
6672
},
6673
 
6674
/**
6675
 * Initializes resizeability on key Column TH elements.
6676
 *
6677
 * @method _initResizeableColumns
6678
 * @private
6679
 */
6680
_initResizeableColumns : function() {
6681
    this._destroyResizeableColumns();
6682
    if(util.DD) {
6683
        var oColumn, elTh, elThLiner, elThResizerLiner, elThResizer, elResizerProxy, cancelClick;
6684
        for(var i=0, len=this._oColumnSet.keys.length; i<len; i++) {
6685
            oColumn = this._oColumnSet.keys[i];
6686
            if(oColumn.resizeable) {
6687
                elTh = oColumn.getThEl();
6688
                Dom.addClass(elTh, DT.CLASS_RESIZEABLE);
6689
                elThLiner = oColumn.getThLinerEl();
6690
 
6691
                // Bug 1915349: So resizer is as tall as TH when rowspan > 1
6692
                // Create a separate resizer liner with position:relative
6693
                elThResizerLiner = elTh.appendChild(document.createElement("div"));
6694
                elThResizerLiner.className = DT.CLASS_RESIZERLINER;
6695
 
6696
                // Move TH contents into the new resizer liner
6697
                elThResizerLiner.appendChild(elThLiner);
6698
 
6699
                // Create the resizer
6700
                elThResizer = elThResizerLiner.appendChild(document.createElement("div"));
6701
                elThResizer.id = elTh.id + "-resizer"; // Needed for ColumnResizer
6702
                elThResizer.className = DT.CLASS_RESIZER;
6703
                oColumn._elResizer = elThResizer;
6704
 
6705
                // Create the resizer proxy, once per instance
6706
                elResizerProxy = this._initColumnResizerProxyEl();
6707
                oColumn._ddResizer = new YAHOO.util.ColumnResizer(
6708
                        this, oColumn, elTh, elThResizer, elResizerProxy);
6709
                cancelClick = function(e) {
6710
                    Ev.stopPropagation(e);
6711
                };
6712
                Ev.addListener(elThResizer,"click",cancelClick);
6713
            }
6714
        }
6715
    }
6716
    else {
6717
        YAHOO.log("Could not find DragDrop for resizeable Columns", "warn", this.toString());
6718
    }
6719
},
6720
 
6721
/**
6722
 * Destroys shared Column resizer proxy.
6723
 *
6724
 * @method _destroyColumnResizerProxyEl
6725
 * @return {HTMLElement} Reference to Column resizer proxy.
6726
 * @private
6727
 */
6728
_destroyColumnResizerProxyEl : function() {
6729
    if(this._elColumnResizerProxy) {
6730
        var el = this._elColumnResizerProxy;
6731
        YAHOO.util.Event.purgeElement(el);
6732
        el.parentNode.removeChild(el);
6733
        this._elColumnResizerProxy = null;
6734
    }
6735
},
6736
 
6737
/**
6738
 * Creates HTML markup for shared Column resizer proxy.
6739
 *
6740
 * @method _initColumnResizerProxyEl
6741
 * @return {HTMLElement} Reference to Column resizer proxy.
6742
 * @private
6743
 */
6744
_initColumnResizerProxyEl : function() {
6745
    if(!this._elColumnResizerProxy) {
6746
        // Attach Column resizer element as first child of body
6747
        var elColumnResizerProxy = document.createElement("div");
6748
        elColumnResizerProxy.id = this.getId() + "-colresizerproxy"; // Needed for ColumnResizer
6749
        elColumnResizerProxy.className = DT.CLASS_RESIZERPROXY;
6750
        document.body.insertBefore(elColumnResizerProxy, document.body.firstChild);
6751
 
6752
        // Internal tracker of Column resizer proxy
6753
        this._elColumnResizerProxy = elColumnResizerProxy;
6754
    }
6755
    return this._elColumnResizerProxy;
6756
},
6757
 
6758
/**
6759
 * Destroys elements associated with Column functionality: ColumnDD and ColumnResizers.
6760
 *
6761
 * @method _destroyColumnHelpers
6762
 * @private
6763
 */
6764
_destroyColumnHelpers : function() {
6765
    this._destroyDraggableColumns();
6766
    this._destroyResizeableColumns();
6767
},
6768
 
6769
/**
6770
 * Initializes elements associated with Column functionality: ColumnDD and ColumnResizers.
6771
 *
6772
 * @method _initColumnHelpers
6773
 * @private
6774
 */
6775
_initColumnHelpers : function() {
6776
    if(this.get("draggableColumns")) {
6777
        this._initDraggableColumns();
6778
    }
6779
    this._initResizeableColumns();
6780
},
6781
 
6782
/**
6783
 * Destroy's the DataTable TBODY element, if available.
6784
 *
6785
 * @method _destroyTbodyEl
6786
 * @private
6787
 */
6788
_destroyTbodyEl : function() {
6789
    var elTbody = this._elTbody;
6790
    if(elTbody) {
6791
        var elTable = elTbody.parentNode;
6792
        Ev.purgeElement(elTbody, true);
6793
        elTable.removeChild(elTbody);
6794
        this._elTbody = null;
6795
    }
6796
},
6797
 
6798
/**
6799
 * Initializes TBODY element for data.
6800
 *
6801
 * @method _initTbodyEl
6802
 * @param elTable {HTMLElement} TABLE element into which to create TBODY .
6803
 * @private
6804
 */
6805
_initTbodyEl : function(elTable) {
6806
    if(elTable) {
6807
        // Destroy previous
6808
        this._destroyTbodyEl();
6809
 
6810
        // Create TBODY
6811
        var elTbody = elTable.appendChild(document.createElement("tbody"));
6812
        elTbody.tabIndex = 0;
6813
        elTbody.className = DT.CLASS_DATA;
6814
 
6815
        // Set up DOM events for TBODY
6816
        Ev.addListener(elTbody, "focus", this._onTbodyFocus, this);
6817
        Ev.addListener(elTbody, "mousedown", this._onTableMousedown, this);
6818
        Ev.addListener(elTbody, "mouseup", this._onTableMouseup, this);
6819
        Ev.addListener(elTbody, "keydown", this._onTbodyKeydown, this);
6820
        Ev.addListener(elTbody, "click", this._onTbodyClick, this);
6821
 
6822
        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
6823
        // delegation at the TABLE level
6824
 
6825
        // Since we can't listen for click and dblclick on the same element...
6826
        // Attach separately to THEAD and TBODY
6827
        ///Ev.addListener(elTbody, "dblclick", this._onTableDblclick, this);
6828
 
6829
 
6830
        // IE puts focus outline in the wrong place
6831
        if(ua.ie) {
6832
            elTbody.hideFocus=true;
6833
        }
6834
 
6835
        this._elTbody = elTbody;
6836
    }
6837
},
6838
 
6839
/**
6840
 * Destroy's the DataTable message TBODY element, if available.
6841
 *
6842
 * @method _destroyMsgTbodyEl
6843
 * @private
6844
 */
6845
_destroyMsgTbodyEl : function() {
6846
    var elMsgTbody = this._elMsgTbody;
6847
    if(elMsgTbody) {
6848
        var elTable = elMsgTbody.parentNode;
6849
        Ev.purgeElement(elMsgTbody, true);
6850
        elTable.removeChild(elMsgTbody);
6851
        this._elTbody = null;
6852
    }
6853
},
6854
 
6855
/**
6856
 * Initializes TBODY element for messaging.
6857
 *
6858
 * @method _initMsgTbodyEl
6859
 * @param elTable {HTMLElement} TABLE element into which to create TBODY
6860
 * @private
6861
 */
6862
_initMsgTbodyEl : function(elTable) {
6863
    if(elTable) {
6864
        var elMsgTbody = document.createElement("tbody");
6865
        elMsgTbody.className = DT.CLASS_MESSAGE;
6866
        var elMsgTr = elMsgTbody.appendChild(document.createElement("tr"));
6867
        elMsgTr.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
6868
        this._elMsgTr = elMsgTr;
6869
        var elMsgTd = elMsgTr.appendChild(document.createElement("td"));
6870
        elMsgTd.colSpan = this._oColumnSet.keys.length || 1;
6871
        elMsgTd.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
6872
        this._elMsgTd = elMsgTd;
6873
        elMsgTbody = elTable.insertBefore(elMsgTbody, this._elTbody);
6874
        var elMsgLiner = elMsgTd.appendChild(document.createElement("div"));
6875
        elMsgLiner.className = DT.CLASS_LINER;
6876
        this._elMsgTbody = elMsgTbody;
6877
 
6878
        // Set up DOM events for TBODY
6879
        Ev.addListener(elMsgTbody, "focus", this._onTbodyFocus, this);
6880
        Ev.addListener(elMsgTbody, "mousedown", this._onTableMousedown, this);
6881
        Ev.addListener(elMsgTbody, "mouseup", this._onTableMouseup, this);
6882
        Ev.addListener(elMsgTbody, "keydown", this._onTbodyKeydown, this);
6883
        Ev.addListener(elMsgTbody, "click", this._onTbodyClick, this);
6884
 
6885
        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
6886
        // delegation at the TABLE level
6887
    }
6888
},
6889
 
6890
/**
6891
 * Initialize internal event listeners
6892
 *
6893
 * @method _initEvents
6894
 * @private
6895
 */
6896
_initEvents : function () {
6897
    // Initialize Column sort
6898
    this._initColumnSort();
6899
 
6900
    // Add the document level click listener
6901
    YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this);
6902
 
6903
    // Paginator integration
6904
    this.subscribe("paginatorChange",function () {
6905
        this._handlePaginatorChange.apply(this,arguments);
6906
    });
6907
 
6908
    this.subscribe("initEvent",function () {
6909
        this.renderPaginator();
6910
    });
6911
 
6912
    // Initialize CellEditor integration
6913
    this._initCellEditing();
6914
},
6915
 
6916
/**
6917
  * Initializes Column sorting.
6918
  *
6919
  * @method _initColumnSort
6920
  * @private
6921
  */
6922
_initColumnSort : function() {
6923
    this.subscribe("theadCellClickEvent", this.onEventSortColumn);
6924
 
6925
    // Backward compatibility
6926
    var oSortedBy = this.get("sortedBy");
6927
    if(oSortedBy) {
6928
        if(oSortedBy.dir == "desc") {
6929
            this._configs.sortedBy.value.dir = DT.CLASS_DESC;
6930
        }
6931
        else if(oSortedBy.dir == "asc") {
6932
            this._configs.sortedBy.value.dir = DT.CLASS_ASC;
6933
        }
6934
    }
6935
},
6936
 
6937
/**
6938
  * Initializes CellEditor integration.
6939
  *
6940
  * @method _initCellEditing
6941
  * @private
6942
  */
6943
_initCellEditing : function() {
6944
    this.subscribe("editorBlurEvent",function () {
6945
        this.onEditorBlurEvent.apply(this,arguments);
6946
    });
6947
    this.subscribe("editorBlockEvent",function () {
6948
        this.onEditorBlockEvent.apply(this,arguments);
6949
    });
6950
    this.subscribe("editorUnblockEvent",function () {
6951
        this.onEditorUnblockEvent.apply(this,arguments);
6952
    });
6953
},
6954
 
6955
 
6956
 
6957
 
6958
 
6959
 
6960
 
6961
 
6962
 
6963
 
6964
 
6965
 
6966
 
6967
 
6968
 
6969
 
6970
 
6971
 
6972
 
6973
 
6974
 
6975
 
6976
 
6977
 
6978
 
6979
 
6980
 
6981
 
6982
 
6983
 
6984
 
6985
 
6986
 
6987
// DOM MUTATION FUNCTIONS
6988
 
6989
/**
6990
 * Retruns classnames to represent current Column states.
6991
 * @method _getColumnClassnames
6992
 * @param oColumn {YAHOO.widget.Column} Column instance.
6993
 * @param aAddClasses {String[]} An array of additional classnames to add to the
6994
 * return value.
6995
 * @return {String} A String of classnames to be assigned to TH or TD elements
6996
 * for given Column.
6997
 * @private
6998
 */
6999
_getColumnClassNames : function (oColumn, aAddClasses) {
7000
    var allClasses;
7001
 
7002
    // Add CSS classes
7003
    if(lang.isString(oColumn.className)) {
7004
        // Single custom class
7005
        allClasses = [oColumn.className];
7006
    }
7007
    else if(lang.isArray(oColumn.className)) {
7008
        // Array of custom classes
7009
        allClasses = oColumn.className;
7010
    }
7011
    else {
7012
        // no custom classes
7013
        allClasses = [];
7014
    }
7015
 
7016
    // Hook for setting width with via dynamic style uses key since ID is too disposable
7017
    allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey();
7018
 
7019
    // Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
7020
    allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
7021
 
7022
    var isSortedBy = this.get("sortedBy") || {};
7023
    // Sorted
7024
    if(oColumn.key === isSortedBy.key) {
7025
        allClasses[allClasses.length] = isSortedBy.dir || '';
7026
    }
7027
    // Hidden
7028
    if(oColumn.hidden) {
7029
        allClasses[allClasses.length] = DT.CLASS_HIDDEN;
7030
    }
7031
    // Selected
7032
    if(oColumn.selected) {
7033
        allClasses[allClasses.length] = DT.CLASS_SELECTED;
7034
    }
7035
    // Sortable
7036
    if(oColumn.sortable) {
7037
        allClasses[allClasses.length] = DT.CLASS_SORTABLE;
7038
    }
7039
    // Resizeable
7040
    if(oColumn.resizeable) {
7041
        allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
7042
    }
7043
    // Editable
7044
    if(oColumn.editor) {
7045
        allClasses[allClasses.length] = DT.CLASS_EDITABLE;
7046
    }
7047
 
7048
    // Addtnl classes, including First/Last
7049
    if(aAddClasses) {
7050
        allClasses = allClasses.concat(aAddClasses);
7051
    }
7052
 
7053
    return allClasses.join(' ');
7054
},
7055
 
7056
/**
7057
 * Clears TR element template in response to any Column state change.
7058
 * @method _clearTrTemplateEl
7059
 * @private
7060
 */
7061
_clearTrTemplateEl : function () {
7062
    this._elTrTemplate = null;
7063
},
7064
 
7065
/**
7066
 * Returns a new TR element template with TD elements classed with current
7067
 * Column states.
7068
 * @method _getTrTemplateEl
7069
 * @return {HTMLElement} A TR element to be cloned and added to the DOM.
7070
 * @private
7071
 */
7072
_getTrTemplateEl : function (oRecord, index) {
7073
    // Template is already available
7074
    if(this._elTrTemplate) {
7075
        return this._elTrTemplate;
7076
    }
7077
    // Template needs to be created
7078
    else {
7079
        var d   = document,
7080
            tr  = d.createElement('tr'),
7081
            td  = d.createElement('td'),
7082
            div = d.createElement('div');
7083
 
7084
        // Append the liner element
7085
        td.appendChild(div);
7086
 
7087
        // Create TD elements into DOCUMENT FRAGMENT
7088
        var df = document.createDocumentFragment(),
7089
            allKeys = this._oColumnSet.keys,
7090
            elTd;
7091
 
7092
        // Set state for each TD;
7093
        var aAddClasses;
7094
        for(var i=0, keysLen=allKeys.length; i<keysLen; i++) {
7095
            // Clone the TD template
7096
            elTd = td.cloneNode(true);
7097
 
7098
            // Format the base TD
7099
            elTd = this._formatTdEl(allKeys[i], elTd, i, (i===keysLen-1));
7100
 
7101
            df.appendChild(elTd);
7102
        }
7103
        tr.appendChild(df);
7104
        tr.className = DT.CLASS_REC;
7105
        this._elTrTemplate = tr;
7106
        return tr;
7107
    }
7108
},
7109
 
7110
/**
7111
 * Formats a basic TD element.
7112
 * @method _formatTdEl
7113
 * @param oColumn {YAHOO.widget.Column} Associated Column instance.
7114
 * @param elTd {HTMLElement} An unformatted TD element.
7115
 * @param index {Number} Column key index.
7116
 * @param isLast {Boolean} True if Column is last key of the ColumnSet.
7117
 * @return {HTMLElement} A formatted TD element.
7118
 * @private
7119
 */
7120
_formatTdEl : function (oColumn, elTd, index, isLast) {
7121
    var oColumnSet = this._oColumnSet;
7122
 
7123
    // Set the TD's accessibility headers
7124
    var allHeaders = oColumnSet.headers,
7125
        allColHeaders = allHeaders[index],
7126
        sTdHeaders = "",
7127
        sHeader;
7128
    for(var j=0, headersLen=allColHeaders.length; j < headersLen; j++) {
7129
        sHeader = this._sId + "-th-" + allColHeaders[j] + ' ';
7130
        sTdHeaders += sHeader;
7131
    }
7132
    elTd.headers = sTdHeaders;
7133
 
7134
    // Class the TD element
7135
    var aAddClasses = [];
7136
    if(index === 0) {
7137
        aAddClasses[aAddClasses.length] = DT.CLASS_FIRST;
7138
    }
7139
    if(isLast) {
7140
        aAddClasses[aAddClasses.length] = DT.CLASS_LAST;
7141
    }
7142
    elTd.className = this._getColumnClassNames(oColumn, aAddClasses);
7143
 
7144
    // Class the liner element
7145
    elTd.firstChild.className = DT.CLASS_LINER;
7146
 
7147
    // Set Column width for fallback cases
7148
    if(oColumn.width && DT._bDynStylesFallback) {
7149
        // Validate minWidth
7150
        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
7151
                oColumn.minWidth : oColumn.width;
7152
        elTd.firstChild.style.overflow = 'hidden';
7153
        elTd.firstChild.style.width = nWidth + 'px';
7154
    }
7155
 
7156
    return elTd;
7157
},
7158
 
7159
 
7160
/**
7161
 * Create a new TR element for a given Record and appends it with the correct
7162
 * number of Column-state-classed TD elements. Striping is the responsibility of
7163
 * the calling function, which may decide to stripe the single row, a subset of
7164
 * rows, or all the rows.
7165
 * @method _createTrEl
7166
 * @param oRecord {YAHOO.widget.Record} Record instance
7167
 * @return {HTMLElement} The new TR element.  This must be added to the DOM.
7168
 * @private
7169
 */
7170
_addTrEl : function (oRecord) {
7171
    var elTrTemplate = this._getTrTemplateEl();
7172
 
7173
    // Clone the TR template.
7174
    var elTr = elTrTemplate.cloneNode(true);
7175
 
7176
    // Populate content
7177
    return this._updateTrEl(elTr,oRecord);
7178
},
7179
 
7180
/**
7181
 * Formats the contents of the given TR's TD elements with data from the given
7182
 * Record. Only innerHTML should change, nothing structural.
7183
 *
7184
 * @method _updateTrEl
7185
 * @param elTr {HTMLElement} The TR element to update.
7186
 * @param oRecord {YAHOO.widget.Record} The associated Record instance.
7187
 * @return {HTMLElement} DOM reference to the new TR element.
7188
 * @private
7189
 */
7190
_updateTrEl : function(elTr, oRecord) {
7191
    var ok = this.get("formatRow") ? this.get("formatRow").call(this, elTr, oRecord) : true;
7192
    if(ok) {
7193
        // Hide the row to prevent constant reflows
7194
        elTr.style.display = 'none';
7195
 
7196
        // Update TD elements with new data
7197
        var allTds = elTr.childNodes,
7198
            elTd;
7199
        for(var i=0,len=allTds.length; i<len; ++i) {
7200
            elTd = allTds[i];
7201
 
7202
            // Set the cell content
7203
            this.formatCell(allTds[i].firstChild, oRecord, this._oColumnSet.keys[i]);
7204
        }
7205
 
7206
        // Redisplay the row for reflow
7207
        elTr.style.display = '';
7208
    }
7209
 
7210
     // Record-to-TR association and tracking of FIRST/LAST
7211
    var oldId = elTr.id,
7212
        newId = oRecord.getId();
7213
    if(this._sFirstTrId === oldId) {
7214
        this._sFirstTrId = newId;
7215
    }
7216
    if(this._sLastTrId === oldId) {
7217
        this._sLastTrId = newId;
7218
    }
7219
    elTr.id = newId;
7220
    return elTr;
7221
},
7222
 
7223
 
7224
/**
7225
 * Deletes TR element by DOM reference or by DataTable page row index.
7226
 *
7227
 * @method _deleteTrEl
7228
 * @param row {HTMLElement | Number} TR element reference or Datatable page row index.
7229
 * @return {Boolean} Returns true if successful, else returns false.
7230
 * @private
7231
 */
7232
_deleteTrEl : function(row) {
7233
    var rowIndex;
7234
 
7235
    // Get page row index for the element
7236
    if(!lang.isNumber(row)) {
7237
        rowIndex = Dom.get(row).sectionRowIndex;
7238
    }
7239
    else {
7240
        rowIndex = row;
7241
    }
7242
    if(lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
7243
        // Cannot use tbody.deleteRow due to IE6 instability
7244
        //return this._elTbody.deleteRow(rowIndex);
7245
        return this._elTbody.removeChild(this._elTbody.rows[row]);
7246
    }
7247
    else {
7248
        return null;
7249
    }
7250
},
7251
 
7252
 
7253
 
7254
 
7255
 
7256
 
7257
 
7258
 
7259
 
7260
 
7261
 
7262
 
7263
 
7264
 
7265
 
7266
 
7267
 
7268
 
7269
 
7270
 
7271
 
7272
 
7273
 
7274
 
7275
 
7276
 
7277
 
7278
// CSS/STATE FUNCTIONS
7279
 
7280
 
7281
 
7282
 
7283
/**
7284
 * Removes the class YAHOO.widget.DataTable.CLASS_FIRST from the first TR element
7285
 * of the DataTable page and updates internal tracker.
7286
 *
7287
 * @method _unsetFirstRow
7288
 * @private
7289
 */
7290
_unsetFirstRow : function() {
7291
    // Remove FIRST
7292
    if(this._sFirstTrId) {
7293
        Dom.removeClass(this._sFirstTrId, DT.CLASS_FIRST);
7294
        this._sFirstTrId = null;
7295
    }
7296
},
7297
 
7298
/**
7299
 * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element
7300
 * of the DataTable page and updates internal tracker.
7301
 *
7302
 * @method _setFirstRow
7303
 * @private
7304
 */
7305
_setFirstRow : function() {
7306
    this._unsetFirstRow();
7307
    var elTr = this.getFirstTrEl();
7308
    if(elTr) {
7309
        // Set FIRST
7310
        Dom.addClass(elTr, DT.CLASS_FIRST);
7311
        this._sFirstTrId = elTr.id;
7312
    }
7313
},
7314
 
7315
/**
7316
 * Removes the class YAHOO.widget.DataTable.CLASS_LAST from the last TR element
7317
 * of the DataTable page and updates internal tracker.
7318
 *
7319
 * @method _unsetLastRow
7320
 * @private
7321
 */
7322
_unsetLastRow : function() {
7323
    // Unassign previous class
7324
    if(this._sLastTrId) {
7325
        Dom.removeClass(this._sLastTrId, DT.CLASS_LAST);
7326
        this._sLastTrId = null;
7327
    }
7328
},
7329
 
7330
/**
7331
 * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element
7332
 * of the DataTable page and updates internal tracker.
7333
 *
7334
 * @method _setLastRow
7335
 * @private
7336
 */
7337
_setLastRow : function() {
7338
    this._unsetLastRow();
7339
    var elTr = this.getLastTrEl();
7340
    if(elTr) {
7341
        // Assign class
7342
        Dom.addClass(elTr, DT.CLASS_LAST);
7343
        this._sLastTrId = elTr.id;
7344
    }
7345
},
7346
 
7347
/**
7348
 * Assigns the classes DT.CLASS_EVEN and DT.CLASS_ODD to one, many, or all TR elements.
7349
 *
7350
 * @method _setRowStripes
7351
 * @param row {HTMLElement | String | Number} (optional) HTML TR element reference
7352
 * or string ID, or page row index of where to start striping.
7353
 * @param range {Number} (optional) If given, how many rows to stripe, otherwise
7354
 * stripe all the rows until the end.
7355
 * @private
7356
 */
7357
_setRowStripes : function(row, range) {
7358
    // Default values stripe all rows
7359
    var allRows = this._elTbody.rows,
7360
        nStartIndex = 0,
7361
        nEndIndex = allRows.length,
7362
        aOdds = [], nOddIdx = 0,
7363
        aEvens = [], nEvenIdx = 0;
7364
 
7365
    // Stripe a subset
7366
    if((row !== null) && (row !== undefined)) {
7367
        // Validate given start row
7368
        var elStartRow = this.getTrEl(row);
7369
        if(elStartRow) {
7370
            nStartIndex = elStartRow.sectionRowIndex;
7371
 
7372
            // Validate given range
7373
            if(lang.isNumber(range) && (range > 1)) {
7374
                nEndIndex = nStartIndex + range;
7375
            }
7376
        }
7377
    }
7378
 
7379
    for(var i=nStartIndex; i<nEndIndex; i++) {
7380
        if(i%2) {
7381
            aOdds[nOddIdx++] = allRows[i];
7382
        } else {
7383
            aEvens[nEvenIdx++] = allRows[i];
7384
        }
7385
    }
7386
 
7387
    if (aOdds.length) {
7388
        Dom.replaceClass(aOdds, DT.CLASS_EVEN, DT.CLASS_ODD);
7389
    }
7390
 
7391
    if (aEvens.length) {
7392
        Dom.replaceClass(aEvens, DT.CLASS_ODD, DT.CLASS_EVEN);
7393
    }
7394
},
7395
 
7396
/**
7397
 * Assigns the class DT.CLASS_SELECTED to TR and TD elements.
7398
 *
7399
 * @method _setSelections
7400
 * @private
7401
 */
7402
_setSelections : function() {
7403
    // Keep track of selected rows
7404
    var allSelectedRows = this.getSelectedRows();
7405
    // Keep track of selected cells
7406
    var allSelectedCells = this.getSelectedCells();
7407
    // Anything to select?
7408
    if((allSelectedRows.length>0) || (allSelectedCells.length > 0)) {
7409
        var oColumnSet = this._oColumnSet,
7410
            el;
7411
        // Loop over each row
7412
        for(var i=0; i<allSelectedRows.length; i++) {
7413
            el = Dom.get(allSelectedRows[i]);
7414
            if(el) {
7415
                Dom.addClass(el, DT.CLASS_SELECTED);
7416
            }
7417
        }
7418
        // Loop over each cell
7419
        for(i=0; i<allSelectedCells.length; i++) {
7420
            el = Dom.get(allSelectedCells[i].recordId);
7421
            if(el) {
7422
                Dom.addClass(el.childNodes[oColumnSet.getColumn(allSelectedCells[i].columnKey).getKeyIndex()], DT.CLASS_SELECTED);
7423
            }
7424
        }
7425
    }
7426
},
7427
 
7428
 
7429
 
7430
 
7431
 
7432
 
7433
 
7434
 
7435
 
7436
 
7437
 
7438
 
7439
 
7440
 
7441
 
7442
 
7443
 
7444
 
7445
 
7446
 
7447
 
7448
 
7449
 
7450
 
7451
 
7452
 
7453
 
7454
 
7455
 
7456
 
7457
 
7458
 
7459
 
7460
 
7461
 
7462
 
7463
 
7464
 
7465
 
7466
 
7467
 
7468
 
7469
 
7470
/////////////////////////////////////////////////////////////////////////////
7471
//
7472
// Private DOM Event Handlers
7473
//
7474
/////////////////////////////////////////////////////////////////////////////
7475
 
7476
/**
7477
 * Validates minWidths whenever the render chain ends.
7478
 *
7479
 * @method _onRenderChainEnd
7480
 * @private
7481
 */
7482
_onRenderChainEnd : function() {
7483
    // Hide loading message
7484
    this.hideTableMessage();
7485
 
7486
    // Show empty message
7487
    if(this._elTbody.rows.length === 0) {
7488
        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
7489
    }
7490
 
7491
    // Execute in timeout thread to give implementers a chance
7492
    // to subscribe after the constructor
7493
    var oSelf = this;
7494
    setTimeout(function() {
7495
        if((oSelf instanceof DT) && oSelf._sId) {
7496
            // Init event
7497
            if(oSelf._bInit) {
7498
                oSelf._bInit = false;
7499
                oSelf.fireEvent("initEvent");
7500
            }
7501
 
7502
            // Render event
7503
            oSelf.fireEvent("renderEvent");
7504
            // Backward compatibility
7505
            oSelf.fireEvent("refreshEvent");
7506
            YAHOO.log("DataTable rendered", "info", oSelf.toString());
7507
 
7508
            // Post-render routine
7509
            oSelf.validateColumnWidths();
7510
 
7511
            // Post-render event
7512
            oSelf.fireEvent("postRenderEvent");
7513
 
7514
            /*if(YAHOO.example.Performance.trialStart) {
7515
                YAHOO.log((new Date()).getTime() - YAHOO.example.Performance.trialStart.getTime() + " ms", "time");
7516
                YAHOO.example.Performance.trialStart = null;
7517
            }*/
7518
 
7519
            YAHOO.log("Post-render routine executed", "info", oSelf.toString());
7520
        }
7521
    }, 0);
7522
},
7523
 
7524
/**
7525
 * Handles click events on the DOCUMENT.
7526
 *
7527
 * @method _onDocumentClick
7528
 * @param e {HTMLEvent} The click event.
7529
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7530
 * @private
7531
 */
7532
_onDocumentClick : function(e, oSelf) {
7533
    var elTarget = Ev.getTarget(e);
7534
    var elTag = elTarget.nodeName.toLowerCase();
7535
 
7536
    if(!Dom.isAncestor(oSelf._elContainer, elTarget)) {
7537
        oSelf.fireEvent("tableBlurEvent");
7538
 
7539
        // Fires editorBlurEvent when click is not within the TABLE.
7540
        // For cases when click is within the TABLE, due to timing issues,
7541
        // the editorBlurEvent needs to get fired by the lower-level DOM click
7542
        // handlers below rather than by the TABLE click handler directly.
7543
        if(oSelf._oCellEditor) {
7544
            if(oSelf._oCellEditor.getContainerEl) {
7545
                var elContainer = oSelf._oCellEditor.getContainerEl();
7546
                // Only if the click was not within the CellEditor container
7547
                if(!Dom.isAncestor(elContainer, elTarget) &&
7548
                        (elContainer.id !== elTarget.id)) {
7549
                    oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
7550
                }
7551
            }
7552
            // Backward Compatibility
7553
            else if(oSelf._oCellEditor.isActive) {
7554
                // Only if the click was not within the Cell Editor container
7555
                if(!Dom.isAncestor(oSelf._oCellEditor.container, elTarget) &&
7556
                        (oSelf._oCellEditor.container.id !== elTarget.id)) {
7557
                    oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
7558
                }
7559
            }
7560
        }
7561
    }
7562
},
7563
 
7564
/**
7565
 * Handles focus events on the DataTable instance.
7566
 *
7567
 * @method _onTableFocus
7568
 * @param e {HTMLEvent} The focus event.
7569
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7570
 * @private
7571
 */
7572
_onTableFocus : function(e, oSelf) {
7573
    oSelf.fireEvent("tableFocusEvent");
7574
},
7575
 
7576
/**
7577
 * Handles focus events on the THEAD element.
7578
 *
7579
 * @method _onTheadFocus
7580
 * @param e {HTMLEvent} The focus event.
7581
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7582
 * @private
7583
 */
7584
_onTheadFocus : function(e, oSelf) {
7585
    oSelf.fireEvent("theadFocusEvent");
7586
    oSelf.fireEvent("tableFocusEvent");
7587
},
7588
 
7589
/**
7590
 * Handles focus events on the TBODY element.
7591
 *
7592
 * @method _onTbodyFocus
7593
 * @param e {HTMLEvent} The focus event.
7594
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7595
 * @private
7596
 */
7597
_onTbodyFocus : function(e, oSelf) {
7598
    oSelf.fireEvent("tbodyFocusEvent");
7599
    oSelf.fireEvent("tableFocusEvent");
7600
},
7601
 
7602
/**
7603
 * Handles mouseover events on the DataTable instance.
7604
 *
7605
 * @method _onTableMouseover
7606
 * @param e {HTMLEvent} The mouseover event.
7607
 * @param origTarget {HTMLElement} The mouseenter delegated element.
7608
 * @param container {HTMLElement} The mouseenter delegation container.
7609
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7610
 * @private
7611
 */
7612
_onTableMouseover : function(e, origTarget, container, oSelf) {
7613
    var elTarget = origTarget;
7614
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7615
    var bKeepBubbling = true;
7616
    while(elTarget && (elTag != "table")) {
7617
        switch(elTag) {
7618
            case "body":
7619
                 return;
7620
            case "a":
7621
                break;
7622
            case "td":
7623
                bKeepBubbling = oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
7624
                break;
7625
            case "span":
7626
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7627
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseoverEvent",{target:elTarget,event:e});
7628
                    // Backward compatibility
7629
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
7630
                }
7631
                break;
7632
            case "th":
7633
                bKeepBubbling = oSelf.fireEvent("theadCellMouseoverEvent",{target:elTarget,event:e});
7634
                // Backward compatibility
7635
                bKeepBubbling = oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
7636
                break;
7637
            case "tr":
7638
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7639
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseoverEvent",{target:elTarget,event:e});
7640
                    // Backward compatibility
7641
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
7642
                }
7643
                else {
7644
                    bKeepBubbling = oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
7645
                }
7646
                break;
7647
            default:
7648
                break;
7649
        }
7650
        if(bKeepBubbling === false) {
7651
            return;
7652
        }
7653
        else {
7654
            elTarget = elTarget.parentNode;
7655
            if(elTarget) {
7656
                elTag = elTarget.nodeName.toLowerCase();
7657
            }
7658
        }
7659
    }
7660
    oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elContainer),event:e});
7661
},
7662
 
7663
/**
7664
 * Handles mouseout events on the DataTable instance.
7665
 *
7666
 * @method _onTableMouseout
7667
 * @param e {HTMLEvent} The mouseout event.
7668
 * @param origTarget {HTMLElement} The mouseleave delegated element.
7669
 * @param container {HTMLElement} The mouseleave delegation container.
7670
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7671
 * @private
7672
 */
7673
_onTableMouseout : function(e, origTarget, container, oSelf) {
7674
    var elTarget = origTarget;
7675
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7676
    var bKeepBubbling = true;
7677
    while(elTarget && (elTag != "table")) {
7678
        switch(elTag) {
7679
            case "body":
7680
                return;
7681
            case "a":
7682
                break;
7683
            case "td":
7684
                bKeepBubbling = oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
7685
                break;
7686
            case "span":
7687
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7688
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseoutEvent",{target:elTarget,event:e});
7689
                    // Backward compatibility
7690
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
7691
                }
7692
                break;
7693
            case "th":
7694
                bKeepBubbling = oSelf.fireEvent("theadCellMouseoutEvent",{target:elTarget,event:e});
7695
                // Backward compatibility
7696
                bKeepBubbling = oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
7697
                break;
7698
            case "tr":
7699
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7700
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseoutEvent",{target:elTarget,event:e});
7701
                    // Backward compatibility
7702
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
7703
                }
7704
                else {
7705
                    bKeepBubbling = oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
7706
                }
7707
                break;
7708
            default:
7709
                break;
7710
        }
7711
        if(bKeepBubbling === false) {
7712
            return;
7713
        }
7714
        else {
7715
            elTarget = elTarget.parentNode;
7716
            if(elTarget) {
7717
                elTag = elTarget.nodeName.toLowerCase();
7718
            }
7719
        }
7720
    }
7721
    oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elContainer),event:e});
7722
},
7723
 
7724
/**
7725
 * Handles mousedown events on the DataTable instance.
7726
 *
7727
 * @method _onTableMousedown
7728
 * @param e {HTMLEvent} The mousedown event.
7729
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7730
 * @private
7731
 */
7732
_onTableMousedown : function(e, oSelf) {
7733
    var elTarget = Ev.getTarget(e);
7734
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7735
    var bKeepBubbling = true;
7736
    while(elTarget && (elTag != "table")) {
7737
        switch(elTag) {
7738
            case "body":
7739
                return;
7740
            case "a":
7741
                break;
7742
            case "td":
7743
                bKeepBubbling = oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
7744
                break;
7745
            case "span":
7746
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7747
                    bKeepBubbling = oSelf.fireEvent("theadLabelMousedownEvent",{target:elTarget,event:e});
7748
                    // Backward compatibility
7749
                    bKeepBubbling = oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
7750
                }
7751
                break;
7752
            case "th":
7753
                bKeepBubbling = oSelf.fireEvent("theadCellMousedownEvent",{target:elTarget,event:e});
7754
                // Backward compatibility
7755
                bKeepBubbling = oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
7756
                break;
7757
            case "tr":
7758
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7759
                    bKeepBubbling = oSelf.fireEvent("theadRowMousedownEvent",{target:elTarget,event:e});
7760
                    // Backward compatibility
7761
                    bKeepBubbling = oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
7762
                }
7763
                else {
7764
                    bKeepBubbling = oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
7765
                }
7766
                break;
7767
            default:
7768
                break;
7769
        }
7770
        if(bKeepBubbling === false) {
7771
            return;
7772
        }
7773
        else {
7774
            elTarget = elTarget.parentNode;
7775
            if(elTarget) {
7776
                elTag = elTarget.nodeName.toLowerCase();
7777
            }
7778
        }
7779
    }
7780
    oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elContainer),event:e});
7781
},
7782
 
7783
/**
7784
 * Handles mouseup events on the DataTable instance.
7785
 *
7786
 * @method _onTableMouseup
7787
 * @param e {HTMLEvent} The mouseup event.
7788
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7789
 * @private
7790
 */
7791
_onTableMouseup : function(e, oSelf) {
7792
    var elTarget = Ev.getTarget(e);
7793
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7794
    var bKeepBubbling = true;
7795
    while(elTarget && (elTag != "table")) {
7796
        switch(elTag) {
7797
            case "body":
7798
                return;
7799
            case "a":
7800
                break;
7801
            case "td":
7802
                bKeepBubbling = oSelf.fireEvent("cellMouseupEvent",{target:elTarget,event:e});
7803
                break;
7804
            case "span":
7805
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7806
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseupEvent",{target:elTarget,event:e});
7807
                    // Backward compatibility
7808
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseupEvent",{target:elTarget,event:e});
7809
                }
7810
                break;
7811
            case "th":
7812
                bKeepBubbling = oSelf.fireEvent("theadCellMouseupEvent",{target:elTarget,event:e});
7813
                // Backward compatibility
7814
                bKeepBubbling = oSelf.fireEvent("headerCellMouseupEvent",{target:elTarget,event:e});
7815
                break;
7816
            case "tr":
7817
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7818
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseupEvent",{target:elTarget,event:e});
7819
                    // Backward compatibility
7820
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseupEvent",{target:elTarget,event:e});
7821
                }
7822
                else {
7823
                    bKeepBubbling = oSelf.fireEvent("rowMouseupEvent",{target:elTarget,event:e});
7824
                }
7825
                break;
7826
            default:
7827
                break;
7828
        }
7829
        if(bKeepBubbling === false) {
7830
            return;
7831
        }
7832
        else {
7833
            elTarget = elTarget.parentNode;
7834
            if(elTarget) {
7835
                elTag = elTarget.nodeName.toLowerCase();
7836
            }
7837
        }
7838
    }
7839
    oSelf.fireEvent("tableMouseupEvent",{target:(elTarget || oSelf._elContainer),event:e});
7840
},
7841
 
7842
/**
7843
 * Handles dblclick events on the DataTable instance.
7844
 *
7845
 * @method _onTableDblclick
7846
 * @param e {HTMLEvent} The dblclick event.
7847
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7848
 * @private
7849
 */
7850
_onTableDblclick : function(e, oSelf) {
7851
    var elTarget = Ev.getTarget(e);
7852
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7853
    var bKeepBubbling = true;
7854
    while(elTarget && (elTag != "table")) {
7855
        switch(elTag) {
7856
            case "body":
7857
                return;
7858
            case "td":
7859
                bKeepBubbling = oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
7860
                break;
7861
            case "span":
7862
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7863
                    bKeepBubbling = oSelf.fireEvent("theadLabelDblclickEvent",{target:elTarget,event:e});
7864
                    // Backward compatibility
7865
                    bKeepBubbling = oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
7866
                }
7867
                break;
7868
            case "th":
7869
                bKeepBubbling = oSelf.fireEvent("theadCellDblclickEvent",{target:elTarget,event:e});
7870
                // Backward compatibility
7871
                bKeepBubbling = oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
7872
                break;
7873
            case "tr":
7874
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7875
                    bKeepBubbling = oSelf.fireEvent("theadRowDblclickEvent",{target:elTarget,event:e});
7876
                    // Backward compatibility
7877
                    bKeepBubbling = oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
7878
                }
7879
                else {
7880
                    bKeepBubbling = oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
7881
                }
7882
                break;
7883
            default:
7884
                break;
7885
        }
7886
        if(bKeepBubbling === false) {
7887
            return;
7888
        }
7889
        else {
7890
            elTarget = elTarget.parentNode;
7891
            if(elTarget) {
7892
                elTag = elTarget.nodeName.toLowerCase();
7893
            }
7894
        }
7895
    }
7896
    oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elContainer),event:e});
7897
},
7898
/**
7899
 * Handles keydown events on the THEAD element.
7900
 *
7901
 * @method _onTheadKeydown
7902
 * @param e {HTMLEvent} The key event.
7903
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7904
 * @private
7905
 */
7906
_onTheadKeydown : function(e, oSelf) {
7907
    var elTarget = Ev.getTarget(e);
7908
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7909
    var bKeepBubbling = true;
7910
    while(elTarget && (elTag != "table")) {
7911
        switch(elTag) {
7912
            case "body":
7913
                return;
7914
            case "input":
7915
            case "textarea":
7916
                // TODO: implement textareaKeyEvent
7917
                break;
7918
            case "thead":
7919
                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
7920
                break;
7921
            default:
7922
                break;
7923
        }
7924
        if(bKeepBubbling === false) {
7925
            return;
7926
        }
7927
        else {
7928
            elTarget = elTarget.parentNode;
7929
            if(elTarget) {
7930
                elTag = elTarget.nodeName.toLowerCase();
7931
            }
7932
        }
7933
    }
7934
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
7935
},
7936
 
7937
/**
7938
 * Handles keydown events on the TBODY element. Handles selection behavior,
7939
 * provides hooks for ENTER to edit functionality.
7940
 *
7941
 * @method _onTbodyKeydown
7942
 * @param e {HTMLEvent} The key event.
7943
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7944
 * @private
7945
 */
7946
_onTbodyKeydown : function(e, oSelf) {
7947
    var sMode = oSelf.get("selectionMode");
7948
 
7949
    if(sMode == "standard") {
7950
        oSelf._handleStandardSelectionByKey(e);
7951
    }
7952
    else if(sMode == "single") {
7953
        oSelf._handleSingleSelectionByKey(e);
7954
    }
7955
    else if(sMode == "cellblock") {
7956
        oSelf._handleCellBlockSelectionByKey(e);
7957
    }
7958
    else if(sMode == "cellrange") {
7959
        oSelf._handleCellRangeSelectionByKey(e);
7960
    }
7961
    else if(sMode == "singlecell") {
7962
        oSelf._handleSingleCellSelectionByKey(e);
7963
    }
7964
 
7965
    if(oSelf._oCellEditor) {
7966
        if(oSelf._oCellEditor.fireEvent) {
7967
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
7968
        }
7969
        else if(oSelf._oCellEditor.isActive) {
7970
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
7971
        }
7972
    }
7973
 
7974
    var elTarget = Ev.getTarget(e);
7975
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7976
    var bKeepBubbling = true;
7977
    while(elTarget && (elTag != "table")) {
7978
        switch(elTag) {
7979
            case "body":
7980
                return;
7981
            case "tbody":
7982
                bKeepBubbling = oSelf.fireEvent("tbodyKeyEvent",{target:elTarget,event:e});
7983
                break;
7984
            default:
7985
                break;
7986
        }
7987
        if(bKeepBubbling === false) {
7988
            return;
7989
        }
7990
        else {
7991
            elTarget = elTarget.parentNode;
7992
            if(elTarget) {
7993
                elTag = elTarget.nodeName.toLowerCase();
7994
            }
7995
        }
7996
    }
7997
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
7998
},
7999
 
8000
/**
8001
 * Handles click events on the THEAD element.
8002
 *
8003
 * @method _onTheadClick
8004
 * @param e {HTMLEvent} The click event.
8005
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
8006
 * @private
8007
 */
8008
_onTheadClick : function(e, oSelf) {
8009
    // This blurs the CellEditor
8010
    if(oSelf._oCellEditor) {
8011
        if(oSelf._oCellEditor.fireEvent) {
8012
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
8013
        }
8014
        // Backward compatibility
8015
        else if(oSelf._oCellEditor.isActive) {
8016
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
8017
        }
8018
    }
8019
 
8020
    var elTarget = Ev.getTarget(e),
8021
        elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(),
8022
        bKeepBubbling = true;
8023
    while(elTarget && (elTag != "table")) {
8024
        switch(elTag) {
8025
            case "body":
8026
                return;
8027
            case "input":
8028
                var sType = elTarget.type.toLowerCase();
8029
                if(sType == "checkbox") {
8030
                    bKeepBubbling = oSelf.fireEvent("theadCheckboxClickEvent",{target:elTarget,event:e});
8031
                }
8032
                else if(sType == "radio") {
8033
                    bKeepBubbling = oSelf.fireEvent("theadRadioClickEvent",{target:elTarget,event:e});
8034
                }
8035
                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
8036
                    if(!elTarget.disabled) {
8037
                        bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
8038
                    }
8039
                    else {
8040
                        bKeepBubbling = false;
8041
                    }
8042
                }
8043
                else if (elTarget.disabled){
8044
                    bKeepBubbling = false;
8045
                }
8046
                break;
8047
            case "a":
8048
                bKeepBubbling = oSelf.fireEvent("theadLinkClickEvent",{target:elTarget,event:e});
8049
                break;
8050
            case "button":
8051
                if(!elTarget.disabled) {
8052
                    bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
8053
                }
8054
                else {
8055
                    bKeepBubbling = false;
8056
                }
8057
                break;
8058
            case "span":
8059
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
8060
                    bKeepBubbling = oSelf.fireEvent("theadLabelClickEvent",{target:elTarget,event:e});
8061
                    // Backward compatibility
8062
                    bKeepBubbling = oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
8063
                }
8064
                break;
8065
            case "th":
8066
                bKeepBubbling = oSelf.fireEvent("theadCellClickEvent",{target:elTarget,event:e});
8067
                // Backward compatibility
8068
                bKeepBubbling = oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
8069
                break;
8070
            case "tr":
8071
                bKeepBubbling = oSelf.fireEvent("theadRowClickEvent",{target:elTarget,event:e});
8072
                // Backward compatibility
8073
                bKeepBubbling = oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
8074
                break;
8075
            default:
8076
                break;
8077
        }
8078
        if(bKeepBubbling === false) {
8079
            return;
8080
        }
8081
        else {
8082
            elTarget = elTarget.parentNode;
8083
            if(elTarget) {
8084
                elTag = elTarget.nodeName.toLowerCase();
8085
            }
8086
        }
8087
    }
8088
    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
8089
},
8090
 
8091
/**
8092
 * Handles click events on the primary TBODY element.
8093
 *
8094
 * @method _onTbodyClick
8095
 * @param e {HTMLEvent} The click event.
8096
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
8097
 * @private
8098
 */
8099
_onTbodyClick : function(e, oSelf) {
8100
    // This blurs the CellEditor
8101
    if(oSelf._oCellEditor) {
8102
        if(oSelf._oCellEditor.fireEvent) {
8103
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
8104
        }
8105
        else if(oSelf._oCellEditor.isActive) {
8106
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
8107
        }
8108
    }
8109
 
8110
    // Fire Custom Events
8111
    var elTarget = Ev.getTarget(e),
8112
        elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(),
8113
        bKeepBubbling = true;
8114
    while(elTarget && (elTag != "table")) {
8115
        switch(elTag) {
8116
            case "body":
8117
                return;
8118
            case "input":
8119
                var sType = elTarget.type.toLowerCase();
8120
                if(sType == "checkbox") {
8121
                    bKeepBubbling = oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e});
8122
                }
8123
                else if(sType == "radio") {
8124
                    bKeepBubbling = oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e});
8125
                }
8126
                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
8127
                    if(!elTarget.disabled) {
8128
                        bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
8129
                    }
8130
                    else {
8131
                        bKeepBubbling = false;
8132
                    }
8133
                }
8134
                else if (elTarget.disabled){
8135
                    bKeepBubbling = false;
8136
                }
8137
                break;
8138
            case "a":
8139
                bKeepBubbling = oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
8140
                break;
8141
            case "button":
8142
                if(!elTarget.disabled) {
8143
                    bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
8144
                }
8145
                else {
8146
                    bKeepBubbling = false;
8147
                }
8148
                break;
8149
            case "td":
8150
                bKeepBubbling = oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
8151
                break;
8152
            case "tr":
8153
                bKeepBubbling = oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
8154
                break;
8155
            default:
8156
                break;
8157
        }
8158
        if(bKeepBubbling === false) {
8159
            return;
8160
        }
8161
        else {
8162
            elTarget = elTarget.parentNode;
8163
            if(elTarget) {
8164
                elTag = elTarget.nodeName.toLowerCase();
8165
            }
8166
        }
8167
    }
8168
    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
8169
},
8170
 
8171
/**
8172
 * Handles change events on SELECT elements within DataTable.
8173
 *
8174
 * @method _onDropdownChange
8175
 * @param e {HTMLEvent} The change event.
8176
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
8177
 * @private
8178
 */
8179
_onDropdownChange : function(e, oSelf) {
8180
    var elTarget = Ev.getTarget(e);
8181
    oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget});
8182
},
8183
 
8184
 
8185
 
8186
 
8187
 
8188
 
8189
 
8190
 
8191
 
8192
 
8193
 
8194
 
8195
 
8196
 
8197
 
8198
 
8199
 
8200
 
8201
 
8202
 
8203
 
8204
 
8205
 
8206
 
8207
 
8208
 
8209
 
8210
 
8211
 
8212
 
8213
 
8214
 
8215
/////////////////////////////////////////////////////////////////////////////
8216
//
8217
// Public member variables
8218
//
8219
/////////////////////////////////////////////////////////////////////////////
8220
/**
8221
 * Returns object literal of initial configs.
8222
 *
8223
 * @property configs
8224
 * @type Object
8225
 * @default {}
8226
 */
8227
configs: null,
8228
 
8229
 
8230
/////////////////////////////////////////////////////////////////////////////
8231
//
8232
// Public methods
8233
//
8234
/////////////////////////////////////////////////////////////////////////////
8235
 
8236
/**
8237
 * Returns unique id assigned to instance, which is a useful prefix for
8238
 * generating unique DOM ID strings.
8239
 *
8240
 * @method getId
8241
 * @return {String} Unique ID of the DataSource instance.
8242
 */
8243
getId : function() {
8244
    return this._sId;
8245
},
8246
 
8247
/**
8248
 * DataSource instance name, for logging.
8249
 *
8250
 * @method toString
8251
 * @return {String} Unique name of the DataSource instance.
8252
 */
8253
 
8254
toString : function() {
8255
    return "DataTable instance " + this._sId;
8256
},
8257
 
8258
/**
8259
 * Returns the DataTable instance's DataSource instance.
8260
 *
8261
 * @method getDataSource
8262
 * @return {YAHOO.util.DataSource} DataSource instance.
8263
 */
8264
getDataSource : function() {
8265
    return this._oDataSource;
8266
},
8267
 
8268
/**
8269
 * Returns the DataTable instance's ColumnSet instance.
8270
 *
8271
 * @method getColumnSet
8272
 * @return {YAHOO.widget.ColumnSet} ColumnSet instance.
8273
 */
8274
getColumnSet : function() {
8275
    return this._oColumnSet;
8276
},
8277
 
8278
/**
8279
 * Returns the DataTable instance's RecordSet instance.
8280
 *
8281
 * @method getRecordSet
8282
 * @return {YAHOO.widget.RecordSet} RecordSet instance.
8283
 */
8284
getRecordSet : function() {
8285
    return this._oRecordSet;
8286
},
8287
 
8288
/**
8289
 * Returns on object literal representing the DataTable instance's current
8290
 * state with the following properties:
8291
 * <dl>
8292
 * <dt>pagination</dt>
8293
 * <dd>Instance of YAHOO.widget.Paginator</dd>
8294
 *
8295
 * <dt>sortedBy</dt>
8296
 * <dd>
8297
 *     <dl>
8298
 *         <dt>sortedBy.key</dt>
8299
 *         <dd>{String} Key of sorted Column</dd>
8300
 *         <dt>sortedBy.dir</dt>
8301
 *         <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
8302
 *     </dl>
8303
 * </dd>
8304
 *
8305
 * <dt>selectedRows</dt>
8306
 * <dd>Array of selected rows by Record ID.</dd>
8307
 *
8308
 * <dt>selectedCells</dt>
8309
 * <dd>Selected cells as an array of object literals:
8310
 *     {recordId:sRecordId, columnKey:sColumnKey}</dd>
8311
 * </dl>
8312
 *
8313
 * @method getState
8314
 * @return {Object} DataTable instance state object literal values.
8315
 */
8316
getState : function() {
8317
    return {
8318
        totalRecords: this.get('paginator') ? this.get('paginator').get("totalRecords") : this._oRecordSet.getLength(),
8319
        pagination: this.get("paginator") ? this.get("paginator").getState() : null,
8320
        sortedBy: this.get("sortedBy"),
8321
        selectedRows: this.getSelectedRows(),
8322
        selectedCells: this.getSelectedCells()
8323
    };
8324
},
8325
 
8326
 
8327
 
8328
 
8329
 
8330
 
8331
 
8332
 
8333
 
8334
 
8335
 
8336
 
8337
 
8338
 
8339
 
8340
 
8341
 
8342
 
8343
 
8344
 
8345
 
8346
 
8347
 
8348
 
8349
 
8350
 
8351
 
8352
 
8353
 
8354
 
8355
 
8356
 
8357
 
8358
 
8359
 
8360
 
8361
 
8362
 
8363
 
8364
 
8365
 
8366
 
8367
 
8368
// DOM ACCESSORS
8369
 
8370
/**
8371
 * Returns DOM reference to the DataTable's container element.
8372
 *
8373
 * @method getContainerEl
8374
 * @return {HTMLElement} Reference to DIV element.
8375
 */
8376
getContainerEl : function() {
8377
    return this._elContainer;
8378
},
8379
 
8380
/**
8381
 * Returns DOM reference to the DataTable's TABLE element.
8382
 *
8383
 * @method getTableEl
8384
 * @return {HTMLElement} Reference to TABLE element.
8385
 */
8386
getTableEl : function() {
8387
    return this._elTable;
8388
},
8389
 
8390
/**
8391
 * Returns DOM reference to the DataTable's THEAD element.
8392
 *
8393
 * @method getTheadEl
8394
 * @return {HTMLElement} Reference to THEAD element.
8395
 */
8396
getTheadEl : function() {
8397
    return this._elThead;
8398
},
8399
 
8400
/**
8401
 * Returns DOM reference to the DataTable's primary TBODY element.
8402
 *
8403
 * @method getTbodyEl
8404
 * @return {HTMLElement} Reference to TBODY element.
8405
 */
8406
getTbodyEl : function() {
8407
    return this._elTbody;
8408
},
8409
 
8410
/**
8411
 * Returns DOM reference to the DataTable's secondary TBODY element that is
8412
 * used to display messages.
8413
 *
8414
 * @method getMsgTbodyEl
8415
 * @return {HTMLElement} Reference to TBODY element.
8416
 */
8417
getMsgTbodyEl : function() {
8418
    return this._elMsgTbody;
8419
},
8420
 
8421
/**
8422
 * Returns DOM reference to the TD element within the secondary TBODY that is
8423
 * used to display messages.
8424
 *
8425
 * @method getMsgTdEl
8426
 * @return {HTMLElement} Reference to TD element.
8427
 */
8428
getMsgTdEl : function() {
8429
    return this._elMsgTd;
8430
},
8431
 
8432
/**
8433
 * Returns the corresponding TR reference for a given DOM element, ID string or
8434
 * page row index. If the given identifier is a child of a TR element,
8435
 * then DOM tree is traversed until a parent TR element is returned, otherwise
8436
 * null. Returns null if the row is not considered a primary row (i.e., row
8437
 * extensions).
8438
 *
8439
 * @method getTrEl
8440
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to
8441
 * get: by element reference, ID string, page row index, or Record.
8442
 * @return {HTMLElement} Reference to TR element, or null.
8443
 */
8444
getTrEl : function(row) {
8445
    // By Record
8446
    if(row instanceof YAHOO.widget.Record) {
8447
        return document.getElementById(row.getId());
8448
    }
8449
    // By page row index
8450
    else if(lang.isNumber(row)) {
8451
        var dataRows = Dom.getElementsByClassName(DT.CLASS_REC, "tr", this._elTbody);
8452
        return dataRows && dataRows[row] ? dataRows[row] : null;
8453
    }
8454
    // By ID string or element reference
8455
    else if(row) {
8456
        var elRow = (lang.isString(row)) ? document.getElementById(row) : row;
8457
 
8458
        // Validate HTML element
8459
        if(elRow && elRow.ownerDocument == document) {
8460
            // Validate TR element
8461
            if(elRow.nodeName.toLowerCase() != "tr") {
8462
                // Traverse up the DOM to find the corresponding TR element
8463
                elRow = Dom.getAncestorByTagName(elRow,"tr");
8464
            }
8465
 
8466
            return elRow;
8467
        }
8468
    }
8469
 
8470
    return null;
8471
},
8472
 
8473
/**
8474
 * Returns DOM reference to the first primary TR element in the DataTable page, or null.
8475
 *
8476
 * @method getFirstTrEl
8477
 * @return {HTMLElement} Reference to TR element.
8478
 */
8479
getFirstTrEl : function() {
8480
    var allRows = this._elTbody.rows,
8481
        i=0;
8482
    while(allRows[i]) {
8483
        if(this.getRecord(allRows[i])) {
8484
            return allRows[i];
8485
        }
8486
        i++;
8487
    }
8488
    return null;
8489
 
8490
},
8491
 
8492
/**
8493
 * Returns DOM reference to the last primary TR element in the DataTable page, or null.
8494
 *
8495
 * @method getLastTrEl
8496
 * @return {HTMLElement} Reference to last TR element.
8497
 */
8498
getLastTrEl : function() {
8499
    var allRows = this._elTbody.rows,
8500
        i=allRows.length-1;
8501
    while(i>-1) {
8502
        if(this.getRecord(allRows[i])) {
8503
            return allRows[i];
8504
        }
8505
        i--;
8506
    }
8507
    return null;
8508
},
8509
 
8510
/**
8511
 * Returns DOM reference to the next TR element from the given primary TR element, or null.
8512
 *
8513
 * @method getNextTrEl
8514
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
8515
 * reference, ID string, page row index, or Record from which to get next TR element.
8516
 * @param forcePrimary {Boolean} (optional) If true, will only return TR elements
8517
 * that correspond to Records. Non-primary rows (such as row expansions)
8518
 * will be skipped.
8519
 * @return {HTMLElement} Reference to next TR element.
8520
 */
8521
getNextTrEl : function(row, forcePrimary) {
8522
    var nThisTrIndex = this.getTrIndex(row);
8523
    if(nThisTrIndex !== null) {
8524
        var allRows = this._elTbody.rows;
8525
        if(forcePrimary) {
8526
            while(nThisTrIndex < allRows.length-1) {
8527
                row = allRows[nThisTrIndex+1];
8528
                if(this.getRecord(row)) {
8529
                    return row;
8530
                }
8531
                nThisTrIndex++;
8532
            }
8533
        }
8534
        else {
8535
            if(nThisTrIndex < allRows.length-1) {
8536
                return allRows[nThisTrIndex+1];
8537
            }
8538
        }
8539
    }
8540
 
8541
    YAHOO.log("Could not get next TR element for row " + row, "info", this.toString());
8542
    return null;
8543
},
8544
 
8545
/**
8546
 * Returns DOM reference to the previous TR element from the given primary TR element, or null.
8547
 *
8548
 * @method getPreviousTrEl
8549
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
8550
 * reference, ID string, page row index, or Record from which to get previous TR element.
8551
 * @param forcePrimary {Boolean} (optional) If true, will only return TR elements
8552
 * from rothat correspond to Records. Non-primary rows (such as row expansions)
8553
 * will be skipped.
8554
 * @return {HTMLElement} Reference to previous TR element.
8555
 */
8556
getPreviousTrEl : function(row, forcePrimary) {
8557
    var nThisTrIndex = this.getTrIndex(row);
8558
    if(nThisTrIndex !== null) {
8559
        var allRows = this._elTbody.rows;
8560
 
8561
        if(forcePrimary) {
8562
            while(nThisTrIndex > 0) {
8563
                row = allRows[nThisTrIndex-1];
8564
                if(this.getRecord(row)) {
8565
                    return row;
8566
                }
8567
                nThisTrIndex--;
8568
            }
8569
        }
8570
        else {
8571
            if(nThisTrIndex > 0) {
8572
                return allRows[nThisTrIndex-1];
8573
            }
8574
        }
8575
    }
8576
 
8577
    YAHOO.log("Could not get previous TR element for row " + row, "info", this.toString());
8578
    return null;
8579
},
8580
 
8581
 
8582
/**
8583
 * Workaround for IE bug where hidden or not-in-dom elements cause cellIndex
8584
 * value to be incorrect.
8585
 *
8586
 * @method getCellIndex
8587
 * @param cell {HTMLElement | Object} TD element or child of a TD element, or
8588
 * object literal of syntax {record:oRecord, column:oColumn}.
8589
 * @return {Number} TD.cellIndex value.
8590
 */
8591
getCellIndex : function(cell) {
8592
    cell = this.getTdEl(cell);
8593
    if(cell) {
8594
        if(ua.ie > 0) {
8595
            var i=0,
8596
                tr = cell.parentNode,
8597
                allCells = tr.childNodes,
8598
                len = allCells.length;
8599
            for(; i<len; i++) {
8600
                if(allCells[i] == cell) {
8601
                    return i;
8602
                }
8603
            }
8604
        }
8605
        else {
8606
            return cell.cellIndex;
8607
        }
8608
    }
8609
},
8610
 
8611
/**
8612
 * Returns DOM reference to a TD liner element.
8613
 *
8614
 * @method getTdLinerEl
8615
 * @param cell {HTMLElement | Object} TD element or child of a TD element, or
8616
 * object literal of syntax {record:oRecord, column:oColumn}.
8617
 * @return {HTMLElement} Reference to TD liner element.
8618
 */
8619
getTdLinerEl : function(cell) {
8620
    var elCell = this.getTdEl(cell);
8621
    return elCell.firstChild || null;
8622
},
8623
 
8624
/**
8625
 * Returns DOM reference to a TD element. Returns null if the row is not
8626
 * considered a primary row (i.e., row extensions).
8627
 *
8628
 * @method getTdEl
8629
 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
8630
 * object literal of syntax {record:oRecord, column:oColumn}.
8631
 * @return {HTMLElement} Reference to TD element.
8632
 */
8633
getTdEl : function(cell) {
8634
    var elCell;
8635
    var el = Dom.get(cell);
8636
 
8637
    // Validate HTML element
8638
    if(el && (el.ownerDocument == document)) {
8639
        // Validate TD element
8640
        if(el.nodeName.toLowerCase() != "td") {
8641
            // Traverse up the DOM to find the corresponding TR element
8642
            elCell = Dom.getAncestorByTagName(el, "td");
8643
        }
8644
        else {
8645
            elCell = el;
8646
        }
8647
 
8648
        // Make sure the TD is in this TBODY or is not in DOM
8649
        // Bug 2527707 and bug 2263558
8650
        if(elCell && ((elCell.parentNode.parentNode == this._elTbody) ||
8651
            (elCell.parentNode.parentNode === null) ||
8652
            (elCell.parentNode.parentNode.nodeType === 11))) {
8653
            // Now we can return the TD element
8654
            return elCell;
8655
        }
8656
    }
8657
    else if(cell) {
8658
        var oRecord, nColKeyIndex;
8659
 
8660
        if(lang.isString(cell.columnKey) && lang.isString(cell.recordId)) {
8661
            oRecord = this.getRecord(cell.recordId);
8662
            var oColumn = this.getColumn(cell.columnKey);
8663
            if(oColumn) {
8664
                nColKeyIndex = oColumn.getKeyIndex();
8665
            }
8666
 
8667
        }
8668
        if(cell.record && cell.column && cell.column.getKeyIndex) {
8669
            oRecord = cell.record;
8670
            nColKeyIndex = cell.column.getKeyIndex();
8671
        }
8672
        var elRow = this.getTrEl(oRecord);
8673
        if((nColKeyIndex !== null) && elRow && elRow.cells && elRow.cells.length > 0) {
8674
            return elRow.cells[nColKeyIndex] || null;
8675
        }
8676
    }
8677
 
8678
    return null;
8679
},
8680
 
8681
/**
8682
 * Returns DOM reference to the first primary TD element in the DataTable page (by default),
8683
 * the first TD element of the optionally given row, or null.
8684
 *
8685
 * @method getFirstTdEl
8686
 * @param row {HTMLElement} (optional) row from which to get first TD
8687
 * @return {HTMLElement} Reference to TD element.
8688
 */
8689
getFirstTdEl : function(row) {
8690
    var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getFirstTrEl();
8691
    if(elRow) {
8692
        if(elRow.cells && elRow.cells.length > 0) {
8693
            return elRow.cells[0];
8694
        }
8695
        else if(elRow.childNodes && elRow.childNodes.length > 0) {
8696
            return elRow.childNodes[0];
8697
        }
8698
    }
8699
    YAHOO.log("Could not get first TD element for row " + elRow, "info", this.toString());
8700
    return null;
8701
},
8702
 
8703
/**
8704
 * Returns DOM reference to the last primary TD element in the DataTable page (by default),
8705
 * the first TD element of the optionally given row, or null.
8706
 *
8707
 * @method getLastTdEl
8708
 * @param row {HTMLElement} (optional) row from which to get first TD
8709
 * @return {HTMLElement} Reference to last TD element.
8710
 */
8711
getLastTdEl : function(row) {
8712
    var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getLastTrEl();
8713
    if(elRow) {
8714
        if(elRow.cells && elRow.cells.length > 0) {
8715
            return elRow.cells[elRow.cells.length-1];
8716
        }
8717
        else if(elRow.childNodes && elRow.childNodes.length > 0) {
8718
            return elRow.childNodes[elRow.childNodes.length-1];
8719
        }
8720
    }
8721
    YAHOO.log("Could not get last TD element for row " + elRow, "info", this.toString());
8722
    return null;
8723
},
8724
 
8725
/**
8726
 * Returns DOM reference to the next TD element from the given cell, or null.
8727
 *
8728
 * @method getNextTdEl
8729
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8730
 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
8731
 * @return {HTMLElement} Reference to next TD element, or null.
8732
 */
8733
getNextTdEl : function(cell) {
8734
    var elCell = this.getTdEl(cell);
8735
    if(elCell) {
8736
        var nThisTdIndex = this.getCellIndex(elCell);
8737
        var elRow = this.getTrEl(elCell);
8738
        if(elRow.cells && (elRow.cells.length) > 0 && (nThisTdIndex < elRow.cells.length-1)) {
8739
            return elRow.cells[nThisTdIndex+1];
8740
        }
8741
        else if(elRow.childNodes && (elRow.childNodes.length) > 0 && (nThisTdIndex < elRow.childNodes.length-1)) {
8742
            return elRow.childNodes[nThisTdIndex+1];
8743
        }
8744
        else {
8745
            var elNextRow = this.getNextTrEl(elRow);
8746
            if(elNextRow) {
8747
                return elNextRow.cells[0];
8748
            }
8749
        }
8750
    }
8751
    YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
8752
    return null;
8753
},
8754
 
8755
/**
8756
 * Returns DOM reference to the previous TD element from the given cell, or null.
8757
 *
8758
 * @method getPreviousTdEl
8759
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8760
 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
8761
 * @return {HTMLElement} Reference to previous TD element, or null.
8762
 */
8763
getPreviousTdEl : function(cell) {
8764
    var elCell = this.getTdEl(cell);
8765
    if(elCell) {
8766
        var nThisTdIndex = this.getCellIndex(elCell);
8767
        var elRow = this.getTrEl(elCell);
8768
        if(nThisTdIndex > 0) {
8769
            if(elRow.cells && elRow.cells.length > 0) {
8770
                return elRow.cells[nThisTdIndex-1];
8771
            }
8772
            else if(elRow.childNodes && elRow.childNodes.length > 0) {
8773
                return elRow.childNodes[nThisTdIndex-1];
8774
            }
8775
        }
8776
        else {
8777
            var elPreviousRow = this.getPreviousTrEl(elRow);
8778
            if(elPreviousRow) {
8779
                return this.getLastTdEl(elPreviousRow);
8780
            }
8781
        }
8782
    }
8783
    YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
8784
    return null;
8785
},
8786
 
8787
/**
8788
 * Returns DOM reference to the above TD element from the given cell, or null.
8789
 *
8790
 * @method getAboveTdEl
8791
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8792
 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
8793
 * @param forcePrimary {Boolean} (optional) If true, will only return TD elements
8794
 * from rows that correspond to Records. Non-primary rows (such as row expansions)
8795
 * will be skipped.
8796
 * @return {HTMLElement} Reference to above TD element, or null.
8797
 */
8798
getAboveTdEl : function(cell, forcePrimary) {
8799
    var elCell = this.getTdEl(cell);
8800
    if(elCell) {
8801
        var elPreviousRow = this.getPreviousTrEl(elCell, forcePrimary);
8802
        if(elPreviousRow ) {
8803
            var cellIndex = this.getCellIndex(elCell);
8804
            if(elPreviousRow.cells && elPreviousRow.cells.length > 0) {
8805
                return elPreviousRow.cells[cellIndex] ? elPreviousRow.cells[cellIndex] : null;
8806
            }
8807
            else if(elPreviousRow.childNodes && elPreviousRow.childNodes.length > 0) {
8808
                return elPreviousRow.childNodes[cellIndex] ? elPreviousRow.childNodes[cellIndex] : null;
8809
            }
8810
        }
8811
    }
8812
    YAHOO.log("Could not get above TD element for cell " + cell, "info", this.toString());
8813
    return null;
8814
},
8815
 
8816
/**
8817
 * Returns DOM reference to the below TD element from the given cell, or null.
8818
 *
8819
 * @method getBelowTdEl
8820
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8821
 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
8822
 * @param forcePrimary {Boolean} (optional) If true, will only return TD elements
8823
 * from rows that correspond to Records. Non-primary rows (such as row expansions)
8824
 * will be skipped.
8825
 * @return {HTMLElement} Reference to below TD element, or null.
8826
 */
8827
getBelowTdEl : function(cell, forcePrimary) {
8828
    var elCell = this.getTdEl(cell);
8829
    if(elCell) {
8830
        var elNextRow = this.getNextTrEl(elCell, forcePrimary);
8831
        if(elNextRow) {
8832
            var cellIndex = this.getCellIndex(elCell);
8833
            if(elNextRow.cells && elNextRow.cells.length > 0) {
8834
                return elNextRow.cells[cellIndex] ? elNextRow.cells[cellIndex] : null;
8835
            }
8836
            else if(elNextRow.childNodes && elNextRow.childNodes.length > 0) {
8837
                return elNextRow.childNodes[cellIndex] ? elNextRow.childNodes[cellIndex] : null;
8838
            }
8839
        }
8840
    }
8841
    YAHOO.log("Could not get below TD element for cell " + cell, "info", this.toString());
8842
    return null;
8843
},
8844
 
8845
/**
8846
 * Returns DOM reference to a TH liner element. Needed to normalize for resizeable
8847
 * Columns, which have an additional resizer liner DIV element between the TH
8848
 * element and the liner DIV element.
8849
 *
8850
 * @method getThLinerEl
8851
 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
8852
 * DOM element reference, or string ID.
8853
 * @return {HTMLElement} Reference to TH liner element.
8854
 */
8855
getThLinerEl : function(theadCell) {
8856
    var oColumn = this.getColumn(theadCell);
8857
    return (oColumn) ? oColumn.getThLinerEl() : null;
8858
},
8859
 
8860
/**
8861
 * Returns DOM reference to a TH element.
8862
 *
8863
 * @method getThEl
8864
 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
8865
 * DOM element reference, or string ID.
8866
 * @return {HTMLElement} Reference to TH element.
8867
 */
8868
getThEl : function(theadCell) {
8869
    var elTh;
8870
 
8871
    // Validate Column instance
8872
    if(theadCell instanceof YAHOO.widget.Column) {
8873
        var oColumn = theadCell;
8874
        elTh = oColumn.getThEl();
8875
        if(elTh) {
8876
            return elTh;
8877
        }
8878
    }
8879
    // Validate HTML element
8880
    else {
8881
        var el = Dom.get(theadCell);
8882
 
8883
        if(el && (el.ownerDocument == document)) {
8884
            // Validate TH element
8885
            if(el.nodeName.toLowerCase() != "th") {
8886
                // Traverse up the DOM to find the corresponding TR element
8887
                elTh = Dom.getAncestorByTagName(el,"th");
8888
            }
8889
            else {
8890
                elTh = el;
8891
            }
8892
 
8893
            return elTh;
8894
        }
8895
    }
8896
 
8897
    return null;
8898
},
8899
 
8900
/**
8901
 * Returns the page row index of given primary row. Returns null if the row is not on the
8902
 * current DataTable page, or if row is not considered a primary row (i.e., row
8903
 * extensions).
8904
 *
8905
 * @method getTrIndex
8906
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID
8907
 * string reference to an element within the DataTable page, a Record instance,
8908
 * or a Record's RecordSet index.
8909
 * @return {Number} Page row index, or null if data row does not exist or is not on current page.
8910
 */
8911
getTrIndex : function(row) {
8912
    var record = this.getRecord(row),
8913
        index = this.getRecordIndex(record),
8914
        tr;
8915
    if(record) {
8916
        tr = this.getTrEl(record);
8917
        if(tr) {
8918
            return tr.sectionRowIndex;
8919
        }
8920
        else {
8921
            var oPaginator = this.get("paginator");
8922
            if(oPaginator) {
8923
                return oPaginator.get('recordOffset') + index;
8924
            }
8925
            else {
8926
                return index;
8927
            }
8928
        }
8929
    }
8930
    YAHOO.log("Could not get page row index for row " + row, "info", this.toString());
8931
    return null;
8932
},
8933
 
8934
 
8935
 
8936
 
8937
 
8938
 
8939
 
8940
 
8941
 
8942
 
8943
 
8944
 
8945
 
8946
 
8947
 
8948
 
8949
 
8950
 
8951
 
8952
 
8953
 
8954
 
8955
 
8956
 
8957
 
8958
 
8959
 
8960
 
8961
 
8962
 
8963
 
8964
 
8965
 
8966
 
8967
 
8968
 
8969
 
8970
 
8971
 
8972
 
8973
 
8974
 
8975
 
8976
 
8977
 
8978
 
8979
// TABLE FUNCTIONS
8980
 
8981
/**
8982
 * Loads new data. Convenience method that calls DataSource's sendRequest()
8983
 * method under the hood.
8984
 *
8985
 * @method load
8986
 * @param oConfig {object} Optional configuration parameters:
8987
 *
8988
 * <dl>
8989
 * <dt>request</dt><dd>Pass in a new request, or initialRequest is used.</dd>
8990
 * <dt>callback</dt><dd>Pass in DataSource sendRequest() callback object, or the following is used:
8991
 *    <dl>
8992
 *      <dt>success</dt><dd>datatable.onDataReturnInitializeTable</dd>
8993
 *      <dt>failure</dt><dd>datatable.onDataReturnInitializeTable</dd>
8994
 *      <dt>scope</dt><dd>datatable</dd>
8995
 *      <dt>argument</dt><dd>datatable.getState()</dd>
8996
 *    </dl>
8997
 * </dd>
8998
 * <dt>datasource</dt><dd>Pass in a new DataSource instance to override the current DataSource for this transaction.</dd>
8999
 * </dl>
9000
 */
9001
load : function(oConfig) {
9002
    oConfig = oConfig || {};
9003
 
9004
    (oConfig.datasource || this._oDataSource).sendRequest(oConfig.request || this.get("initialRequest"), oConfig.callback || {
9005
        success: this.onDataReturnInitializeTable,
9006
        failure: this.onDataReturnInitializeTable,
9007
        scope: this,
9008
        argument: this.getState()
9009
    });
9010
},
9011
 
9012
/**
9013
 * Resets a RecordSet with the given data and populates the page view
9014
 * with the new data. Any previous data, and selection and sort states are
9015
 * cleared. New data should be added as a separate step.
9016
 *
9017
 * @method initializeTable
9018
 */
9019
initializeTable : function() {
9020
    // Reset init flag
9021
    this._bInit = true;
9022
 
9023
    // Clear the RecordSet
9024
    this._oRecordSet.reset();
9025
 
9026
    // Clear the Paginator's totalRecords if paginating
9027
    var pag = this.get('paginator');
9028
    if (pag) {
9029
        pag.set('totalRecords',0);
9030
    }
9031
 
9032
    // Clear selections
9033
    this._unselectAllTrEls();
9034
    this._unselectAllTdEls();
9035
    this._aSelections = null;
9036
    this._oAnchorRecord = null;
9037
    this._oAnchorCell = null;
9038
 
9039
    // Clear sort
9040
    this.set("sortedBy", null);
9041
},
9042
 
9043
/**
9044
 * Internal wrapper calls run() on render Chain instance.
9045
 *
9046
 * @method _runRenderChain
9047
 * @private
9048
 */
9049
_runRenderChain : function() {
9050
    this._oChainRender.run();
9051
},
9052
 
9053
/**
9054
 * Returns array of Records for current view. For example, if paginated, it
9055
 * returns the subset of Records for current page.
9056
 *
9057
 * @method _getViewRecords
9058
 * @protected
9059
 * @return {Array} Array of Records to display in current view.
9060
 */
9061
_getViewRecords : function() {
9062
    // Paginator is enabled, show a subset of Records
9063
    var oPaginator = this.get('paginator');
9064
    if(oPaginator) {
9065
        return this._oRecordSet.getRecords(
9066
                        oPaginator.getStartIndex(),
9067
                        oPaginator.getRowsPerPage());
9068
    }
9069
    // Not paginated, show all records
9070
    else {
9071
        return this._oRecordSet.getRecords();
9072
    }
9073
 
9074
},
9075
 
9076
/**
9077
 * Renders the view with existing Records from the RecordSet while
9078
 * maintaining sort, pagination, and selection states. For performance, reuses
9079
 * existing DOM elements when possible while deleting extraneous elements.
9080
 *
9081
 * @method render
9082
 */
9083
render : function() {
9084
//YAHOO.example.Performance.trialStart = new Date();
9085
 
9086
    this._oChainRender.stop();
9087
 
9088
    this.fireEvent("beforeRenderEvent");
9089
    YAHOO.log("DataTable rendering...", "info", this.toString());
9090
 
9091
    var i, j, k, len,
9092
        allRecords = this._getViewRecords();
9093
 
9094
 
9095
    // From the top, update in-place existing rows, so as to reuse DOM elements
9096
    var elTbody = this._elTbody,
9097
        loopN = this.get("renderLoopSize"),
9098
        nRecordsLength = allRecords.length;
9099
 
9100
    // Table has rows
9101
    if(nRecordsLength > 0) {
9102
        elTbody.style.display = "none";
9103
        while(elTbody.lastChild) {
9104
            elTbody.removeChild(elTbody.lastChild);
9105
        }
9106
        elTbody.style.display = "";
9107
 
9108
        // Set up the loop Chain to render rows
9109
        this._oChainRender.add({
9110
            method: function(oArg) {
9111
                if((this instanceof DT) && this._sId) {
9112
                    var i = oArg.nCurrentRecord,
9113
                        endRecordIndex = ((oArg.nCurrentRecord+oArg.nLoopLength) > nRecordsLength) ?
9114
                                nRecordsLength : (oArg.nCurrentRecord+oArg.nLoopLength),
9115
                        elRow, nextSibling;
9116
 
9117
                    elTbody.style.display = "none";
9118
 
9119
                    for(; i<endRecordIndex; i++) {
9120
                        elRow = Dom.get(allRecords[i].getId());
9121
                        elRow = elRow || this._addTrEl(allRecords[i]);
9122
                        nextSibling = elTbody.childNodes[i] || null;
9123
                        elTbody.insertBefore(elRow, nextSibling);
9124
                    }
9125
                    elTbody.style.display = "";
9126
 
9127
                    // Set up for the next loop
9128
                    oArg.nCurrentRecord = i;
9129
                }
9130
            },
9131
            scope: this,
9132
            iterations: (loopN > 0) ? Math.ceil(nRecordsLength/loopN) : 1,
9133
            argument: {
9134
                nCurrentRecord: 0,//nRecordsLength-1,  // Start at first Record
9135
                nLoopLength: (loopN > 0) ? loopN : nRecordsLength
9136
            },
9137
            timeout: (loopN > 0) ? 0 : -1
9138
        });
9139
 
9140
        // Post-render tasks
9141
        this._oChainRender.add({
9142
            method: function(oArg) {
9143
                if((this instanceof DT) && this._sId) {
9144
                    while(elTbody.rows.length > nRecordsLength) {
9145
                        elTbody.removeChild(elTbody.lastChild);
9146
                    }
9147
                    this._setFirstRow();
9148
                    this._setLastRow();
9149
                    this._setRowStripes();
9150
                    this._setSelections();
9151
                }
9152
            },
9153
            scope: this,
9154
            timeout: (loopN > 0) ? 0 : -1
9155
        });
9156
 
9157
    }
9158
    // Table has no rows
9159
    else {
9160
        // Set up the loop Chain to delete rows
9161
        var nTotal = elTbody.rows.length;
9162
        if(nTotal > 0) {
9163
            this._oChainRender.add({
9164
                method: function(oArg) {
9165
                    if((this instanceof DT) && this._sId) {
9166
                        var i = oArg.nCurrent,
9167
                            loopN = oArg.nLoopLength,
9168
                            nIterEnd = (i - loopN < 0) ? 0 : i - loopN;
9169
 
9170
                        elTbody.style.display = "none";
9171
 
9172
                        for(; i>nIterEnd; i--) {
9173
                            elTbody.deleteRow(-1);
9174
                        }
9175
                        elTbody.style.display = "";
9176
 
9177
                        // Set up for the next loop
9178
                        oArg.nCurrent = i;
9179
                    }
9180
                },
9181
                scope: this,
9182
                iterations: (loopN > 0) ? Math.ceil(nTotal/loopN) : 1,
9183
                argument: {
9184
                    nCurrent: nTotal,
9185
                    nLoopLength: (loopN > 0) ? loopN : nTotal
9186
                },
9187
                timeout: (loopN > 0) ? 0 : -1
9188
            });
9189
        }
9190
    }
9191
    this._runRenderChain();
9192
},
9193
 
9194
/**
9195
 * Disables DataTable UI.
9196
 *
9197
 * @method disable
9198
 */
9199
disable : function() {
9200
    this._disabled = true;
9201
    var elTable = this._elTable;
9202
    var elMask = this._elMask;
9203
    elMask.style.width = elTable.offsetWidth + "px";
9204
    elMask.style.height = elTable.offsetHeight + "px";
9205
    elMask.style.left = elTable.offsetLeft + "px";
9206
    elMask.style.display = "";
9207
    this.fireEvent("disableEvent");
9208
},
9209
 
9210
/**
9211
 * Undisables DataTable UI.
9212
 *
9213
 * @method undisable
9214
 */
9215
undisable : function() {
9216
    this._disabled = false;
9217
    this._elMask.style.display = "none";
9218
    this.fireEvent("undisableEvent");
9219
},
9220
 
9221
 /**
9222
 * Returns disabled state.
9223
 *
9224
 * @method isDisabled
9225
 * @return {Boolean} True if UI is disabled, otherwise false
9226
 */
9227
isDisabled : function() {
9228
    return this._disabled;
9229
},
9230
 
9231
/**
9232
 * Nulls out the entire DataTable instance and related objects, removes attached
9233
 * event listeners, and clears out DOM elements inside the container. After
9234
 * calling this method, the instance reference should be expliclitly nulled by
9235
 * implementer, as in myDataTable = null. Use with caution!
9236
 *
9237
 * @method destroy
9238
 */
9239
destroy : function() {
9240
    // Store for later
9241
    var instanceName = this.toString();
9242
 
9243
    this._oChainRender.stop();
9244
 
9245
    // Destroy ColumnDD and ColumnResizers
9246
    this._destroyColumnHelpers();
9247
 
9248
    // Destroy all CellEditors
9249
    var oCellEditor;
9250
    for(var i=0, len=this._oColumnSet.flat.length; i<len; i++) {
9251
        oCellEditor = this._oColumnSet.flat[i].editor;
9252
        if(oCellEditor && oCellEditor.destroy) {
9253
            oCellEditor.destroy();
9254
            this._oColumnSet.flat[i].editor = null;
9255
        }
9256
    }
9257
 
9258
    // Destroy Paginator
9259
    this._destroyPaginator();
9260
 
9261
    // Unhook custom events
9262
    this._oRecordSet.unsubscribeAll();
9263
    this.unsubscribeAll();
9264
 
9265
    // Unhook DOM events
9266
    Ev.removeListener(document, "click", this._onDocumentClick);
9267
 
9268
    // Clear out the container
9269
    this._destroyContainerEl(this._elContainer);
9270
 
9271
    // Null out objects
9272
    for(var param in this) {
9273
        if(lang.hasOwnProperty(this, param)) {
9274
            this[param] = null;
9275
        }
9276
    }
9277
 
9278
    // Clean up static values
9279
    DT._nCurrentCount--;
9280
 
9281
    if(DT._nCurrentCount < 1) {
9282
        if(DT._elDynStyleNode) {
9283
            document.getElementsByTagName('head')[0].removeChild(DT._elDynStyleNode);
9284
            DT._elDynStyleNode = null;
9285
        }
9286
    }
9287
 
9288
    YAHOO.log("DataTable instance destroyed: " + instanceName);
9289
},
9290
 
9291
/**
9292
 * Displays message within secondary TBODY.
9293
 *
9294
 * @method showTableMessage
9295
 * @param sHTML {HTML} (optional) Value for innerHTML.
9296
 * @param sClassName {String} (optional) Classname.
9297
 */
9298
showTableMessage : function(sHTML, sClassName) {
9299
    var elCell = this._elMsgTd;
9300
    if(lang.isString(sHTML)) {
9301
        elCell.firstChild.innerHTML = sHTML;
9302
    }
9303
    if(lang.isString(sClassName)) {
9304
        elCell.className = sClassName;
9305
    }
9306
 
9307
    this._elMsgTbody.style.display = "";
9308
 
9309
    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
9310
    YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
9311
},
9312
 
9313
/**
9314
 * Hides secondary TBODY.
9315
 *
9316
 * @method hideTableMessage
9317
 */
9318
hideTableMessage : function() {
9319
    if(this._elMsgTbody.style.display != "none") {
9320
        this._elMsgTbody.style.display = "none";
9321
        this._elMsgTbody.parentNode.style.width = "";
9322
        this.fireEvent("tableMsgHideEvent");
9323
        YAHOO.log("DataTable message hidden", "info", this.toString());
9324
    }
9325
},
9326
 
9327
/**
9328
 * Brings focus to the TBODY element. Alias to focusTbodyEl.
9329
 *
9330
 * @method focus
9331
 */
9332
focus : function() {
9333
    this.focusTbodyEl();
9334
},
9335
 
9336
/**
9337
 * Brings focus to the THEAD element.
9338
 *
9339
 * @method focusTheadEl
9340
 */
9341
focusTheadEl : function() {
9342
    this._focusEl(this._elThead);
9343
},
9344
 
9345
/**
9346
 * Brings focus to the TBODY element.
9347
 *
9348
 * @method focusTbodyEl
9349
 */
9350
focusTbodyEl : function() {
9351
    this._focusEl(this._elTbody);
9352
},
9353
 
9354
/**
9355
 * Setting display:none on DataTable or any parent may impact width validations.
9356
 * After setting display back to "", implementers should call this method to
9357
 * manually perform those validations.
9358
 *
9359
 * @method onShow
9360
 */
9361
onShow : function() {
9362
    this.validateColumnWidths();
9363
 
9364
    for(var allKeys = this._oColumnSet.keys, i=0, len=allKeys.length, col; i<len; i++) {
9365
        col = allKeys[i];
9366
        if(col._ddResizer) {
9367
            col._ddResizer.resetResizerEl();
9368
        }
9369
    }
9370
},
9371
 
9372
 
9373
 
9374
 
9375
 
9376
 
9377
 
9378
 
9379
 
9380
 
9381
 
9382
 
9383
 
9384
 
9385
 
9386
 
9387
 
9388
 
9389
 
9390
 
9391
 
9392
 
9393
 
9394
 
9395
 
9396
 
9397
 
9398
 
9399
 
9400
 
9401
 
9402
 
9403
 
9404
 
9405
 
9406
 
9407
 
9408
 
9409
 
9410
 
9411
 
9412
 
9413
 
9414
 
9415
 
9416
 
9417
 
9418
 
9419
 
9420
 
9421
 
9422
 
9423
 
9424
 
9425
 
9426
 
9427
 
9428
 
9429
 
9430
 
9431
 
9432
 
9433
 
9434
 
9435
 
9436
 
9437
 
9438
// RECORDSET FUNCTIONS
9439
 
9440
/**
9441
 * Returns Record index for given TR element or page row index.
9442
 *
9443
 * @method getRecordIndex
9444
 * @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR
9445
 * element reference or page row index.
9446
 * @return {Number} Record's RecordSet index, or null.
9447
 */
9448
getRecordIndex : function(row) {
9449
    var nTrIndex;
9450
 
9451
    if(!lang.isNumber(row)) {
9452
        // By Record
9453
        if(row instanceof YAHOO.widget.Record) {
9454
            return this._oRecordSet.getRecordIndex(row);
9455
        }
9456
        // By element reference
9457
        else {
9458
            // Find the TR element
9459
            var el = this.getTrEl(row);
9460
            if(el) {
9461
                nTrIndex = el.sectionRowIndex;
9462
            }
9463
        }
9464
    }
9465
    // By page row index
9466
    else {
9467
        nTrIndex = row;
9468
    }
9469
 
9470
    if(lang.isNumber(nTrIndex)) {
9471
        var oPaginator = this.get("paginator");
9472
        if(oPaginator) {
9473
            return oPaginator.get('recordOffset') + nTrIndex;
9474
        }
9475
        else {
9476
            return nTrIndex;
9477
        }
9478
    }
9479
 
9480
    YAHOO.log("Could not get Record index for row " + row, "info", this.toString());
9481
    return null;
9482
},
9483
 
9484
/**
9485
 * For the given identifier, returns the associated Record instance.
9486
 *
9487
 * @method getRecord
9488
 * @param row {HTMLElement | Number | String} DOM reference to a TR element (or
9489
 * child of a TR element), RecordSet position index, or Record ID.
9490
 * @return {YAHOO.widget.Record} Record instance.
9491
 */
9492
getRecord : function(row) {
9493
    var oRecord = this._oRecordSet.getRecord(row);
9494
 
9495
    if(!oRecord) {
9496
        // Validate TR element
9497
        var elRow = this.getTrEl(row);
9498
        if(elRow) {
9499
            oRecord = this._oRecordSet.getRecord(elRow.id);
9500
        }
9501
    }
9502
 
9503
    if(oRecord instanceof YAHOO.widget.Record) {
9504
        return this._oRecordSet.getRecord(oRecord);
9505
    }
9506
    else {
9507
        YAHOO.log("Could not get Record for row at " + row, "info", this.toString());
9508
        return null;
9509
    }
9510
},
9511
 
9512
 
9513
 
9514
 
9515
 
9516
 
9517
 
9518
 
9519
 
9520
 
9521
 
9522
 
9523
 
9524
 
9525
 
9526
 
9527
 
9528
 
9529
 
9530
 
9531
 
9532
 
9533
 
9534
 
9535
 
9536
 
9537
 
9538
 
9539
 
9540
 
9541
 
9542
 
9543
 
9544
 
9545
 
9546
 
9547
 
9548
 
9549
 
9550
 
9551
 
9552
 
9553
 
9554
 
9555
 
9556
 
9557
// COLUMN FUNCTIONS
9558
 
9559
/**
9560
 * For the given identifier, returns the associated Column instance. Note: For
9561
 * getting Columns by Column ID string, please use the method getColumnById().
9562
 *
9563
 * @method getColumn
9564
 * @param column {HTMLElement | String | Number} TH/TD element (or child of a
9565
 * TH/TD element), a Column key, or a ColumnSet key index.
9566
 * @return {YAHOO.widget.Column} Column instance.
9567
 */
9568
getColumn : function(column) {
9569
    var oColumn = this._oColumnSet.getColumn(column);
9570
 
9571
    if(!oColumn) {
9572
        // Validate TD element
9573
        var elCell = this.getTdEl(column);
9574
        if(elCell) {
9575
            oColumn = this._oColumnSet.getColumn(this.getCellIndex(elCell));
9576
        }
9577
        // Validate TH element
9578
        else {
9579
            elCell = this.getThEl(column);
9580
            if(elCell) {
9581
                // Find by TH el ID
9582
                var allColumns = this._oColumnSet.flat;
9583
                for(var i=0, len=allColumns.length; i<len; i++) {
9584
                    if(allColumns[i].getThEl().id === elCell.id) {
9585
                        oColumn = allColumns[i];
9586
                    }
9587
                }
9588
            }
9589
        }
9590
    }
9591
    if(!oColumn) {
9592
        YAHOO.log("Could not get Column for column at " + column, "info", this.toString());
9593
    }
9594
    return oColumn;
9595
},
9596
 
9597
/**
9598
 * For the given Column ID, returns the associated Column instance. Note: For
9599
 * getting Columns by key, please use the method getColumn().
9600
 *
9601
 * @method getColumnById
9602
 * @param column {String} Column ID string.
9603
 * @return {YAHOO.widget.Column} Column instance.
9604
 */
9605
getColumnById : function(column) {
9606
    return this._oColumnSet.getColumnById(column);
9607
},
9608
 
9609
/**
9610
 * For the given Column instance, returns next direction to sort.
9611
 *
9612
 * @method getColumnSortDir
9613
 * @param oColumn {YAHOO.widget.Column} Column instance.
9614
 * @param oSortedBy {Object} (optional) Specify the state, or use current state.
9615
 * @return {String} YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTableCLASS_DESC.
9616
 */
9617
getColumnSortDir : function(oColumn, oSortedBy) {
9618
    // Backward compatibility
9619
    if(oColumn.sortOptions && oColumn.sortOptions.defaultDir) {
9620
        if(oColumn.sortOptions.defaultDir == "asc") {
9621
            oColumn.sortOptions.defaultDir = DT.CLASS_ASC;
9622
        }
9623
        else if (oColumn.sortOptions.defaultDir == "desc") {
9624
            oColumn.sortOptions.defaultDir = DT.CLASS_DESC;
9625
        }
9626
    }
9627
 
9628
    // What is the Column's default sort direction?
9629
    var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultDir) ? oColumn.sortOptions.defaultDir : DT.CLASS_ASC;
9630
 
9631
    // Is the Column currently sorted?
9632
    var bSorted = false;
9633
    oSortedBy = oSortedBy || this.get("sortedBy");
9634
    if(oSortedBy && (oSortedBy.key === oColumn.key)) {
9635
        bSorted = true;
9636
        if(oSortedBy.dir) {
9637
            sortDir = (oSortedBy.dir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
9638
        }
9639
        else {
9640
            sortDir = (sortDir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
9641
        }
9642
    }
9643
    return sortDir;
9644
},
9645
 
9646
/**
9647
 * Overridable method gives implementers a hook to show loading message before
9648
 * sorting Column.
9649
 *
9650
 * @method doBeforeSortColumn
9651
 * @param oColumn {YAHOO.widget.Column} Column instance.
9652
 * @param sSortDir {String} YAHOO.widget.DataTable.CLASS_ASC or
9653
 * YAHOO.widget.DataTable.CLASS_DESC.
9654
 * @return {Boolean} Return true to continue sorting Column.
9655
 */
9656
doBeforeSortColumn : function(oColumn, sSortDir) {
9657
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
9658
    return true;
9659
},
9660
 
9661
/**
9662
 * Sorts given Column. If "dynamicData" is true, current selections are purged before
9663
 * a request is sent to the DataSource for data for the new state (using the
9664
 * request returned by "generateRequest()").
9665
 *
9666
 * @method sortColumn
9667
 * @param oColumn {YAHOO.widget.Column} Column instance.
9668
 * @param sDir {String} (Optional) YAHOO.widget.DataTable.CLASS_ASC or
9669
 * YAHOO.widget.DataTable.CLASS_DESC
9670
 */
9671
sortColumn : function(oColumn, sDir) {
9672
    if(oColumn && (oColumn instanceof YAHOO.widget.Column)) {
9673
        if(!oColumn.sortable) {
9674
            Dom.addClass(this.getThEl(oColumn), DT.CLASS_SORTABLE);
9675
        }
9676
 
9677
        // Validate given direction
9678
        if(sDir && (sDir !== DT.CLASS_ASC) && (sDir !== DT.CLASS_DESC)) {
9679
            sDir = null;
9680
        }
9681
 
9682
        // Get the sort dir
9683
        var sSortDir = sDir || this.getColumnSortDir(oColumn);
9684
 
9685
        // Is the Column currently sorted?
9686
        var oSortedBy = this.get("sortedBy") || {};
9687
        var bSorted = (oSortedBy.key === oColumn.key) ? true : false;
9688
 
9689
        var ok = this.doBeforeSortColumn(oColumn, sSortDir);
9690
        if(ok) {
9691
            // Server-side sort
9692
            if(this.get("dynamicData")) {
9693
                // Get current state
9694
                var oState = this.getState();
9695
 
9696
                // Reset record offset, if paginated
9697
                if(oState.pagination) {
9698
                    oState.pagination.recordOffset = 0;
9699
                }
9700
 
9701
                // Update sortedBy to new values
9702
                oState.sortedBy = {
9703
                    key: oColumn.key,
9704
                    dir: sSortDir
9705
                };
9706
 
9707
                // Get the request for the new state
9708
                var request = this.get("generateRequest")(oState, this);
9709
 
9710
                // Purge selections
9711
                this.unselectAllRows();
9712
                this.unselectAllCells();
9713
 
9714
                // Send request for new data
9715
                var callback = {
9716
                    success : this.onDataReturnSetRows,
9717
                    failure : this.onDataReturnSetRows,
9718
                    argument : oState, // Pass along the new state to the callback
9719
                    scope : this
9720
                };
9721
                this._oDataSource.sendRequest(request, callback);
9722
            }
9723
            // Client-side sort
9724
            else {
9725
                // Is there a custom sort handler function defined?
9726
                var sortFnc = (oColumn.sortOptions && lang.isFunction(oColumn.sortOptions.sortFunction)) ?
9727
                        // Custom sort function
9728
                        oColumn.sortOptions.sortFunction : null;
9729
 
9730
                // Sort the Records
9731
                if(!bSorted || sDir || sortFnc) {
9732
                    // Default sort function if necessary
9733
                    sortFnc = sortFnc || this.get("sortFunction");
9734
                    // Get the field to sort
9735
                    var sField = (oColumn.sortOptions && oColumn.sortOptions.field) ? oColumn.sortOptions.field : oColumn.field;
9736
 
9737
                    // Sort the Records
9738
                    this._oRecordSet.sortRecords(sortFnc, ((sSortDir == DT.CLASS_DESC) ? true : false), sField);
9739
                }
9740
                // Just reverse the Records
9741
                else {
9742
                    this._oRecordSet.reverseRecords();
9743
                }
9744
 
9745
                // Reset to first page if paginated
9746
                var oPaginator = this.get('paginator');
9747
                if (oPaginator) {
9748
                    // Set page silently, so as not to fire change event.
9749
                    oPaginator.setPage(1,true);
9750
                }
9751
 
9752
                // Update UI via sortedBy
9753
                this.render();
9754
                this.set("sortedBy", {key:oColumn.key, dir:sSortDir, column:oColumn});
9755
            }
9756
 
9757
            this.fireEvent("columnSortEvent",{column:oColumn,dir:sSortDir});
9758
            YAHOO.log("Column \"" + oColumn.key + "\" sorted \"" + sSortDir + "\"", "info", this.toString());
9759
            return;
9760
        }
9761
    }
9762
    YAHOO.log("Could not sort Column \"" + oColumn.key + "\"", "warn", this.toString());
9763
},
9764
 
9765
/**
9766
 * Sets given Column to given pixel width. If new width is less than minimum
9767
 * width, sets to minimum width. Updates oColumn.width value.
9768
 *
9769
 * @method setColumnWidth
9770
 * @param oColumn {YAHOO.widget.Column} Column instance.
9771
 * @param nWidth {Number} New width in pixels. A null value auto-sizes Column,
9772
 * subject to minWidth and maxAutoWidth validations.
9773
 */
9774
setColumnWidth : function(oColumn, nWidth) {
9775
    if(!(oColumn instanceof YAHOO.widget.Column)) {
9776
        oColumn = this.getColumn(oColumn);
9777
    }
9778
    if(oColumn) {
9779
        // Validate new width against minimum width
9780
        if(lang.isNumber(nWidth)) {
9781
            // This is why we must require a Number... :-|
9782
            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
9783
 
9784
            // Save state
9785
            oColumn.width = nWidth;
9786
 
9787
            // Resize the DOM elements
9788
            this._setColumnWidth(oColumn, nWidth+"px");
9789
 
9790
            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
9791
            YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
9792
        }
9793
        // Unsets a width to auto-size
9794
        else if(nWidth === null) {
9795
            // Save state
9796
            oColumn.width = nWidth;
9797
 
9798
            // Resize the DOM elements
9799
            this._setColumnWidth(oColumn, "auto");
9800
            this.validateColumnWidths(oColumn);
9801
            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
9802
            YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
9803
        }
9804
 
9805
        // Bug 2339454: resize then sort misaligment
9806
        this._clearTrTemplateEl();
9807
    }
9808
    else {
9809
        YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
9810
    }
9811
},
9812
 
9813
/**
9814
 * Sets liner DIV elements of given Column to given width. When value should be
9815
 * auto-calculated to fit content overflow is set to visible, otherwise overflow
9816
 * is set to hidden. No validations against minimum width and no updating
9817
 * Column.width value.
9818
 *
9819
 * @method _setColumnWidth
9820
 * @param oColumn {YAHOO.widget.Column} Column instance.
9821
 * @param sWidth {String} New width value.
9822
 * @param sOverflow {String} Should be "hidden" when Column width is explicitly
9823
 * being set to a value, but should be "visible" when Column is meant to auto-fit content.
9824
 * @private
9825
 */
9826
_setColumnWidth : function(oColumn, sWidth, sOverflow) {
9827
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
9828
        sOverflow = sOverflow || (((sWidth === '') || (sWidth === 'auto')) ? 'visible' : 'hidden');
9829
 
9830
        // Dynamic style algorithm
9831
        if(!DT._bDynStylesFallback) {
9832
            this._setColumnWidthDynStyles(oColumn, sWidth, sOverflow);
9833
        }
9834
        // Dynamic function algorithm
9835
        else {
9836
            this._setColumnWidthDynFunction(oColumn, sWidth, sOverflow);
9837
        }
9838
    }
9839
    else {
9840
        YAHOO.log("Could not set width of unknown Column " + oColumn + " to " + sWidth, "warn", this.toString());
9841
    }
9842
},
9843
 
9844
/**
9845
 * Updates width of a Column's liner DIV elements by dynamically creating a
9846
 * STYLE node and writing and updating CSS style rules to it. If this fails during
9847
 * runtime, the fallback method _setColumnWidthDynFunction() will be called.
9848
 * Notes: This technique is not performant in IE6. IE7 crashes if DataTable is
9849
 * nested within another TABLE element. For these cases, it is recommended to
9850
 * use the method _setColumnWidthDynFunction by setting _bDynStylesFallback to TRUE.
9851
 *
9852
 * @method _setColumnWidthDynStyles
9853
 * @param oColumn {YAHOO.widget.Column} Column instance.
9854
 * @param sWidth {String} New width value.
9855
 * @private
9856
 */
9857
_setColumnWidthDynStyles : function(oColumn, sWidth, sOverflow) {
9858
    var s = DT._elDynStyleNode,
9859
        rule;
9860
 
9861
    // Create a new STYLE node
9862
    if(!s) {
9863
        s = document.createElement('style');
9864
        s.type = 'text/css';
9865
        s = document.getElementsByTagName('head').item(0).appendChild(s);
9866
        DT._elDynStyleNode = s;
9867
    }
9868
 
9869
    // We have a STYLE node to update
9870
    if(s) {
9871
        // Use unique classname for this Column instance as a hook for resizing
9872
        var sClassname = "." + this.getId() + "-col-" + oColumn.getSanitizedKey() + " ." + DT.CLASS_LINER;
9873
 
9874
        // Hide for performance
9875
        if(this._elTbody) {
9876
            this._elTbody.style.display = 'none';
9877
        }
9878
 
9879
        rule = DT._oDynStyles[sClassname];
9880
 
9881
        // The Column does not yet have a rule
9882
        if(!rule) {
9883
            if(s.styleSheet && s.styleSheet.addRule) {
9884
                s.styleSheet.addRule(sClassname,"overflow:"+sOverflow);
9885
                s.styleSheet.addRule(sClassname,'width:'+sWidth);
9886
                rule = s.styleSheet.rules[s.styleSheet.rules.length-1];
9887
                DT._oDynStyles[sClassname] = rule;
9888
            }
9889
            else if(s.sheet && s.sheet.insertRule) {
9890
                s.sheet.insertRule(sClassname+" {overflow:"+sOverflow+";width:"+sWidth+";}",s.sheet.cssRules.length);
9891
                rule = s.sheet.cssRules[s.sheet.cssRules.length-1];
9892
                DT._oDynStyles[sClassname] = rule;
9893
            }
9894
        }
9895
        // We have a rule to update
9896
        else {
9897
            rule.style.overflow = sOverflow;
9898
            rule.style.width = sWidth;
9899
        }
9900
 
9901
        // Unhide
9902
        if(this._elTbody) {
9903
            this._elTbody.style.display = '';
9904
        }
9905
    }
9906
 
9907
    // That was not a success, we must call the fallback routine
9908
    if(!rule) {
9909
        DT._bDynStylesFallback = true;
9910
        this._setColumnWidthDynFunction(oColumn, sWidth);
9911
    }
9912
},
9913
 
9914
/**
9915
 * Updates width of a Column's liner DIV elements by dynamically creating a
9916
 * function to update all element style properties in one pass. Note: This
9917
 * technique is not supported in sandboxed environments that prohibit EVALs.
9918
 *
9919
 * @method _setColumnWidthDynFunction
9920
 * @param oColumn {YAHOO.widget.Column} Column instance.
9921
 * @param sWidth {String} New width value.
9922
 * @private
9923
 */
9924
_setColumnWidthDynFunction : function(oColumn, sWidth, sOverflow) {
9925
    // TODO: why is this here?
9926
    if(sWidth == 'auto') {
9927
        sWidth = '';
9928
    }
9929
 
9930
    // Create one function for each value of rows.length
9931
    var rowslen = this._elTbody ? this._elTbody.rows.length : 0;
9932
 
9933
    // Dynamically create the function
9934
    if (!this._aDynFunctions[rowslen]) {
9935
 
9936
        //Compile a custom function to do all the liner div width
9937
        //assignments at the same time.  A unique function is required
9938
        //for each unique number of rows in _elTbody.  This will
9939
        //result in a function declaration like:
9940
        //function (oColumn,sWidth,sOverflow) {
9941
        //    var colIdx = oColumn.getKeyIndex();
9942
        //    oColumn.getThLinerEl().style.overflow =
9943
        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.overflow =
9944
        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.overflow =
9945
        //    ... (for all row indices in this._elTbody.rows.length - 1)
9946
        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.overflow =
9947
        //    sOverflow;
9948
        //    oColumn.getThLinerEl().style.width =
9949
        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.width =
9950
        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.width =
9951
        //    ... (for all row indices in this._elTbody.rows.length - 1)
9952
        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.width =
9953
        //    sWidth;
9954
        //}
9955
 
9956
        var i,j,k;
9957
        var resizerDef = [
9958
            'var colIdx=oColumn.getKeyIndex();',
9959
            'oColumn.getThLinerEl().style.overflow='
9960
        ];
9961
        for (i=rowslen-1, j=2; i >= 0; --i) {
9962
            resizerDef[j++] = 'this._elTbody.rows[';
9963
            resizerDef[j++] = i;
9964
            resizerDef[j++] = '].cells[colIdx].firstChild.style.overflow=';
9965
        }
9966
        resizerDef[j] = 'sOverflow;';
9967
        resizerDef[j+1] = 'oColumn.getThLinerEl().style.width=';
9968
        for (i=rowslen-1, k=j+2; i >= 0; --i) {
9969
            resizerDef[k++] = 'this._elTbody.rows[';
9970
            resizerDef[k++] = i;
9971
            resizerDef[k++] = '].cells[colIdx].firstChild.style.width=';
9972
        }
9973
        resizerDef[k] = 'sWidth;';
9974
        this._aDynFunctions[rowslen] =
9975
            new Function('oColumn','sWidth','sOverflow',resizerDef.join(''));
9976
    }
9977
 
9978
    // Get the function to execute
9979
    var resizerFn = this._aDynFunctions[rowslen];
9980
 
9981
    // TODO: Hide TBODY for performance in _setColumnWidthDynFunction?
9982
    if (resizerFn) {
9983
        resizerFn.call(this,oColumn,sWidth,sOverflow);
9984
    }
9985
},
9986
 
9987
/**
9988
 * For one or all Columns, when Column is not hidden, width is not set, and minWidth
9989
 * and/or maxAutoWidth is set, validates auto-width against minWidth and maxAutoWidth.
9990
 *
9991
 * @method validateColumnWidths
9992
 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
9993
 */
9994
validateColumnWidths : function(oColumn) {
9995
    var elColgroup = this._elColgroup;
9996
    var elColgroupClone = elColgroup.cloneNode(true);
9997
    var bNeedsValidation = false;
9998
    var allKeys = this._oColumnSet.keys;
9999
    var elThLiner;
10000
    // Validate just one Column's minWidth and/or maxAutoWidth
10001
    if(oColumn && !oColumn.hidden && !oColumn.width && (oColumn.getKeyIndex() !== null)) {
10002
            elThLiner = oColumn.getThLinerEl();
10003
            if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
10004
                elColgroupClone.childNodes[oColumn.getKeyIndex()].style.width =
10005
                        oColumn.minWidth +
10006
                        (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
10007
                        (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
10008
                bNeedsValidation = true;
10009
            }
10010
            else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
10011
                this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
10012
            }
10013
    }
10014
    // Validate all Columns
10015
    else {
10016
        for(var i=0, len=allKeys.length; i<len; i++) {
10017
            oColumn = allKeys[i];
10018
            if(!oColumn.hidden && !oColumn.width) {
10019
                elThLiner = oColumn.getThLinerEl();
10020
                if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
10021
                    elColgroupClone.childNodes[i].style.width =
10022
                            oColumn.minWidth +
10023
                            (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
10024
                            (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
10025
                    bNeedsValidation = true;
10026
                }
10027
                else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
10028
                    this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
10029
                }
10030
            }
10031
        }
10032
    }
10033
    if(bNeedsValidation) {
10034
        elColgroup.parentNode.replaceChild(elColgroupClone, elColgroup);
10035
        this._elColgroup = elColgroupClone;
10036
    }
10037
},
10038
 
10039
/**
10040
 * Clears minWidth.
10041
 *
10042
 * @method _clearMinWidth
10043
 * @param oColumn {YAHOO.widget.Column} Which Column.
10044
 * @private
10045
 */
10046
_clearMinWidth : function(oColumn) {
10047
    if(oColumn.getKeyIndex() !== null) {
10048
        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = '';
10049
    }
10050
},
10051
 
10052
/**
10053
 * Restores minWidth.
10054
 *
10055
 * @method _restoreMinWidth
10056
 * @param oColumn {YAHOO.widget.Column} Which Column.
10057
 * @private
10058
 */
10059
_restoreMinWidth : function(oColumn) {
10060
    if(oColumn.minWidth && (oColumn.getKeyIndex() !== null)) {
10061
        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = oColumn.minWidth + 'px';
10062
    }
10063
},
10064
 
10065
/**
10066
 * Hides given Column. NOTE: You cannot hide/show nested Columns. You can only
10067
 * hide/show non-nested Columns, and top-level parent Columns (which will
10068
 * hide/show all children Columns).
10069
 *
10070
 * @method hideColumn
10071
 * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column
10072
 * instance, TH/TD element (or child of a TH/TD element), a Column key, or a
10073
 * ColumnSet key index.
10074
 */
10075
hideColumn : function(oColumn) {
10076
    if(!(oColumn instanceof YAHOO.widget.Column)) {
10077
        oColumn = this.getColumn(oColumn);
10078
    }
10079
    // Only top-level Columns can get hidden due to issues in FF2 and SF3
10080
    if(oColumn && !oColumn.hidden && oColumn.getTreeIndex() !== null) {
10081
 
10082
        var allrows = this.getTbodyEl().rows;
10083
        var l = allrows.length;
10084
        var allDescendants = this._oColumnSet.getDescendants(oColumn);
10085
 
10086
        // Hide each nested Column
10087
        for(var i=0, len=allDescendants.length; i<len; i++) {
10088
            var thisColumn = allDescendants[i];
10089
            thisColumn.hidden = true;
10090
 
10091
            // Style the head cell
10092
            Dom.addClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
10093
 
10094
            // Does this Column have body cells?
10095
            var thisKeyIndex = thisColumn.getKeyIndex();
10096
            if(thisKeyIndex !== null) {
10097
                // Clear minWidth
10098
                this._clearMinWidth(oColumn);
10099
 
10100
                // Style the body cells
10101
                for(var j=0;j<l;j++) {
10102
                    Dom.addClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
10103
                }
10104
            }
10105
 
10106
            this.fireEvent("columnHideEvent",{column:thisColumn});
10107
            YAHOO.log("Column \"" + oColumn.key + "\" hidden", "info", this.toString());
10108
        }
10109
 
10110
        this._repaintOpera();
10111
        this._clearTrTemplateEl();
10112
    }
10113
    else {
10114
        YAHOO.log("Could not hide Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be hidden", "warn", this.toString());
10115
    }
10116
},
10117
 
10118
/**
10119
 * Shows given Column. NOTE: You cannot hide/show nested Columns. You can only
10120
 * hide/show non-nested Columns, and top-level parent Columns (which will
10121
 * hide/show all children Columns).
10122
 *
10123
 * @method showColumn
10124
 * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column
10125
 * instance, TH/TD element (or child of a TH/TD element), a Column key, or a
10126
 * ColumnSet key index.
10127
 */
10128
showColumn : function(oColumn) {
10129
    if(!(oColumn instanceof YAHOO.widget.Column)) {
10130
        oColumn = this.getColumn(oColumn);
10131
    }
10132
    // Only top-level Columns can get hidden
10133
    if(oColumn && oColumn.hidden && (oColumn.getTreeIndex() !== null)) {
10134
        var allrows = this.getTbodyEl().rows;
10135
        var l = allrows.length;
10136
        var allDescendants = this._oColumnSet.getDescendants(oColumn);
10137
 
10138
        // Show each nested Column
10139
        for(var i=0, len=allDescendants.length; i<len; i++) {
10140
            var thisColumn = allDescendants[i];
10141
            thisColumn.hidden = false;
10142
 
10143
            // Unstyle the head cell
10144
            Dom.removeClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
10145
 
10146
            // Does this Column have body cells?
10147
            var thisKeyIndex = thisColumn.getKeyIndex();
10148
            if(thisKeyIndex !== null) {
10149
                // Restore minWidth
10150
                this._restoreMinWidth(oColumn);
10151
 
10152
 
10153
                // Unstyle the body cells
10154
                for(var j=0;j<l;j++) {
10155
                    Dom.removeClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
10156
                }
10157
            }
10158
 
10159
            this.fireEvent("columnShowEvent",{column:thisColumn});
10160
            YAHOO.log("Column \"" + oColumn.key + "\" shown", "info", this.toString());
10161
        }
10162
        this._clearTrTemplateEl();
10163
    }
10164
    else {
10165
        YAHOO.log("Could not show Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be shown", "warn", this.toString());
10166
    }
10167
},
10168
 
10169
/**
10170
 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
10171
 * non-nested Columns, and top-level parent Columns (which will remove all
10172
 * children Columns).
10173
 *
10174
 * @method removeColumn
10175
 * @param oColumn {YAHOO.widget.Column} Column instance.
10176
 * @return oColumn {YAHOO.widget.Column} Removed Column instance.
10177
 */
10178
removeColumn : function(oColumn) {
10179
    // Validate Column
10180
    if(!(oColumn instanceof YAHOO.widget.Column)) {
10181
        oColumn = this.getColumn(oColumn);
10182
    }
10183
    if(oColumn) {
10184
        var nColTreeIndex = oColumn.getTreeIndex();
10185
        if(nColTreeIndex !== null) {
10186
            // Which key index(es)
10187
            var i, len,
10188
                aKeyIndexes = oColumn.getKeyIndex();
10189
            // Must be a parent Column
10190
            if(aKeyIndexes === null) {
10191
                var descKeyIndexes = [];
10192
                var allDescendants = this._oColumnSet.getDescendants(oColumn);
10193
                for(i=0, len=allDescendants.length; i<len; i++) {
10194
                    // Is this descendant a key Column?
10195
                    var thisKey = allDescendants[i].getKeyIndex();
10196
                    if(thisKey !== null) {
10197
                        descKeyIndexes[descKeyIndexes.length] = thisKey;
10198
                    }
10199
                }
10200
                if(descKeyIndexes.length > 0) {
10201
                    aKeyIndexes = descKeyIndexes;
10202
                }
10203
            }
10204
            // Must be a key Column
10205
            else {
10206
                aKeyIndexes = [aKeyIndexes];
10207
            }
10208
 
10209
            if(aKeyIndexes !== null) {
10210
                // Sort the indexes so we can remove from the right
10211
                aKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
10212
 
10213
                // Destroy previous THEAD
10214
                this._destroyTheadEl();
10215
 
10216
                // Create new THEAD
10217
                var aOrigColumnDefs = this._oColumnSet.getDefinitions();
10218
                oColumn = aOrigColumnDefs.splice(nColTreeIndex,1)[0];
10219
                this._initColumnSet(aOrigColumnDefs);
10220
                this._initTheadEl();
10221
 
10222
                // Remove COL
10223
                for(i=aKeyIndexes.length-1; i>-1; i--) {
10224
                    this._removeColgroupColEl(aKeyIndexes[i]);
10225
                }
10226
 
10227
                // Remove TD
10228
                var allRows = this._elTbody.rows;
10229
                if(allRows.length > 0) {
10230
                    var loopN = this.get("renderLoopSize"),
10231
                        loopEnd = allRows.length;
10232
                    this._oChainRender.add({
10233
                        method: function(oArg) {
10234
                            if((this instanceof DT) && this._sId) {
10235
                                var i = oArg.nCurrentRow,
10236
                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
10237
                                    aIndexes = oArg.aIndexes,
10238
                                    j;
10239
                                for(; i < len; ++i) {
10240
                                    for(j = aIndexes.length-1; j>-1; j--) {
10241
                                        allRows[i].removeChild(allRows[i].childNodes[aIndexes[j]]);
10242
                                    }
10243
                                }
10244
                                oArg.nCurrentRow = i;
10245
                            }
10246
                        },
10247
                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10248
                        argument: {nCurrentRow:0, aIndexes:aKeyIndexes},
10249
                        scope: this,
10250
                        timeout: (loopN > 0) ? 0 : -1
10251
                    });
10252
                    this._runRenderChain();
10253
                }
10254
 
10255
                this.fireEvent("columnRemoveEvent",{column:oColumn});
10256
                YAHOO.log("Column \"" + oColumn.key + "\" removed", "info", this.toString());
10257
                return oColumn;
10258
            }
10259
        }
10260
    }
10261
    YAHOO.log("Could not remove Column \"" + oColumn.key + "\". Only non-nested Columns can be removed", "warn", this.toString());
10262
},
10263
 
10264
/**
10265
 * Inserts given Column at the index if given, otherwise at the end. NOTE: You
10266
 * can only add non-nested Columns and top-level parent Columns. You cannot add
10267
 * a nested Column to an existing parent.
10268
 *
10269
 * @method insertColumn
10270
 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
10271
 * definition or a Column instance.
10272
 * @param index {Number} (optional) New tree index.
10273
 * @return oColumn {YAHOO.widget.Column} Inserted Column instance.
10274
 */
10275
insertColumn : function(oColumn, index) {
10276
    // Validate Column
10277
    if(oColumn instanceof YAHOO.widget.Column) {
10278
        oColumn = oColumn.getDefinition();
10279
    }
10280
    else if(oColumn.constructor !== Object) {
10281
        YAHOO.log("Could not insert Column \"" + oColumn + "\" due to invalid argument", "warn", this.toString());
10282
        return;
10283
    }
10284
 
10285
    // Validate index or append new Column to the end of the ColumnSet
10286
    var oColumnSet = this._oColumnSet;
10287
    if(!lang.isValue(index) || !lang.isNumber(index)) {
10288
        index = oColumnSet.tree[0].length;
10289
    }
10290
 
10291
    // Destroy previous THEAD
10292
    this._destroyTheadEl();
10293
 
10294
    // Create new THEAD
10295
    var aNewColumnDefs = this._oColumnSet.getDefinitions();
10296
    aNewColumnDefs.splice(index, 0, oColumn);
10297
    this._initColumnSet(aNewColumnDefs);
10298
    this._initTheadEl();
10299
 
10300
    // Need to refresh the reference
10301
    oColumnSet = this._oColumnSet;
10302
    var oNewColumn = oColumnSet.tree[0][index];
10303
 
10304
    // Get key index(es) for new Column
10305
    var i, len,
10306
        descKeyIndexes = [];
10307
    var allDescendants = oColumnSet.getDescendants(oNewColumn);
10308
    for(i=0, len=allDescendants.length; i<len; i++) {
10309
        // Is this descendant a key Column?
10310
        var thisKey = allDescendants[i].getKeyIndex();
10311
        if(thisKey !== null) {
10312
            descKeyIndexes[descKeyIndexes.length] = thisKey;
10313
        }
10314
    }
10315
 
10316
    if(descKeyIndexes.length > 0) {
10317
        // Sort the indexes
10318
        var newIndex = descKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
10319
 
10320
        // Add COL
10321
        for(i=descKeyIndexes.length-1; i>-1; i--) {
10322
            this._insertColgroupColEl(descKeyIndexes[i]);
10323
        }
10324
 
10325
        // Add TD
10326
        var allRows = this._elTbody.rows;
10327
        if(allRows.length > 0) {
10328
            var loopN = this.get("renderLoopSize"),
10329
                loopEnd = allRows.length;
10330
 
10331
            // Get templates for each new TD
10332
            var aTdTemplates = [],
10333
                elTdTemplate;
10334
            for(i=0, len=descKeyIndexes.length; i<len; i++) {
10335
                var thisKeyIndex = descKeyIndexes[i];
10336
                elTdTemplate = this._getTrTemplateEl().childNodes[i].cloneNode(true);
10337
                elTdTemplate = this._formatTdEl(this._oColumnSet.keys[thisKeyIndex], elTdTemplate, thisKeyIndex, (thisKeyIndex===this._oColumnSet.keys.length-1));
10338
                aTdTemplates[thisKeyIndex] = elTdTemplate;
10339
            }
10340
 
10341
            this._oChainRender.add({
10342
                method: function(oArg) {
10343
                    if((this instanceof DT) && this._sId) {
10344
                        var i = oArg.nCurrentRow, j,
10345
                            descKeyIndexes = oArg.descKeyIndexes,
10346
                            len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
10347
                            nextSibling;
10348
                        for(; i < len; ++i) {
10349
                            nextSibling = allRows[i].childNodes[newIndex] || null;
10350
                            for(j=descKeyIndexes.length-1; j>-1; j--) {
10351
                                allRows[i].insertBefore(oArg.aTdTemplates[descKeyIndexes[j]].cloneNode(true), nextSibling);
10352
                            }
10353
                        }
10354
                        oArg.nCurrentRow = i;
10355
                    }
10356
                },
10357
                iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10358
                argument: {nCurrentRow:0,aTdTemplates:aTdTemplates,descKeyIndexes:descKeyIndexes},
10359
                scope: this,
10360
                timeout: (loopN > 0) ? 0 : -1
10361
            });
10362
            this._runRenderChain();
10363
        }
10364
 
10365
        this.fireEvent("columnInsertEvent",{column:oColumn,index:index});
10366
        YAHOO.log("Column \"" + oColumn.key + "\" inserted into index " + index, "info", this.toString());
10367
        return oNewColumn;
10368
    }
10369
},
10370
 
10371
/**
10372
 * Removes given Column and inserts into given tree index. NOTE: You
10373
 * can only reorder non-nested Columns and top-level parent Columns. You cannot
10374
 * reorder a nested Column to an existing parent.
10375
 *
10376
 * @method reorderColumn
10377
 * @param oColumn {YAHOO.widget.Column} Column instance.
10378
 * @param index {Number} New tree index.
10379
 * @return oColumn {YAHOO.widget.Column} Reordered Column instance.
10380
 */
10381
reorderColumn : function(oColumn, index) {
10382
    // Validate Column and new index
10383
    if(!(oColumn instanceof YAHOO.widget.Column)) {
10384
        oColumn = this.getColumn(oColumn);
10385
    }
10386
    if(oColumn && YAHOO.lang.isNumber(index)) {
10387
        var nOrigTreeIndex = oColumn.getTreeIndex();
10388
        if((nOrigTreeIndex !== null) && (nOrigTreeIndex !== index)) {
10389
            // Which key index(es)
10390
            var i, len,
10391
                aOrigKeyIndexes = oColumn.getKeyIndex(),
10392
                allDescendants,
10393
                descKeyIndexes = [],
10394
                thisKey;
10395
            // Must be a parent Column...
10396
            if(aOrigKeyIndexes === null) {
10397
                allDescendants = this._oColumnSet.getDescendants(oColumn);
10398
                for(i=0, len=allDescendants.length; i<len; i++) {
10399
                    // Is this descendant a key Column?
10400
                    thisKey = allDescendants[i].getKeyIndex();
10401
                    if(thisKey !== null) {
10402
                        descKeyIndexes[descKeyIndexes.length] = thisKey;
10403
                    }
10404
                }
10405
                if(descKeyIndexes.length > 0) {
10406
                    aOrigKeyIndexes = descKeyIndexes;
10407
                }
10408
            }
10409
            // ...or else must be a key Column
10410
            else {
10411
                aOrigKeyIndexes = [aOrigKeyIndexes];
10412
            }
10413
 
10414
            if(aOrigKeyIndexes !== null) {
10415
                // Sort the indexes
10416
                aOrigKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
10417
 
10418
                // Destroy previous THEAD
10419
                this._destroyTheadEl();
10420
 
10421
                // Create new THEAD
10422
                var aColumnDefs = this._oColumnSet.getDefinitions();
10423
                var oColumnDef = aColumnDefs.splice(nOrigTreeIndex,1)[0];
10424
                aColumnDefs.splice(index, 0, oColumnDef);
10425
                this._initColumnSet(aColumnDefs);
10426
                this._initTheadEl();
10427
 
10428
                // Need to refresh the reference
10429
                var oNewColumn = this._oColumnSet.tree[0][index];
10430
 
10431
                // What are new key index(es)
10432
                var aNewKeyIndexes = oNewColumn.getKeyIndex();
10433
                // Must be a parent Column
10434
                if(aNewKeyIndexes === null) {
10435
                    descKeyIndexes = [];
10436
                    allDescendants = this._oColumnSet.getDescendants(oNewColumn);
10437
                    for(i=0, len=allDescendants.length; i<len; i++) {
10438
                        // Is this descendant a key Column?
10439
                        thisKey = allDescendants[i].getKeyIndex();
10440
                        if(thisKey !== null) {
10441
                            descKeyIndexes[descKeyIndexes.length] = thisKey;
10442
                        }
10443
                    }
10444
                    if(descKeyIndexes.length > 0) {
10445
                        aNewKeyIndexes = descKeyIndexes;
10446
                    }
10447
                }
10448
                // Must be a key Column
10449
                else {
10450
                    aNewKeyIndexes = [aNewKeyIndexes];
10451
                }
10452
 
10453
                // Sort the new indexes and grab the first one for the new location
10454
                var newIndex = aNewKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
10455
 
10456
                // Reorder COL
10457
                this._reorderColgroupColEl(aOrigKeyIndexes, newIndex);
10458
 
10459
                // Reorder TD
10460
                var allRows = this._elTbody.rows;
10461
                if(allRows.length > 0) {
10462
                    var loopN = this.get("renderLoopSize"),
10463
                        loopEnd = allRows.length;
10464
                    this._oChainRender.add({
10465
                        method: function(oArg) {
10466
                            if((this instanceof DT) && this._sId) {
10467
                                var i = oArg.nCurrentRow, j, tmpTds, nextSibling,
10468
                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
10469
                                    aIndexes = oArg.aIndexes, thisTr;
10470
                                // For each row
10471
                                for(; i < len; ++i) {
10472
                                    tmpTds = [];
10473
                                    thisTr = allRows[i];
10474
 
10475
                                    // Remove each TD
10476
                                    for(j=aIndexes.length-1; j>-1; j--) {
10477
                                        tmpTds.push(thisTr.removeChild(thisTr.childNodes[aIndexes[j]]));
10478
                                    }
10479
 
10480
                                    // Insert each TD
10481
                                    nextSibling = thisTr.childNodes[newIndex] || null;
10482
                                    for(j=tmpTds.length-1; j>-1; j--) {
10483
                                        thisTr.insertBefore(tmpTds[j], nextSibling);
10484
                                    }
10485
                                }
10486
                                oArg.nCurrentRow = i;
10487
                            }
10488
                        },
10489
                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10490
                        argument: {nCurrentRow:0, aIndexes:aOrigKeyIndexes},
10491
                        scope: this,
10492
                        timeout: (loopN > 0) ? 0 : -1
10493
                    });
10494
                    this._runRenderChain();
10495
                }
10496
 
10497
                this.fireEvent("columnReorderEvent",{column:oNewColumn, oldIndex:nOrigTreeIndex});
10498
                YAHOO.log("Column \"" + oNewColumn.key + "\" reordered", "info", this.toString());
10499
                return oNewColumn;
10500
            }
10501
        }
10502
    }
10503
    YAHOO.log("Could not reorder Column \"" + oColumn.key + "\". Only non-nested Columns can be reordered", "warn", this.toString());
10504
},
10505
 
10506
/**
10507
 * Selects given Column. NOTE: You cannot select/unselect nested Columns. You can only
10508
 * select/unselect non-nested Columns, and bottom-level key Columns.
10509
 *
10510
 * @method selectColumn
10511
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10512
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10513
 */
10514
selectColumn : function(oColumn) {
10515
    oColumn = this.getColumn(oColumn);
10516
    if(oColumn && !oColumn.selected) {
10517
        // Only bottom-level Columns can get hidden
10518
        if(oColumn.getKeyIndex() !== null) {
10519
            oColumn.selected = true;
10520
 
10521
            // Update head cell
10522
            var elTh = oColumn.getThEl();
10523
            Dom.addClass(elTh,DT.CLASS_SELECTED);
10524
 
10525
            // Update body cells
10526
            var allRows = this.getTbodyEl().rows;
10527
            var oChainRender = this._oChainRender;
10528
            oChainRender.add({
10529
                method: function(oArg) {
10530
                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10531
                        Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
10532
                    }
10533
                    oArg.rowIndex++;
10534
                },
10535
                scope: this,
10536
                iterations: allRows.length,
10537
                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
10538
            });
10539
 
10540
            this._clearTrTemplateEl();
10541
 
10542
            this._elTbody.style.display = "none";
10543
            this._runRenderChain();
10544
            this._elTbody.style.display = "";
10545
 
10546
            this.fireEvent("columnSelectEvent",{column:oColumn});
10547
            YAHOO.log("Column \"" + oColumn.key + "\" selected", "info", this.toString());
10548
        }
10549
        else {
10550
            YAHOO.log("Could not select Column \"" + oColumn.key + "\". Only non-nested Columns can be selected", "warn", this.toString());
10551
        }
10552
    }
10553
},
10554
 
10555
/**
10556
 * Unselects given Column. NOTE: You cannot select/unselect nested Columns. You can only
10557
 * select/unselect non-nested Columns, and bottom-level key Columns.
10558
 *
10559
 * @method unselectColumn
10560
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10561
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10562
 */
10563
unselectColumn : function(oColumn) {
10564
    oColumn = this.getColumn(oColumn);
10565
    if(oColumn && oColumn.selected) {
10566
        // Only bottom-level Columns can get hidden
10567
        if(oColumn.getKeyIndex() !== null) {
10568
            oColumn.selected = false;
10569
 
10570
            // Update head cell
10571
            var elTh = oColumn.getThEl();
10572
            Dom.removeClass(elTh,DT.CLASS_SELECTED);
10573
 
10574
            // Update body cells
10575
            var allRows = this.getTbodyEl().rows;
10576
            var oChainRender = this._oChainRender;
10577
            oChainRender.add({
10578
                method: function(oArg) {
10579
                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10580
                        Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
10581
                    }
10582
                    oArg.rowIndex++;
10583
                },
10584
                scope: this,
10585
                iterations:allRows.length,
10586
                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
10587
            });
10588
 
10589
            this._clearTrTemplateEl();
10590
 
10591
            this._elTbody.style.display = "none";
10592
            this._runRenderChain();
10593
            this._elTbody.style.display = "";
10594
 
10595
            this.fireEvent("columnUnselectEvent",{column:oColumn});
10596
            YAHOO.log("Column \"" + oColumn.key + "\" unselected", "info", this.toString());
10597
        }
10598
        else {
10599
            YAHOO.log("Could not unselect Column \"" + oColumn.key + "\". Only non-nested Columns can be unselected", "warn", this.toString());
10600
        }
10601
    }
10602
},
10603
 
10604
/**
10605
 * Returns an array selected Column instances.
10606
 *
10607
 * @method getSelectedColumns
10608
 * @return {YAHOO.widget.Column[]} Array of Column instances.
10609
 */
10610
getSelectedColumns : function(oColumn) {
10611
    var selectedColumns = [];
10612
    var aKeys = this._oColumnSet.keys;
10613
    for(var i=0,len=aKeys.length; i<len; i++) {
10614
        if(aKeys[i].selected) {
10615
            selectedColumns[selectedColumns.length] = aKeys[i];
10616
        }
10617
    }
10618
    return selectedColumns;
10619
},
10620
 
10621
/**
10622
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
10623
 * NOTE: You cannot highlight/unhighlight nested Columns. You can only
10624
 * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
10625
 *
10626
 * @method highlightColumn
10627
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10628
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10629
 */
10630
highlightColumn : function(column) {
10631
    var oColumn = this.getColumn(column);
10632
    // Only bottom-level Columns can get highlighted
10633
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
10634
        // Update head cell
10635
        var elTh = oColumn.getThEl();
10636
        Dom.addClass(elTh,DT.CLASS_HIGHLIGHTED);
10637
 
10638
        // Update body cells
10639
        var allRows = this.getTbodyEl().rows;
10640
        var oChainRender = this._oChainRender;
10641
        oChainRender.add({
10642
            method: function(oArg) {
10643
                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10644
                    Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
10645
                }
10646
                oArg.rowIndex++;
10647
            },
10648
            scope: this,
10649
            iterations:allRows.length,
10650
            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
10651
            timeout: -1
10652
        });
10653
        this._elTbody.style.display = "none";
10654
        this._runRenderChain();
10655
        this._elTbody.style.display = "";
10656
 
10657
        this.fireEvent("columnHighlightEvent",{column:oColumn});
10658
        YAHOO.log("Column \"" + oColumn.key + "\" highlighed", "info", this.toString());
10659
    }
10660
    else {
10661
        YAHOO.log("Could not highlight Column \"" + oColumn.key + "\". Only non-nested Columns can be highlighted", "warn", this.toString());
10662
    }
10663
},
10664
 
10665
/**
10666
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
10667
 * NOTE: You cannot highlight/unhighlight nested Columns. You can only
10668
 * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
10669
 *
10670
 * @method unhighlightColumn
10671
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10672
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10673
 */
10674
unhighlightColumn : function(column) {
10675
    var oColumn = this.getColumn(column);
10676
    // Only bottom-level Columns can get highlighted
10677
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
10678
        // Update head cell
10679
        var elTh = oColumn.getThEl();
10680
        Dom.removeClass(elTh,DT.CLASS_HIGHLIGHTED);
10681
 
10682
        // Update body cells
10683
        var allRows = this.getTbodyEl().rows;
10684
        var oChainRender = this._oChainRender;
10685
        oChainRender.add({
10686
            method: function(oArg) {
10687
                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10688
                    Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
10689
                }
10690
                oArg.rowIndex++;
10691
            },
10692
            scope: this,
10693
            iterations:allRows.length,
10694
            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
10695
            timeout: -1
10696
        });
10697
        this._elTbody.style.display = "none";
10698
        this._runRenderChain();
10699
        this._elTbody.style.display = "";
10700
 
10701
        this.fireEvent("columnUnhighlightEvent",{column:oColumn});
10702
        YAHOO.log("Column \"" + oColumn.key + "\" unhighlighted", "info", this.toString());
10703
    }
10704
    else {
10705
        YAHOO.log("Could not unhighlight Column \"" + oColumn.key + "\". Only non-nested Columns can be unhighlighted", "warn", this.toString());
10706
    }
10707
},
10708
 
10709
 
10710
 
10711
 
10712
 
10713
 
10714
 
10715
 
10716
 
10717
 
10718
 
10719
 
10720
 
10721
 
10722
 
10723
 
10724
 
10725
 
10726
 
10727
 
10728
 
10729
 
10730
 
10731
 
10732
 
10733
 
10734
 
10735
 
10736
 
10737
 
10738
 
10739
 
10740
 
10741
 
10742
 
10743
 
10744
 
10745
 
10746
 
10747
 
10748
 
10749
 
10750
 
10751
 
10752
// ROW FUNCTIONS
10753
 
10754
/**
10755
 * Adds one new Record of data into the RecordSet at the index if given,
10756
 * otherwise at the end. If the new Record is in page view, the
10757
 * corresponding DOM elements are also updated.
10758
 *
10759
 * @method addRow
10760
 * @param oData {Object} Object literal of data for the row.
10761
 * @param index {Number} (optional) RecordSet position index at which to add data.
10762
 */
10763
addRow : function(oData, index) {
10764
    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
10765
        YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
10766
        return;
10767
    }
10768
 
10769
    if(oData && lang.isObject(oData)) {
10770
        var oRecord = this._oRecordSet.addRecord(oData, index);
10771
        if(oRecord) {
10772
            var recIndex;
10773
            var oPaginator = this.get('paginator');
10774
 
10775
            // Paginated
10776
            if (oPaginator) {
10777
                // Update the paginator's totalRecords
10778
                var totalRecords = oPaginator.get('totalRecords');
10779
                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
10780
                    oPaginator.set('totalRecords',totalRecords + 1);
10781
                }
10782
 
10783
                recIndex = this.getRecordIndex(oRecord);
10784
                var endRecIndex = (oPaginator.getPageRecords())[1];
10785
 
10786
                // New record affects the view
10787
                if (recIndex <= endRecIndex) {
10788
                    // Defer UI updates to the render method
10789
                    this.render();
10790
                }
10791
 
10792
                this.fireEvent("rowAddEvent", {record:oRecord});
10793
                YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString());
10794
                return;
10795
            }
10796
            // Not paginated
10797
            else {
10798
                recIndex = this.getRecordIndex(oRecord);
10799
                if(lang.isNumber(recIndex)) {
10800
                    // Add the TR element
10801
                    this._oChainRender.add({
10802
                        method: function(oArg) {
10803
                            if((this instanceof DT) && this._sId) {
10804
                                var oRecord = oArg.record;
10805
                                var recIndex = oArg.recIndex;
10806
                                var elNewTr = this._addTrEl(oRecord);
10807
                                if(elNewTr) {
10808
                                    var elNext = (this._elTbody.rows[recIndex]) ? this._elTbody.rows[recIndex] : null;
10809
                                    this._elTbody.insertBefore(elNewTr, elNext);
10810
 
10811
                                    // Set FIRST/LAST
10812
                                    if(recIndex === 0) {
10813
                                        this._setFirstRow();
10814
                                    }
10815
                                    if(elNext === null) {
10816
                                        this._setLastRow();
10817
                                    }
10818
                                    // Set EVEN/ODD
10819
                                    this._setRowStripes();
10820
 
10821
                                    this.hideTableMessage();
10822
 
10823
                                    this.fireEvent("rowAddEvent", {record:oRecord});
10824
                                    YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString());
10825
                                }
10826
                            }
10827
                        },
10828
                        argument: {record: oRecord, recIndex: recIndex},
10829
                        scope: this,
10830
                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
10831
                    });
10832
                    this._runRenderChain();
10833
                    return;
10834
                }
10835
            }
10836
        }
10837
    }
10838
    YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
10839
},
10840
 
10841
/**
10842
 * Convenience method to add multiple rows.
10843
 *
10844
 * @method addRows
10845
 * @param aData {Object[]} Array of object literal data for the rows.
10846
 * @param index {Number} (optional) RecordSet position index at which to add data.
10847
 */
10848
addRows : function(aData, index) {
10849
    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
10850
        YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());
10851
        return;
10852
    }
10853
 
10854
    if(lang.isArray(aData)) {
10855
        var aRecords = this._oRecordSet.addRecords(aData, index);
10856
        if(aRecords) {
10857
            var recIndex = this.getRecordIndex(aRecords[0]);
10858
 
10859
            // Paginated
10860
            var oPaginator = this.get('paginator');
10861
            if (oPaginator) {
10862
                // Update the paginator's totalRecords
10863
                var totalRecords = oPaginator.get('totalRecords');
10864
                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
10865
                    oPaginator.set('totalRecords',totalRecords + aRecords.length);
10866
                }
10867
 
10868
                var endRecIndex = (oPaginator.getPageRecords())[1];
10869
 
10870
                // At least one of the new records affects the view
10871
                if (recIndex <= endRecIndex) {
10872
                    this.render();
10873
                }
10874
 
10875
                this.fireEvent("rowsAddEvent", {records:aRecords});
10876
                YAHOO.log("Added " + aRecords.length +
10877
                        " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
10878
                        " with data " + lang.dump(aData), "info", this.toString());
10879
                return;
10880
            }
10881
            // Not paginated
10882
            else {
10883
                // Add the TR elements
10884
                var loopN = this.get("renderLoopSize");
10885
                var loopEnd = recIndex + aData.length;
10886
                var nRowsNeeded = (loopEnd - recIndex); // how many needed
10887
                var isLast = (recIndex >= this._elTbody.rows.length);
10888
                this._oChainRender.add({
10889
                    method: function(oArg) {
10890
                        if((this instanceof DT) && this._sId) {
10891
                            var aRecords = oArg.aRecords,
10892
                                i = oArg.nCurrentRow,
10893
                                j = oArg.nCurrentRecord,
10894
                                len = loopN > 0 ? Math.min(i + loopN,loopEnd) : loopEnd,
10895
                                df = document.createDocumentFragment(),
10896
                                elNext = (this._elTbody.rows[i]) ? this._elTbody.rows[i] : null;
10897
                            for(; i < len; i++, j++) {
10898
                                df.appendChild(this._addTrEl(aRecords[j]));
10899
                            }
10900
                            this._elTbody.insertBefore(df, elNext);
10901
                            oArg.nCurrentRow = i;
10902
                            oArg.nCurrentRecord = j;
10903
                        }
10904
                    },
10905
                    iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10906
                    argument: {nCurrentRow:recIndex,nCurrentRecord:0,aRecords:aRecords},
10907
                    scope: this,
10908
                    timeout: (loopN > 0) ? 0 : -1
10909
                });
10910
                this._oChainRender.add({
10911
                    method: function(oArg) {
10912
                        var recIndex = oArg.recIndex;
10913
                        // Set FIRST/LAST
10914
                        if(recIndex === 0) {
10915
                            this._setFirstRow();
10916
                        }
10917
                        if(oArg.isLast) {
10918
                            this._setLastRow();
10919
                        }
10920
                        // Set EVEN/ODD
10921
                        this._setRowStripes();
10922
 
10923
                        this.fireEvent("rowsAddEvent", {records:aRecords});
10924
                        YAHOO.log("Added " + aRecords.length +
10925
                                " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
10926
                                " with data " + lang.dump(aData), "info", this.toString());
10927
                    },
10928
                    argument: {recIndex: recIndex, isLast: isLast},
10929
                    scope: this,
10930
                    timeout: -1 // Needs to run immediately after the DOM insertions above
10931
                });
10932
                this._runRenderChain();
10933
                this.hideTableMessage();
10934
                return;
10935
            }
10936
        }
10937
    }
10938
    YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());
10939
},
10940
 
10941
/**
10942
 * For the given row, updates the associated Record with the given data. If the
10943
 * row is on current page, the corresponding DOM elements are also updated.
10944
 *
10945
 * @method updateRow
10946
 * @param row {YAHOO.widget.Record | Number | HTMLElement | String}
10947
 * Which row to update: By Record instance, by Record's RecordSet
10948
 * position index, by HTMLElement reference to the TR element, or by ID string
10949
 * of the TR element.
10950
 * @param oData {Object} Object literal of data for the row.
10951
 */
10952
updateRow : function(row, oData) {
10953
    var index = row;
10954
    if (!lang.isNumber(index)) {
10955
        index = this.getRecordIndex(row);
10956
    }
10957
 
10958
    // Update the Record
10959
    if(lang.isNumber(index) && (index >= 0)) {
10960
        var oRecordSet = this._oRecordSet,
10961
            oldRecord = oRecordSet.getRecord(index);
10962
 
10963
        if(oldRecord) {
10964
            var updatedRecord = this._oRecordSet.setRecord(oData, index),
10965
                elRow = this.getTrEl(oldRecord),
10966
                // Copy data from the Record for the event that gets fired later
10967
                oldData = oldRecord ? oldRecord.getData() : null;
10968
 
10969
            if(updatedRecord) {
10970
                // Update selected rows as necessary
10971
                var tracker = this._aSelections || [],
10972
                i=0,
10973
                oldId = oldRecord.getId(),
10974
                newId = updatedRecord.getId();
10975
                for(; i<tracker.length; i++) {
10976
                    if((tracker[i] === oldId)) {
10977
                        tracker[i] = newId;
10978
                    }
10979
                    else if(tracker[i].recordId === oldId) {
10980
                        tracker[i].recordId = newId;
10981
                    }
10982
                }
10983
 
10984
                // Update anchors as necessary
10985
                if(this._oAnchorRecord && this._oAnchorRecord.getId() === oldId) {
10986
                    this._oAnchorRecord = updatedRecord;
10987
                }
10988
                if(this._oAnchorCell && this._oAnchorCell.record.getId() === oldId) {
10989
                    this._oAnchorCell.record = updatedRecord;
10990
                }
10991
 
10992
                // Update the TR only if row is on current page
10993
                this._oChainRender.add({
10994
                    method: function() {
10995
                        if((this instanceof DT) && this._sId) {
10996
                            // Paginated
10997
                            var oPaginator = this.get('paginator');
10998
                            if (oPaginator) {
10999
                                var pageStartIndex = (oPaginator.getPageRecords())[0],
11000
                                    pageLastIndex = (oPaginator.getPageRecords())[1];
11001
 
11002
                                // At least one of the new records affects the view
11003
                                if ((index >= pageStartIndex) || (index <= pageLastIndex)) {
11004
                                    this.render();
11005
                                }
11006
                            }
11007
                            else {
11008
                                if(elRow) {
11009
                                    this._updateTrEl(elRow, updatedRecord);
11010
                                }
11011
                                else {
11012
                                    this.getTbodyEl().appendChild(this._addTrEl(updatedRecord));
11013
                                }
11014
                            }
11015
                            this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData});
11016
                            YAHOO.log("DataTable row updated: Record ID = " + updatedRecord.getId() +
11017
                                    ", Record index = " + this.getRecordIndex(updatedRecord) +
11018
                                    ", page row index = " + this.getTrIndex(updatedRecord), "info", this.toString());
11019
                        }
11020
                    },
11021
                    scope: this,
11022
                    timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
11023
                });
11024
                this._runRenderChain();
11025
                return;
11026
            }
11027
        }
11028
    }
11029
    YAHOO.log("Could not update row " + row + " with the data : " + lang.dump(oData), "warn", this.toString());
11030
    return;
11031
},
11032
 
11033
/**
11034
 * Starting with the given row, updates associated Records with the given data.
11035
 * The number of rows to update are determined by the array of data provided.
11036
 * Undefined data (i.e., not an object literal) causes a row to be skipped. If
11037
 * any of the rows are on current page, the corresponding DOM elements are also
11038
 * updated.
11039
 *
11040
 * @method updateRows
11041
 * @param startrow {YAHOO.widget.Record | Number | HTMLElement | String}
11042
 * Starting row to update: By Record instance, by Record's RecordSet
11043
 * position index, by HTMLElement reference to the TR element, or by ID string
11044
 * of the TR element.
11045
 * @param aData {Object[]} Array of object literal of data for the rows.
11046
 */
11047
updateRows : function(startrow, aData) {
11048
    if(lang.isArray(aData)) {
11049
        var startIndex = startrow,
11050
            oRecordSet = this._oRecordSet,
11051
            lastRowIndex = oRecordSet.getLength();
11052
 
11053
        if (!lang.isNumber(startrow)) {
11054
            startIndex = this.getRecordIndex(startrow);
11055
        }
11056
 
11057
        if(lang.isNumber(startIndex) && (startIndex >= 0) && (startIndex < oRecordSet.getLength())) {
11058
            var lastIndex = startIndex + aData.length,
11059
                aOldRecords = oRecordSet.getRecords(startIndex, aData.length),
11060
                aNewRecords = oRecordSet.setRecords(aData, startIndex);
11061
            if(aNewRecords) {
11062
                var tracker = this._aSelections || [],
11063
                    i=0, j, newRecord, newId, oldId,
11064
                    anchorRecord = this._oAnchorRecord ? this._oAnchorRecord.getId() : null,
11065
                    anchorCell = this._oAnchorCell ? this._oAnchorCell.record.getId() : null;
11066
                for(; i<aOldRecords.length; i++) {
11067
                    oldId = aOldRecords[i].getId();
11068
                    newRecord = aNewRecords[i];
11069
                    newId = newRecord.getId();
11070
 
11071
                    // Update selected rows as necessary
11072
                    for(j=0; j<tracker.length; j++) {
11073
                        if((tracker[j] === oldId)) {
11074
                            tracker[j] = newId;
11075
                        }
11076
                        else if(tracker[j].recordId === oldId) {
11077
                            tracker[j].recordId = newId;
11078
                        }
11079
                    }
11080
 
11081
                    // Update anchors as necessary
11082
                    if(anchorRecord && anchorRecord === oldId) {
11083
                        this._oAnchorRecord = newRecord;
11084
                    }
11085
                    if(anchorCell && anchorCell === oldId) {
11086
                        this._oAnchorCell.record = newRecord;
11087
                    }
11088
               }
11089
 
11090
                // Paginated
11091
                var oPaginator = this.get('paginator');
11092
                if (oPaginator) {
11093
                    var pageStartIndex = (oPaginator.getPageRecords())[0],
11094
                        pageLastIndex = (oPaginator.getPageRecords())[1];
11095
 
11096
                    // At least one of the new records affects the view
11097
                    if ((startIndex >= pageStartIndex) || (lastIndex <= pageLastIndex)) {
11098
                        this.render();
11099
                    }
11100
 
11101
                    this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
11102
                    YAHOO.log("Added " + aNewRecords.length +
11103
                            " rows starting at index " + startIndex +
11104
                            " with data " + lang.dump(aData), "info", this.toString());
11105
                    return;
11106
                }
11107
                // Not paginated
11108
                else {
11109
                    // Update the TR elements
11110
                    var loopN = this.get("renderLoopSize"),
11111
                        rowCount = aData.length, // how many needed
11112
                        isLast = (lastIndex >= lastRowIndex),
11113
                        isAdding = (lastIndex > lastRowIndex);
11114
 
11115
                    this._oChainRender.add({
11116
                        method: function(oArg) {
11117
                            if((this instanceof DT) && this._sId) {
11118
                                var aRecords = oArg.aRecords,
11119
                                    i = oArg.nCurrentRow,
11120
                                    j = oArg.nDataPointer,
11121
                                    len = loopN > 0 ? Math.min(i+loopN, startIndex+aRecords.length) : startIndex+aRecords.length;
11122
 
11123
                                for(; i < len; i++,j++) {
11124
                                    if(isAdding && (i>=lastRowIndex)) {
11125
                                        this._elTbody.appendChild(this._addTrEl(aRecords[j]));
11126
                                    }
11127
                                    else {
11128
                                        this._updateTrEl(this._elTbody.rows[i], aRecords[j]);
11129
                                    }
11130
                                }
11131
                                oArg.nCurrentRow = i;
11132
                                oArg.nDataPointer = j;
11133
                            }
11134
                        },
11135
                        iterations: (loopN > 0) ? Math.ceil(rowCount/loopN) : 1,
11136
                        argument: {nCurrentRow:startIndex,aRecords:aNewRecords,nDataPointer:0,isAdding:isAdding},
11137
                        scope: this,
11138
                        timeout: (loopN > 0) ? 0 : -1
11139
                    });
11140
                    this._oChainRender.add({
11141
                        method: function(oArg) {
11142
                            var recIndex = oArg.recIndex;
11143
                            // Set FIRST/LAST
11144
                            if(recIndex === 0) {
11145
                                this._setFirstRow();
11146
                            }
11147
                            if(oArg.isLast) {
11148
                                this._setLastRow();
11149
                            }
11150
                            // Set EVEN/ODD
11151
                            this._setRowStripes();
11152
 
11153
                            this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
11154
                            YAHOO.log("Added " + aNewRecords.length +
11155
                                    " rows starting at index " + startIndex +
11156
                                    " with data " + lang.dump(aData), "info", this.toString());
11157
                        },
11158
                        argument: {recIndex: startIndex, isLast: isLast},
11159
                        scope: this,
11160
                        timeout: -1 // Needs to run immediately after the DOM insertions above
11161
                    });
11162
                    this._runRenderChain();
11163
                    this.hideTableMessage();
11164
                    return;
11165
                }
11166
            }
11167
        }
11168
    }
11169
    YAHOO.log("Could not update rows at " + startrow + " with " + lang.dump(aData), "warn", this.toString());
11170
},
11171
 
11172
/**
11173
 * Deletes the given row's Record from the RecordSet. If the row is on current page,
11174
 * the corresponding DOM elements are also deleted.
11175
 *
11176
 * @method deleteRow
11177
 * @param row {HTMLElement | String | Number} DOM element reference or ID string
11178
 * to DataTable page element or RecordSet index.
11179
 */
11180
deleteRow : function(row) {
11181
    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
11182
    if(lang.isNumber(nRecordIndex)) {
11183
        var oRecord = this.getRecord(nRecordIndex);
11184
        if(oRecord) {
11185
            var nTrIndex = this.getTrIndex(nRecordIndex);
11186
 
11187
            // Remove from selection tracker if there
11188
            var sRecordId = oRecord.getId();
11189
            var tracker = this._aSelections || [];
11190
            for(var j=tracker.length-1; j>-1; j--) {
11191
                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
11192
                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
11193
                    tracker.splice(j,1);
11194
                }
11195
            }
11196
 
11197
            // Delete Record from RecordSet
11198
            var oData = this._oRecordSet.deleteRecord(nRecordIndex);
11199
 
11200
            // Update the UI
11201
            if(oData) {
11202
                // If paginated and the deleted row was on this or a prior page, just
11203
                // re-render
11204
                var oPaginator = this.get('paginator');
11205
                if (oPaginator) {
11206
                    // Update the paginator's totalRecords
11207
                    var totalRecords = oPaginator.get('totalRecords'),
11208
                        // must capture before the totalRecords change because
11209
                        // Paginator shifts to previous page automatically
11210
                        rng = oPaginator.getPageRecords();
11211
 
11212
                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
11213
                        oPaginator.set('totalRecords',totalRecords - 1);
11214
                    }
11215
 
11216
                    // The deleted record was on this or a prior page, re-render
11217
                    if (!rng || nRecordIndex <= rng[1]) {
11218
                        this.render();
11219
                    }
11220
 
11221
                    this._oChainRender.add({
11222
                        method: function() {
11223
                            if((this instanceof DT) && this._sId) {
11224
                                this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex, oldData:oData, trElIndex:nTrIndex});
11225
                                YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());
11226
                            }
11227
                        },
11228
                        scope: this,
11229
                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
11230
                    });
11231
                    this._runRenderChain();
11232
                }
11233
                // Not paginated
11234
                else {
11235
                    if(lang.isNumber(nTrIndex)) {
11236
                        this._oChainRender.add({
11237
                            method: function() {
11238
                                if((this instanceof DT) && this._sId) {
11239
                                    var isLast = (nRecordIndex === this._oRecordSet.getLength());//(nTrIndex == this.getLastTrEl().sectionRowIndex);
11240
                                    this._deleteTrEl(nTrIndex);
11241
 
11242
                                    // Post-delete tasks
11243
                                    if(this._elTbody.rows.length > 0) {
11244
                                        // Set FIRST/LAST
11245
                                        if(nTrIndex === 0) {
11246
                                            this._setFirstRow();
11247
                                        }
11248
                                        if(isLast) {
11249
                                            this._setLastRow();
11250
                                        }
11251
                                        // Set EVEN/ODD
11252
                                        if(nTrIndex != this._elTbody.rows.length) {
11253
                                            this._setRowStripes(nTrIndex);
11254
                                        }
11255
                                    }
11256
 
11257
                                    this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,oldData:oData, trElIndex:nTrIndex});
11258
                                    YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());
11259
                                }
11260
                            },
11261
                            scope: this,
11262
                            timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
11263
                        });
11264
                        this._runRenderChain();
11265
                        return;
11266
                    }
11267
                }
11268
            }
11269
        }
11270
    }
11271
    YAHOO.log("Could not delete row: " + row, "warn", this.toString());
11272
    return null;
11273
},
11274
 
11275
/**
11276
 * Convenience method to delete multiple rows.
11277
 *
11278
 * @method deleteRows
11279
 * @param row {HTMLElement | String | Number} DOM element reference or ID string
11280
 * to DataTable page element or RecordSet index.
11281
 * @param count {Number} (optional) How many rows to delete. A negative value
11282
 * will delete towards the beginning.
11283
 */
11284
deleteRows : function(row, count) {
11285
    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
11286
    if(lang.isNumber(nRecordIndex)) {
11287
        var oRecord = this.getRecord(nRecordIndex);
11288
        if(oRecord) {
11289
            var nTrIndex = this.getTrIndex(nRecordIndex);
11290
 
11291
            // Remove from selection tracker if there
11292
            var sRecordId = oRecord.getId();
11293
            var tracker = this._aSelections || [];
11294
            for(var j=tracker.length-1; j>-1; j--) {
11295
                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
11296
                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
11297
                    tracker.splice(j,1);
11298
                }
11299
            }
11300
 
11301
            // Delete Record from RecordSet
11302
            var highIndex = nRecordIndex;
11303
            var lowIndex = nRecordIndex;
11304
 
11305
            // Validate count and account for negative value
11306
            if(count && lang.isNumber(count)) {
11307
                highIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex;
11308
                lowIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1;
11309
                count = (count > 0) ? count : count*-1;
11310
                if(lowIndex < 0) {
11311
                    lowIndex = 0;
11312
                    count = highIndex - lowIndex + 1;
11313
                }
11314
            }
11315
            else {
11316
                count = 1;
11317
            }
11318
 
11319
            var aData = this._oRecordSet.deleteRecords(lowIndex, count);
11320
 
11321
            // Update the UI
11322
            if(aData) {
11323
                var oPaginator = this.get('paginator'),
11324
                    loopN = this.get("renderLoopSize");
11325
                // If paginated and the deleted row was on this or a prior page, just
11326
                // re-render
11327
                if (oPaginator) {
11328
                    // Update the paginator's totalRecords
11329
                    var totalRecords = oPaginator.get('totalRecords'),
11330
                        // must capture before the totalRecords change because
11331
                        // Paginator shifts to previous page automatically
11332
                        rng = oPaginator.getPageRecords();
11333
 
11334
                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
11335
                        oPaginator.set('totalRecords',totalRecords - aData.length);
11336
                    }
11337
 
11338
                    // The records were on this or a prior page, re-render
11339
                    if (!rng || lowIndex <= rng[1]) {
11340
                        this.render();
11341
                    }
11342
 
11343
                    this._oChainRender.add({
11344
                        method: function(oArg) {
11345
                            if((this instanceof DT) && this._sId) {
11346
                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
11347
                                YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
11348
                            }
11349
                        },
11350
                        scope: this,
11351
                        timeout: (loopN > 0) ? 0 : -1
11352
                    });
11353
                    this._runRenderChain();
11354
                    return;
11355
                }
11356
                // Not paginated
11357
                else {
11358
                    if(lang.isNumber(nTrIndex)) {
11359
                        // Delete the TR elements starting with highest index
11360
                        var loopEnd = lowIndex;
11361
                        var nRowsNeeded = count; // how many needed
11362
                        this._oChainRender.add({
11363
                            method: function(oArg) {
11364
                                if((this instanceof DT) && this._sId) {
11365
                                    var i = oArg.nCurrentRow,
11366
                                        len = (loopN > 0) ? (Math.max(i - loopN,loopEnd)-1) : loopEnd-1;
11367
                                    for(; i>len; --i) {
11368
                                        this._deleteTrEl(i);
11369
                                    }
11370
                                    oArg.nCurrentRow = i;
11371
                                }
11372
                            },
11373
                            iterations: (loopN > 0) ? Math.ceil(count/loopN) : 1,
11374
                            argument: {nCurrentRow:highIndex},
11375
                            scope: this,
11376
                            timeout: (loopN > 0) ? 0 : -1
11377
                        });
11378
                        this._oChainRender.add({
11379
                            method: function() {
11380
                                // Post-delete tasks
11381
                                if(this._elTbody.rows.length > 0) {
11382
                                    this._setFirstRow();
11383
                                    this._setLastRow();
11384
                                    this._setRowStripes();
11385
                                }
11386
 
11387
                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
11388
                                YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
11389
                            },
11390
                            scope: this,
11391
                            timeout: -1 // Needs to run immediately after the DOM deletions above
11392
                        });
11393
                        this._runRenderChain();
11394
                        return;
11395
                    }
11396
                }
11397
            }
11398
        }
11399
    }
11400
    YAHOO.log("Could not delete " + count + " rows at row " + row, "warn", this.toString());
11401
    return null;
11402
},
11403
 
11404
 
11405
 
11406
 
11407
 
11408
 
11409
 
11410
 
11411
 
11412
 
11413
 
11414
 
11415
 
11416
 
11417
 
11418
 
11419
 
11420
 
11421
 
11422
 
11423
 
11424
 
11425
 
11426
 
11427
 
11428
 
11429
 
11430
 
11431
 
11432
 
11433
 
11434
 
11435
 
11436
 
11437
 
11438
 
11439
 
11440
 
11441
 
11442
 
11443
 
11444
 
11445
 
11446
 
11447
 
11448
 
11449
// CELL FUNCTIONS
11450
 
11451
/**
11452
 * Outputs markup into the given TD based on given Record.
11453
 *
11454
 * @method formatCell
11455
 * @param elLiner {HTMLElement} The liner DIV element within the TD.
11456
 * @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
11457
 * @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
11458
 */
11459
formatCell : function(elLiner, oRecord, oColumn) {
11460
    if(!oRecord) {
11461
        oRecord = this.getRecord(elLiner);
11462
    }
11463
    if(!oColumn) {
11464
        oColumn = this.getColumn(this.getCellIndex(elLiner.parentNode));
11465
    }
11466
 
11467
    if(oRecord && oColumn) {
11468
        var sField = oColumn.field;
11469
        var oData = oRecord.getData(sField);
11470
 
11471
        var fnFormatter = typeof oColumn.formatter === 'function' ?
11472
                          oColumn.formatter :
11473
                          DT.Formatter[oColumn.formatter+''] ||
11474
                          DT.Formatter.defaultFormatter;
11475
 
11476
        // Apply special formatter
11477
        if(fnFormatter) {
11478
            fnFormatter.call(this, elLiner, oRecord, oColumn, oData);
11479
        }
11480
        else {
11481
            elLiner.innerHTML = oData;
11482
        }
11483
 
11484
        this.fireEvent("cellFormatEvent", {record:oRecord, column:oColumn, key:oColumn.key, el:elLiner});
11485
    }
11486
    else {
11487
        YAHOO.log("Could not format cell " + elLiner, "error", this.toString());
11488
    }
11489
},
11490
 
11491
/**
11492
 * For the given row and column, updates the Record with the given data. If the
11493
 * cell is on current page, the corresponding DOM elements are also updated.
11494
 *
11495
 * @method updateCell
11496
 * @param oRecord {YAHOO.widget.Record} Record instance.
11497
 * @param oColumn {YAHOO.widget.Column | String | Number} A Column key, or a ColumnSet key index.
11498
 * @param oData {Object} New data value for the cell.
11499
 * @param skipRender {Boolean} Skips render step. Editors that update multiple
11500
 * cells in ScrollingDataTable should render only on the last call to updateCell().
11501
 */
11502
updateCell : function(oRecord, oColumn, oData, skipRender) {
11503
    // Validate Column and Record
11504
    oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn);
11505
    if(oColumn && oColumn.getField() && (oRecord instanceof YAHOO.widget.Record)) {
11506
        var sKey = oColumn.getField(),
11507
 
11508
        // Copy data from the Record for the event that gets fired later
11509
        //var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData());
11510
            oldData = oRecord.getData(sKey);
11511
 
11512
        // Update Record with new data
11513
        this._oRecordSet.updateRecordValue(oRecord, sKey, oData);
11514
 
11515
        // Update the TD only if row is on current page
11516
        var elTd = this.getTdEl({record: oRecord, column: oColumn});
11517
        if(elTd) {
11518
            this._oChainRender.add({
11519
                method: function() {
11520
                    if((this instanceof DT) && this._sId) {
11521
                        this.formatCell(elTd.firstChild, oRecord, oColumn);
11522
                        this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
11523
                        YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
11524
                                ", Record index = " + this.getRecordIndex(oRecord) +
11525
                                ", page row index = " + this.getTrIndex(oRecord) +
11526
                                ", Column key = " + oColumn.getKey(), "info", this.toString());
11527
                    }
11528
                },
11529
                scope: this,
11530
                timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
11531
            });
11532
            // Bug 2529024
11533
            if(!skipRender) {
11534
                this._runRenderChain();
11535
            }
11536
        }
11537
        else {
11538
            this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
11539
            YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
11540
                    ", Record index = " + this.getRecordIndex(oRecord) +
11541
                    ", page row index = " + this.getTrIndex(oRecord) +
11542
                    ", Column key = " + oColumn.getKey(), "info", this.toString());
11543
        }
11544
    }
11545
},
11546
 
11547
 
11548
 
11549
 
11550
 
11551
 
11552
 
11553
 
11554
 
11555
 
11556
 
11557
 
11558
 
11559
 
11560
 
11561
 
11562
 
11563
 
11564
 
11565
 
11566
 
11567
 
11568
 
11569
 
11570
 
11571
 
11572
 
11573
 
11574
 
11575
 
11576
 
11577
 
11578
 
11579
 
11580
 
11581
 
11582
 
11583
 
11584
 
11585
 
11586
 
11587
 
11588
 
11589
 
11590
 
11591
 
11592
 
11593
 
11594
 
11595
 
11596
 
11597
// PAGINATION
11598
/**
11599
 * Method executed during set() operation for the "paginator" attribute.
11600
 * Adds and/or severs event listeners between DataTable and Paginator
11601
 *
11602
 * @method _updatePaginator
11603
 * @param newPag {Paginator} Paginator instance (or null) for DataTable to use
11604
 * @private
11605
 */
11606
_updatePaginator : function (newPag) {
11607
    var oldPag = this.get('paginator');
11608
    if (oldPag && newPag !== oldPag) {
11609
        oldPag.unsubscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
11610
    }
11611
    if (newPag) {
11612
        newPag.subscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
11613
    }
11614
},
11615
 
11616
/**
11617
 * Update the UI infrastructure in response to a "paginator" attribute change.
11618
 *
11619
 * @method _handlePaginatorChange
11620
 * @param e {Object} Change event object containing keys 'type','newValue',
11621
 *                   and 'prevValue'
11622
 * @private
11623
 */
11624
_handlePaginatorChange : function (e) {
11625
    if (e.prevValue === e.newValue) { return; }
11626
 
11627
    var newPag     = e.newValue,
11628
        oldPag     = e.prevValue,
11629
        containers = this._defaultPaginatorContainers();
11630
 
11631
    if (oldPag) {
11632
        if (oldPag.getContainerNodes()[0] == containers[0]) {
11633
            oldPag.set('containers',[]);
11634
        }
11635
        oldPag.destroy();
11636
 
11637
        // Convenience: share the default containers if possible.
11638
        // Otherwise, remove the default containers from the DOM.
11639
        if (containers[0]) {
11640
            if (newPag && !newPag.getContainerNodes().length) {
11641
                newPag.set('containers',containers);
11642
            } else {
11643
                // No new Paginator to use existing containers, OR new
11644
                // Paginator has configured containers.
11645
                for (var i = containers.length - 1; i >= 0; --i) {
11646
                    if (containers[i]) {
11647
                        containers[i].parentNode.removeChild(containers[i]);
11648
                    }
11649
                }
11650
            }
11651
        }
11652
    }
11653
 
11654
    if (!this._bInit) {
11655
        this.render();
11656
 
11657
    }
11658
 
11659
    if (newPag) {
11660
        this.renderPaginator();
11661
    }
11662
 
11663
},
11664
 
11665
/**
11666
 * Returns the default containers used for Paginators.  If create param is
11667
 * passed, the containers will be created and added to the DataTable container.
11668
 *
11669
 * @method _defaultPaginatorContainers
11670
 * @param create {boolean} Create the default containers if not found
11671
 * @private
11672
 */
11673
_defaultPaginatorContainers : function (create) {
11674
    var above_id = this._sId + '-paginator0',
11675
        below_id = this._sId + '-paginator1',
11676
        above    = Dom.get(above_id),
11677
        below    = Dom.get(below_id);
11678
 
11679
    if (create && (!above || !below)) {
11680
        // One above and one below the table
11681
        if (!above) {
11682
            above    = document.createElement('div');
11683
            above.id = above_id;
11684
            Dom.addClass(above, DT.CLASS_PAGINATOR);
11685
 
11686
            this._elContainer.insertBefore(above,this._elContainer.firstChild);
11687
        }
11688
 
11689
        if (!below) {
11690
            below    = document.createElement('div');
11691
            below.id = below_id;
11692
            Dom.addClass(below, DT.CLASS_PAGINATOR);
11693
 
11694
            this._elContainer.appendChild(below);
11695
        }
11696
    }
11697
 
11698
    return [above,below];
11699
},
11700
 
11701
/**
11702
 * Calls Paginator's destroy() method
11703
 *
11704
 * @method _destroyPaginator
11705
 * @private
11706
 */
11707
_destroyPaginator : function () {
11708
    var oldPag = this.get('paginator');
11709
    if (oldPag) {
11710
        oldPag.destroy();
11711
    }
11712
},
11713
 
11714
/**
11715
 * Renders the Paginator to the DataTable UI
11716
 *
11717
 * @method renderPaginator
11718
 */
11719
renderPaginator : function () {
11720
    var pag = this.get("paginator");
11721
    if (!pag) { return; }
11722
 
11723
    // Add the containers if the Paginator is not configured with containers
11724
    if (!pag.getContainerNodes().length) {
11725
        pag.set('containers',this._defaultPaginatorContainers(true));
11726
    }
11727
 
11728
    pag.render();
11729
},
11730
 
11731
/**
11732
 * Overridable method gives implementers a hook to show loading message before
11733
 * changing Paginator value.
11734
 *
11735
 * @method doBeforePaginatorChange
11736
 * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
11737
 * @return {Boolean} Return true to continue changing Paginator value.
11738
 */
11739
doBeforePaginatorChange : function(oPaginatorState) {
11740
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
11741
    return true;
11742
},
11743
 
11744
/**
11745
 * Responds to new Pagination states. By default, updates the UI to reflect the
11746
 * new state. If "dynamicData" is true, current selections are purged before
11747
 * a request is sent to the DataSource for data for the new state (using the
11748
 * request returned by "generateRequest()").
11749
 *
11750
 * @method onPaginatorChangeRequest
11751
 * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
11752
 */
11753
onPaginatorChangeRequest : function (oPaginatorState) {
11754
    var ok = this.doBeforePaginatorChange(oPaginatorState);
11755
    if(ok) {
11756
        // Server-side pagination
11757
        if(this.get("dynamicData")) {
11758
            // Get the current state
11759
            var oState = this.getState();
11760
 
11761
            // Update pagination values
11762
            oState.pagination = oPaginatorState;
11763
 
11764
            // Get the request for the new state
11765
            var request = this.get("generateRequest")(oState, this);
11766
 
11767
            // Purge selections
11768
            this.unselectAllRows();
11769
            this.unselectAllCells();
11770
 
11771
            // Get the new data from the server
11772
            var callback = {
11773
                success : this.onDataReturnSetRows,
11774
                failure : this.onDataReturnSetRows,
11775
                argument : oState, // Pass along the new state to the callback
11776
                scope : this
11777
            };
11778
            this._oDataSource.sendRequest(request, callback);
11779
        }
11780
        // Client-side pagination
11781
        else {
11782
            // Set the core pagination values silently (the second param)
11783
            // to avoid looping back through the changeRequest mechanism
11784
            oPaginatorState.paginator.setStartIndex(oPaginatorState.recordOffset,true);
11785
            oPaginatorState.paginator.setRowsPerPage(oPaginatorState.rowsPerPage,true);
11786
 
11787
            // Update the UI
11788
            this.render();
11789
        }
11790
    }
11791
    else {
11792
        YAHOO.log("Could not change Paginator value \"" + oPaginatorState + "\"", "warn", this.toString());
11793
    }
11794
},
11795
 
11796
 
11797
 
11798
 
11799
 
11800
 
11801
 
11802
 
11803
 
11804
 
11805
 
11806
 
11807
 
11808
 
11809
 
11810
 
11811
 
11812
 
11813
 
11814
 
11815
 
11816
 
11817
 
11818
 
11819
 
11820
 
11821
 
11822
 
11823
 
11824
 
11825
 
11826
 
11827
 
11828
 
11829
 
11830
 
11831
 
11832
 
11833
 
11834
 
11835
 
11836
 
11837
 
11838
 
11839
 
11840
 
11841
 
11842
 
11843
 
11844
 
11845
// SELECTION/HIGHLIGHTING
11846
 
11847
/*
11848
 * Reference to last highlighted cell element
11849
 *
11850
 * @property _elLastHighlightedTd
11851
 * @type HTMLElement
11852
 * @private
11853
 */
11854
_elLastHighlightedTd : null,
11855
 
11856
/*
11857
 * ID string of last highlighted row element
11858
 *
11859
 * @property _sLastHighlightedTrElId
11860
 * @type String
11861
 * @private
11862
 */
11863
//_sLastHighlightedTrElId : null,
11864
 
11865
/**
11866
 * Array to track row selections (by sRecordId) and/or cell selections
11867
 * (by {recordId:sRecordId, columnKey:sColumnKey})
11868
 *
11869
 * @property _aSelections
11870
 * @type Object[]
11871
 * @private
11872
 */
11873
_aSelections : null,
11874
 
11875
/**
11876
 * Record instance of the row selection anchor.
11877
 *
11878
 * @property _oAnchorRecord
11879
 * @type YAHOO.widget.Record
11880
 * @private
11881
 */
11882
_oAnchorRecord : null,
11883
 
11884
/**
11885
 * Object literal representing cell selection anchor:
11886
 * {recordId:sRecordId, columnKey:sColumnKey}.
11887
 *
11888
 * @property _oAnchorCell
11889
 * @type Object
11890
 * @private
11891
 */
11892
_oAnchorCell : null,
11893
 
11894
/**
11895
 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
11896
 * from all TR elements on the page.
11897
 *
11898
 * @method _unselectAllTrEls
11899
 * @private
11900
 */
11901
_unselectAllTrEls : function() {
11902
    var selectedRows = Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
11903
    Dom.removeClass(selectedRows, DT.CLASS_SELECTED);
11904
},
11905
 
11906
/**
11907
 * Returns object literal of values that represent the selection trigger. Used
11908
 * to determine selection behavior resulting from a key event.
11909
 *
11910
 * @method _getSelectionTrigger
11911
 * @private
11912
 */
11913
_getSelectionTrigger : function() {
11914
    var sMode = this.get("selectionMode");
11915
    var oTrigger = {};
11916
    var oTriggerCell, oTriggerRecord, nTriggerRecordIndex, elTriggerRow, nTriggerTrIndex;
11917
 
11918
    // Cell mode
11919
    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
11920
        oTriggerCell = this.getLastSelectedCell();
11921
        // No selected cells found
11922
        if(!oTriggerCell) {
11923
            return null;
11924
        }
11925
        else {
11926
            oTriggerRecord = this.getRecord(oTriggerCell.recordId);
11927
            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
11928
            elTriggerRow = this.getTrEl(oTriggerRecord);
11929
            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
11930
 
11931
            // Selected cell not found on this page
11932
            if(nTriggerTrIndex === null) {
11933
                return null;
11934
            }
11935
            else {
11936
                oTrigger.record = oTriggerRecord;
11937
                oTrigger.recordIndex = nTriggerRecordIndex;
11938
                oTrigger.el = this.getTdEl(oTriggerCell);
11939
                oTrigger.trIndex = nTriggerTrIndex;
11940
                oTrigger.column = this.getColumn(oTriggerCell.columnKey);
11941
                oTrigger.colKeyIndex = oTrigger.column.getKeyIndex();
11942
                oTrigger.cell = oTriggerCell;
11943
                return oTrigger;
11944
            }
11945
        }
11946
    }
11947
    // Row mode
11948
    else {
11949
        oTriggerRecord = this.getLastSelectedRecord();
11950
        // No selected rows found
11951
        if(!oTriggerRecord) {
11952
                return null;
11953
        }
11954
        else {
11955
            // Selected row found, but is it on current page?
11956
            oTriggerRecord = this.getRecord(oTriggerRecord);
11957
            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
11958
            elTriggerRow = this.getTrEl(oTriggerRecord);
11959
            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
11960
 
11961
            // Selected row not found on this page
11962
            if(nTriggerTrIndex === null) {
11963
                return null;
11964
            }
11965
            else {
11966
                oTrigger.record = oTriggerRecord;
11967
                oTrigger.recordIndex = nTriggerRecordIndex;
11968
                oTrigger.el = elTriggerRow;
11969
                oTrigger.trIndex = nTriggerTrIndex;
11970
                return oTrigger;
11971
            }
11972
        }
11973
    }
11974
},
11975
 
11976
/**
11977
 * Returns object literal of values that represent the selection anchor. Used
11978
 * to determine selection behavior resulting from a user event.
11979
 *
11980
 * @method _getSelectionAnchor
11981
 * @param oTrigger {Object} (Optional) Object literal of selection trigger values
11982
 * (for key events).
11983
 * @private
11984
 */
11985
_getSelectionAnchor : function(oTrigger) {
11986
    var sMode = this.get("selectionMode");
11987
    var oAnchor = {};
11988
    var oAnchorRecord, nAnchorRecordIndex, nAnchorTrIndex;
11989
 
11990
    // Cell mode
11991
    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
11992
        // Validate anchor cell
11993
        var oAnchorCell = this._oAnchorCell;
11994
        if(!oAnchorCell) {
11995
            if(oTrigger) {
11996
                oAnchorCell = this._oAnchorCell = oTrigger.cell;
11997
            }
11998
            else {
11999
                return null;
12000
            }
12001
        }
12002
        oAnchorRecord = this._oAnchorCell.record;
12003
        nAnchorRecordIndex = this._oRecordSet.getRecordIndex(oAnchorRecord);
12004
        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
12005
        // If anchor cell is not on this page...
12006
        if(nAnchorTrIndex === null) {
12007
            // ...set TR index equal to top TR
12008
            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
12009
                nAnchorTrIndex = 0;
12010
            }
12011
            // ...set TR index equal to bottom TR
12012
            else {
12013
                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
12014
            }
12015
        }
12016
 
12017
        oAnchor.record = oAnchorRecord;
12018
        oAnchor.recordIndex = nAnchorRecordIndex;
12019
        oAnchor.trIndex = nAnchorTrIndex;
12020
        oAnchor.column = this._oAnchorCell.column;
12021
        oAnchor.colKeyIndex = oAnchor.column.getKeyIndex();
12022
        oAnchor.cell = oAnchorCell;
12023
        return oAnchor;
12024
    }
12025
    // Row mode
12026
    else {
12027
        oAnchorRecord = this._oAnchorRecord;
12028
        if(!oAnchorRecord) {
12029
            if(oTrigger) {
12030
                oAnchorRecord = this._oAnchorRecord = oTrigger.record;
12031
            }
12032
            else {
12033
                return null;
12034
            }
12035
        }
12036
 
12037
        nAnchorRecordIndex = this.getRecordIndex(oAnchorRecord);
12038
        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
12039
        // If anchor row is not on this page...
12040
        if(nAnchorTrIndex === null) {
12041
            // ...set TR index equal to top TR
12042
            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
12043
                nAnchorTrIndex = 0;
12044
            }
12045
            // ...set TR index equal to bottom TR
12046
            else {
12047
                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
12048
            }
12049
        }
12050
 
12051
        oAnchor.record = oAnchorRecord;
12052
        oAnchor.recordIndex = nAnchorRecordIndex;
12053
        oAnchor.trIndex = nAnchorTrIndex;
12054
        return oAnchor;
12055
    }
12056
},
12057
 
12058
/**
12059
 * Determines selection behavior resulting from a mouse event when selection mode
12060
 * is set to "standard".
12061
 *
12062
 * @method _handleStandardSelectionByMouse
12063
 * @param oArgs.event {HTMLEvent} Event object.
12064
 * @param oArgs.target {HTMLElement} Target element.
12065
 * @private
12066
 */
12067
_handleStandardSelectionByMouse : function(oArgs) {
12068
    var elTarget = oArgs.target;
12069
 
12070
    // Validate target row
12071
    var elTargetRow = this.getTrEl(elTarget);
12072
    if(elTargetRow) {
12073
        var e = oArgs.event;
12074
        var bSHIFT = e.shiftKey;
12075
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
12076
 
12077
        var oTargetRecord = this.getRecord(elTargetRow);
12078
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
12079
 
12080
        var oAnchor = this._getSelectionAnchor();
12081
 
12082
        var i;
12083
 
12084
        // Both SHIFT and CTRL
12085
        if(bSHIFT && bCTRL) {
12086
            // Validate anchor
12087
            if(oAnchor) {
12088
                if(this.isSelected(oAnchor.record)) {
12089
                    // Select all rows between anchor row and target row, including target row
12090
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
12091
                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex; i++) {
12092
                            if(!this.isSelected(i)) {
12093
                                this.selectRow(i);
12094
                            }
12095
                        }
12096
                    }
12097
                    // Select all rows between target row and anchor row, including target row
12098
                    else {
12099
                        for(i=oAnchor.recordIndex-1; i>=nTargetRecordIndex; i--) {
12100
                            if(!this.isSelected(i)) {
12101
                                this.selectRow(i);
12102
                            }
12103
                        }
12104
                    }
12105
                }
12106
                else {
12107
                    // Unselect all rows between anchor row and target row
12108
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
12109
                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex-1; i++) {
12110
                            if(this.isSelected(i)) {
12111
                                this.unselectRow(i);
12112
                            }
12113
                        }
12114
                    }
12115
                    // Unselect all rows between target row and anchor row
12116
                    else {
12117
                        for(i=nTargetRecordIndex+1; i<=oAnchor.recordIndex-1; i++) {
12118
                            if(this.isSelected(i)) {
12119
                                this.unselectRow(i);
12120
                            }
12121
                        }
12122
                    }
12123
                    // Select the target row
12124
                    this.selectRow(oTargetRecord);
12125
                }
12126
            }
12127
            // Invalid anchor
12128
            else {
12129
                // Set anchor
12130
                this._oAnchorRecord = oTargetRecord;
12131
 
12132
                // Toggle selection of target
12133
                if(this.isSelected(oTargetRecord)) {
12134
                    this.unselectRow(oTargetRecord);
12135
                }
12136
                else {
12137
                    this.selectRow(oTargetRecord);
12138
                }
12139
            }
12140
        }
12141
         // Only SHIFT
12142
        else if(bSHIFT) {
12143
            this.unselectAllRows();
12144
 
12145
            // Validate anchor
12146
            if(oAnchor) {
12147
                // Select all rows between anchor row and target row,
12148
                // including the anchor row and target row
12149
                if(oAnchor.recordIndex < nTargetRecordIndex) {
12150
                    for(i=oAnchor.recordIndex; i<=nTargetRecordIndex; i++) {
12151
                        this.selectRow(i);
12152
                    }
12153
                }
12154
                // Select all rows between target row and anchor row,
12155
                // including the target row and anchor row
12156
                else {
12157
                    for(i=oAnchor.recordIndex; i>=nTargetRecordIndex; i--) {
12158
                        this.selectRow(i);
12159
                    }
12160
                }
12161
            }
12162
            // Invalid anchor
12163
            else {
12164
                // Set anchor
12165
                this._oAnchorRecord = oTargetRecord;
12166
 
12167
                // Select target row only
12168
                this.selectRow(oTargetRecord);
12169
            }
12170
        }
12171
        // Only CTRL
12172
        else if(bCTRL) {
12173
            // Set anchor
12174
            this._oAnchorRecord = oTargetRecord;
12175
 
12176
            // Toggle selection of target
12177
            if(this.isSelected(oTargetRecord)) {
12178
                this.unselectRow(oTargetRecord);
12179
            }
12180
            else {
12181
                this.selectRow(oTargetRecord);
12182
            }
12183
        }
12184
        // Neither SHIFT nor CTRL
12185
        else {
12186
            this._handleSingleSelectionByMouse(oArgs);
12187
            return;
12188
        }
12189
    }
12190
},
12191
 
12192
/**
12193
 * Determines selection behavior resulting from a key event when selection mode
12194
 * is set to "standard".
12195
 *
12196
 * @method _handleStandardSelectionByKey
12197
 * @param e {HTMLEvent} Event object.
12198
 * @private
12199
 */
12200
_handleStandardSelectionByKey : function(e) {
12201
    var nKey = Ev.getCharCode(e);
12202
 
12203
    if((nKey == 38) || (nKey == 40)) {
12204
        var bSHIFT = e.shiftKey;
12205
 
12206
        // Validate trigger
12207
        var oTrigger = this._getSelectionTrigger();
12208
        // Arrow selection only works if last selected row is on current page
12209
        if(!oTrigger) {
12210
            return null;
12211
        }
12212
 
12213
        Ev.stopEvent(e);
12214
 
12215
        // Validate anchor
12216
        var oAnchor = this._getSelectionAnchor(oTrigger);
12217
 
12218
        // Determine which direction we're going to
12219
        if(bSHIFT) {
12220
            // Selecting down away from anchor row
12221
            if((nKey == 40) && (oAnchor.recordIndex <= oTrigger.trIndex)) {
12222
                this.selectRow(this.getNextTrEl(oTrigger.el));
12223
            }
12224
            // Selecting up away from anchor row
12225
            else if((nKey == 38) && (oAnchor.recordIndex >= oTrigger.trIndex)) {
12226
                this.selectRow(this.getPreviousTrEl(oTrigger.el));
12227
            }
12228
            // Unselect trigger
12229
            else {
12230
                this.unselectRow(oTrigger.el);
12231
            }
12232
        }
12233
        else {
12234
            this._handleSingleSelectionByKey(e);
12235
        }
12236
    }
12237
},
12238
 
12239
/**
12240
 * Determines selection behavior resulting from a mouse event when selection mode
12241
 * is set to "single".
12242
 *
12243
 * @method _handleSingleSelectionByMouse
12244
 * @param oArgs.event {HTMLEvent} Event object.
12245
 * @param oArgs.target {HTMLElement} Target element.
12246
 * @private
12247
 */
12248
_handleSingleSelectionByMouse : function(oArgs) {
12249
    var elTarget = oArgs.target;
12250
 
12251
    // Validate target row
12252
    var elTargetRow = this.getTrEl(elTarget);
12253
    if(elTargetRow) {
12254
        var oTargetRecord = this.getRecord(elTargetRow);
12255
 
12256
        // Set anchor
12257
        this._oAnchorRecord = oTargetRecord;
12258
 
12259
        // Select only target
12260
        this.unselectAllRows();
12261
        this.selectRow(oTargetRecord);
12262
    }
12263
},
12264
 
12265
/**
12266
 * Determines selection behavior resulting from a key event when selection mode
12267
 * is set to "single".
12268
 *
12269
 * @method _handleSingleSelectionByKey
12270
 * @param e {HTMLEvent} Event object.
12271
 * @private
12272
 */
12273
_handleSingleSelectionByKey : function(e) {
12274
    var nKey = Ev.getCharCode(e);
12275
 
12276
    if((nKey == 38) || (nKey == 40)) {
12277
        // Validate trigger
12278
        var oTrigger = this._getSelectionTrigger();
12279
        // Arrow selection only works if last selected row is on current page
12280
        if(!oTrigger) {
12281
            return null;
12282
        }
12283
 
12284
        Ev.stopEvent(e);
12285
 
12286
        // Determine the new row to select
12287
        var elNew;
12288
        if(nKey == 38) { // arrow up
12289
            elNew = this.getPreviousTrEl(oTrigger.el);
12290
 
12291
            // Validate new row
12292
            if(elNew === null) {
12293
                //TODO: wrap around to last tr on current page
12294
                //elNew = this.getLastTrEl();
12295
 
12296
                //TODO: wrap back to last tr of previous page
12297
 
12298
                // Top row selection is sticky
12299
                elNew = this.getFirstTrEl();
12300
            }
12301
        }
12302
        else if(nKey == 40) { // arrow down
12303
            elNew = this.getNextTrEl(oTrigger.el);
12304
 
12305
            // Validate new row
12306
            if(elNew === null) {
12307
                //TODO: wrap around to first tr on current page
12308
                //elNew = this.getFirstTrEl();
12309
 
12310
                //TODO: wrap forward to first tr of previous page
12311
 
12312
                // Bottom row selection is sticky
12313
                elNew = this.getLastTrEl();
12314
            }
12315
        }
12316
 
12317
        // Unselect all rows
12318
        this.unselectAllRows();
12319
 
12320
        // Select the new row
12321
        this.selectRow(elNew);
12322
 
12323
        // Set new anchor
12324
        this._oAnchorRecord = this.getRecord(elNew);
12325
    }
12326
},
12327
 
12328
/**
12329
 * Determines selection behavior resulting from a mouse event when selection mode
12330
 * is set to "cellblock".
12331
 *
12332
 * @method _handleCellBlockSelectionByMouse
12333
 * @param oArgs.event {HTMLEvent} Event object.
12334
 * @param oArgs.target {HTMLElement} Target element.
12335
 * @private
12336
 */
12337
_handleCellBlockSelectionByMouse : function(oArgs) {
12338
    var elTarget = oArgs.target;
12339
 
12340
    // Validate target cell
12341
    var elTargetCell = this.getTdEl(elTarget);
12342
    if(elTargetCell) {
12343
        var e = oArgs.event;
12344
        var bSHIFT = e.shiftKey;
12345
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
12346
 
12347
        var elTargetRow = this.getTrEl(elTargetCell);
12348
        var nTargetTrIndex = this.getTrIndex(elTargetRow);
12349
        var oTargetColumn = this.getColumn(elTargetCell);
12350
        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
12351
        var oTargetRecord = this.getRecord(elTargetRow);
12352
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
12353
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
12354
 
12355
        var oAnchor = this._getSelectionAnchor();
12356
 
12357
        var allRows = this.getTbodyEl().rows;
12358
        var startIndex, endIndex, currentRow, i, j;
12359
 
12360
        // Both SHIFT and CTRL
12361
        if(bSHIFT && bCTRL) {
12362
 
12363
            // Validate anchor
12364
            if(oAnchor) {
12365
                // Anchor is selected
12366
                if(this.isSelected(oAnchor.cell)) {
12367
                    // All cells are on the same row
12368
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12369
                        // Select all cells between anchor cell and target cell, including target cell
12370
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12371
                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
12372
                                this.selectCell(elTargetRow.cells[i]);
12373
                            }
12374
                        }
12375
                        // Select all cells between target cell and anchor cell, including target cell
12376
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12377
                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
12378
                                this.selectCell(elTargetRow.cells[i]);
12379
                            }
12380
                        }
12381
                    }
12382
                    // Anchor row is above target row
12383
                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
12384
                        startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
12385
                        endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
12386
 
12387
                        // Select all cells from startIndex to endIndex on rows between anchor row and target row
12388
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12389
                            for(j=startIndex; j<=endIndex; j++) {
12390
                                this.selectCell(allRows[i].cells[j]);
12391
                            }
12392
                        }
12393
                    }
12394
                    // Anchor row is below target row
12395
                    else {
12396
                        startIndex = Math.min(oAnchor.trIndex, nTargetColKeyIndex);
12397
                        endIndex = Math.max(oAnchor.trIndex, nTargetColKeyIndex);
12398
 
12399
                        // Select all cells from startIndex to endIndex on rows between target row and anchor row
12400
                        for(i=oAnchor.trIndex; i>=nTargetTrIndex; i--) {
12401
                            for(j=endIndex; j>=startIndex; j--) {
12402
                                this.selectCell(allRows[i].cells[j]);
12403
                            }
12404
                        }
12405
                    }
12406
                }
12407
                // Anchor cell is unselected
12408
                else {
12409
                    // All cells are on the same row
12410
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12411
                        // Unselect all cells between anchor cell and target cell
12412
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12413
                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
12414
                                this.unselectCell(elTargetRow.cells[i]);
12415
                            }
12416
                        }
12417
                        // Select all cells between target cell and anchor cell
12418
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12419
                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
12420
                                this.unselectCell(elTargetRow.cells[i]);
12421
                            }
12422
                        }
12423
                    }
12424
                    // Anchor row is above target row
12425
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
12426
                        // Unselect all cells from anchor cell to target cell
12427
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12428
                            currentRow = allRows[i];
12429
                            for(j=0; j<currentRow.cells.length; j++) {
12430
                                // This is the anchor row, only unselect cells after the anchor cell
12431
                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
12432
                                    if(j>oAnchor.colKeyIndex) {
12433
                                        this.unselectCell(currentRow.cells[j]);
12434
                                    }
12435
                                }
12436
                                // This is the target row, only unelect cells before the target cell
12437
                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
12438
                                    if(j<nTargetColKeyIndex) {
12439
                                        this.unselectCell(currentRow.cells[j]);
12440
                                    }
12441
                                }
12442
                                // Unselect all cells on this row
12443
                                else {
12444
                                    this.unselectCell(currentRow.cells[j]);
12445
                                }
12446
                            }
12447
                        }
12448
                    }
12449
                    // Anchor row is below target row
12450
                    else {
12451
                        // Unselect all cells from target cell to anchor cell
12452
                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12453
                            currentRow = allRows[i];
12454
                            for(j=0; j<currentRow.cells.length; j++) {
12455
                                // This is the target row, only unselect cells after the target cell
12456
                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
12457
                                    if(j>nTargetColKeyIndex) {
12458
                                        this.unselectCell(currentRow.cells[j]);
12459
                                    }
12460
                                }
12461
                                // This is the anchor row, only unselect cells before the anchor cell
12462
                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
12463
                                    if(j<oAnchor.colKeyIndex) {
12464
                                        this.unselectCell(currentRow.cells[j]);
12465
                                    }
12466
                                }
12467
                                // Unselect all cells on this row
12468
                                else {
12469
                                    this.unselectCell(currentRow.cells[j]);
12470
                                }
12471
                            }
12472
                        }
12473
                    }
12474
 
12475
                    // Select the target cell
12476
                    this.selectCell(elTargetCell);
12477
                }
12478
            }
12479
            // Invalid anchor
12480
            else {
12481
                // Set anchor
12482
                this._oAnchorCell = oTargetCell;
12483
 
12484
                // Toggle selection of target
12485
                if(this.isSelected(oTargetCell)) {
12486
                    this.unselectCell(oTargetCell);
12487
                }
12488
                else {
12489
                    this.selectCell(oTargetCell);
12490
                }
12491
            }
12492
 
12493
        }
12494
         // Only SHIFT
12495
        else if(bSHIFT) {
12496
            this.unselectAllCells();
12497
 
12498
            // Validate anchor
12499
            if(oAnchor) {
12500
                // All cells are on the same row
12501
                if(oAnchor.recordIndex === nTargetRecordIndex) {
12502
                    // Select all cells between anchor cell and target cell,
12503
                    // including the anchor cell and target cell
12504
                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12505
                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
12506
                            this.selectCell(elTargetRow.cells[i]);
12507
                        }
12508
                    }
12509
                    // Select all cells between target cell and anchor cell
12510
                    // including the target cell and anchor cell
12511
                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12512
                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
12513
                            this.selectCell(elTargetRow.cells[i]);
12514
                        }
12515
                    }
12516
                }
12517
                // Anchor row is above target row
12518
                else if(oAnchor.recordIndex < nTargetRecordIndex) {
12519
                    // Select the cellblock from anchor cell to target cell
12520
                    // including the anchor cell and the target cell
12521
                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
12522
                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
12523
 
12524
                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12525
                        for(j=startIndex; j<=endIndex; j++) {
12526
                            this.selectCell(allRows[i].cells[j]);
12527
                        }
12528
                    }
12529
                }
12530
                // Anchor row is below target row
12531
                else {
12532
                    // Select the cellblock from target cell to anchor cell
12533
                    // including the target cell and the anchor cell
12534
                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
12535
                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
12536
 
12537
                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12538
                        for(j=startIndex; j<=endIndex; j++) {
12539
                            this.selectCell(allRows[i].cells[j]);
12540
                        }
12541
                    }
12542
                }
12543
            }
12544
            // Invalid anchor
12545
            else {
12546
                // Set anchor
12547
                this._oAnchorCell = oTargetCell;
12548
 
12549
                // Select target only
12550
                this.selectCell(oTargetCell);
12551
            }
12552
        }
12553
        // Only CTRL
12554
        else if(bCTRL) {
12555
 
12556
            // Set anchor
12557
            this._oAnchorCell = oTargetCell;
12558
 
12559
            // Toggle selection of target
12560
            if(this.isSelected(oTargetCell)) {
12561
                this.unselectCell(oTargetCell);
12562
            }
12563
            else {
12564
                this.selectCell(oTargetCell);
12565
            }
12566
 
12567
        }
12568
        // Neither SHIFT nor CTRL
12569
        else {
12570
            this._handleSingleCellSelectionByMouse(oArgs);
12571
        }
12572
    }
12573
},
12574
 
12575
/**
12576
 * Determines selection behavior resulting from a key event when selection mode
12577
 * is set to "cellblock".
12578
 *
12579
 * @method _handleCellBlockSelectionByKey
12580
 * @param e {HTMLEvent} Event object.
12581
 * @private
12582
 */
12583
_handleCellBlockSelectionByKey : function(e) {
12584
    var nKey = Ev.getCharCode(e);
12585
    var bSHIFT = e.shiftKey;
12586
    if((nKey == 9) || !bSHIFT) {
12587
        this._handleSingleCellSelectionByKey(e);
12588
        return;
12589
    }
12590
 
12591
    if((nKey > 36) && (nKey < 41)) {
12592
        // Validate trigger
12593
        var oTrigger = this._getSelectionTrigger();
12594
        // Arrow selection only works if last selected row is on current page
12595
        if(!oTrigger) {
12596
            return null;
12597
        }
12598
 
12599
        Ev.stopEvent(e);
12600
 
12601
        // Validate anchor
12602
        var oAnchor = this._getSelectionAnchor(oTrigger);
12603
 
12604
        var i, startIndex, endIndex, elNew, elNewRow;
12605
        var allRows = this.getTbodyEl().rows;
12606
        var elThisRow = oTrigger.el.parentNode;
12607
 
12608
        // Determine which direction we're going to
12609
 
12610
        if(nKey == 40) { // arrow down
12611
            // Selecting away from anchor cell
12612
            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
12613
                // Select the horiz block on the next row...
12614
                // ...making sure there is room below the trigger row
12615
                elNewRow = this.getNextTrEl(oTrigger.el);
12616
                if(elNewRow) {
12617
                    startIndex = oAnchor.colKeyIndex;
12618
                    endIndex = oTrigger.colKeyIndex;
12619
                    // ...going left
12620
                    if(startIndex > endIndex) {
12621
                        for(i=startIndex; i>=endIndex; i--) {
12622
                            elNew = elNewRow.cells[i];
12623
                            this.selectCell(elNew);
12624
                        }
12625
                    }
12626
                    // ... going right
12627
                    else {
12628
                        for(i=startIndex; i<=endIndex; i++) {
12629
                            elNew = elNewRow.cells[i];
12630
                            this.selectCell(elNew);
12631
                        }
12632
                    }
12633
                }
12634
            }
12635
            // Unselecting towards anchor cell
12636
            else {
12637
                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12638
                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12639
                // Unselect the horiz block on this row towards the next row
12640
                for(i=startIndex; i<=endIndex; i++) {
12641
                    this.unselectCell(elThisRow.cells[i]);
12642
                }
12643
            }
12644
        }
12645
        // Arrow up
12646
        else if(nKey == 38) {
12647
            // Selecting away from anchor cell
12648
            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
12649
                // Select the horiz block on the previous row...
12650
                // ...making sure there is room
12651
                elNewRow = this.getPreviousTrEl(oTrigger.el);
12652
                if(elNewRow) {
12653
                    // Select in order from anchor to trigger...
12654
                    startIndex = oAnchor.colKeyIndex;
12655
                    endIndex = oTrigger.colKeyIndex;
12656
                    // ...going left
12657
                    if(startIndex > endIndex) {
12658
                        for(i=startIndex; i>=endIndex; i--) {
12659
                            elNew = elNewRow.cells[i];
12660
                            this.selectCell(elNew);
12661
                        }
12662
                    }
12663
                    // ... going right
12664
                    else {
12665
                        for(i=startIndex; i<=endIndex; i++) {
12666
                            elNew = elNewRow.cells[i];
12667
                            this.selectCell(elNew);
12668
                        }
12669
                    }
12670
                }
12671
            }
12672
            // Unselecting towards anchor cell
12673
            else {
12674
                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12675
                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12676
                // Unselect the horiz block on this row towards the previous row
12677
                for(i=startIndex; i<=endIndex; i++) {
12678
                    this.unselectCell(elThisRow.cells[i]);
12679
                }
12680
            }
12681
        }
12682
        // Arrow right
12683
        else if(nKey == 39) {
12684
            // Selecting away from anchor cell
12685
            if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
12686
                // Select the next vert block to the right...
12687
                // ...making sure there is room
12688
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
12689
                    // Select in order from anchor to trigger...
12690
                    startIndex = oAnchor.trIndex;
12691
                    endIndex = oTrigger.trIndex;
12692
                    // ...going up
12693
                    if(startIndex > endIndex) {
12694
                        for(i=startIndex; i>=endIndex; i--) {
12695
                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
12696
                            this.selectCell(elNew);
12697
                        }
12698
                    }
12699
                    // ... going down
12700
                    else {
12701
                        for(i=startIndex; i<=endIndex; i++) {
12702
                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
12703
                            this.selectCell(elNew);
12704
                        }
12705
                    }
12706
                }
12707
            }
12708
            // Unselecting towards anchor cell
12709
            else {
12710
                // Unselect the vert block on this column towards the right
12711
                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
12712
                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
12713
                for(i=startIndex; i<=endIndex; i++) {
12714
                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
12715
                }
12716
            }
12717
        }
12718
        // Arrow left
12719
        else if(nKey == 37) {
12720
            // Selecting away from anchor cell
12721
            if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
12722
                //Select the previous vert block to the left
12723
                if(oTrigger.colKeyIndex > 0) {
12724
                    // Select in order from anchor to trigger...
12725
                    startIndex = oAnchor.trIndex;
12726
                    endIndex = oTrigger.trIndex;
12727
                    // ...going up
12728
                    if(startIndex > endIndex) {
12729
                        for(i=startIndex; i>=endIndex; i--) {
12730
                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
12731
                            this.selectCell(elNew);
12732
                        }
12733
                    }
12734
                    // ... going down
12735
                    else {
12736
                        for(i=startIndex; i<=endIndex; i++) {
12737
                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
12738
                            this.selectCell(elNew);
12739
                        }
12740
                    }
12741
                }
12742
            }
12743
            // Unselecting towards anchor cell
12744
            else {
12745
                // Unselect the vert block on this column towards the left
12746
                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
12747
                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
12748
                for(i=startIndex; i<=endIndex; i++) {
12749
                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
12750
                }
12751
            }
12752
        }
12753
    }
12754
},
12755
 
12756
/**
12757
 * Determines selection behavior resulting from a mouse event when selection mode
12758
 * is set to "cellrange".
12759
 *
12760
 * @method _handleCellRangeSelectionByMouse
12761
 * @param oArgs.event {HTMLEvent} Event object.
12762
 * @param oArgs.target {HTMLElement} Target element.
12763
 * @private
12764
 */
12765
_handleCellRangeSelectionByMouse : function(oArgs) {
12766
    var elTarget = oArgs.target;
12767
 
12768
    // Validate target cell
12769
    var elTargetCell = this.getTdEl(elTarget);
12770
    if(elTargetCell) {
12771
        var e = oArgs.event;
12772
        var bSHIFT = e.shiftKey;
12773
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
12774
 
12775
        var elTargetRow = this.getTrEl(elTargetCell);
12776
        var nTargetTrIndex = this.getTrIndex(elTargetRow);
12777
        var oTargetColumn = this.getColumn(elTargetCell);
12778
        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
12779
        var oTargetRecord = this.getRecord(elTargetRow);
12780
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
12781
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
12782
 
12783
        var oAnchor = this._getSelectionAnchor();
12784
 
12785
        var allRows = this.getTbodyEl().rows;
12786
        var currentRow, i, j;
12787
 
12788
        // Both SHIFT and CTRL
12789
        if(bSHIFT && bCTRL) {
12790
 
12791
            // Validate anchor
12792
            if(oAnchor) {
12793
                // Anchor is selected
12794
                if(this.isSelected(oAnchor.cell)) {
12795
                    // All cells are on the same row
12796
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12797
                        // Select all cells between anchor cell and target cell, including target cell
12798
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12799
                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
12800
                                this.selectCell(elTargetRow.cells[i]);
12801
                            }
12802
                        }
12803
                        // Select all cells between target cell and anchor cell, including target cell
12804
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12805
                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
12806
                                this.selectCell(elTargetRow.cells[i]);
12807
                            }
12808
                        }
12809
                    }
12810
                    // Anchor row is above target row
12811
                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
12812
                        // Select all cells on anchor row from anchor cell to the end of the row
12813
                        for(i=oAnchor.colKeyIndex+1; i<elTargetRow.cells.length; i++) {
12814
                            this.selectCell(elTargetRow.cells[i]);
12815
                        }
12816
 
12817
                        // Select all cells on all rows between anchor row and target row
12818
                        for(i=oAnchor.trIndex+1; i<nTargetTrIndex; i++) {
12819
                            for(j=0; j<allRows[i].cells.length; j++){
12820
                                this.selectCell(allRows[i].cells[j]);
12821
                            }
12822
                        }
12823
 
12824
                        // Select all cells on target row from first cell to the target cell
12825
                        for(i=0; i<=nTargetColKeyIndex; i++) {
12826
                            this.selectCell(elTargetRow.cells[i]);
12827
                        }
12828
                    }
12829
                    // Anchor row is below target row
12830
                    else {
12831
                        // Select all cells on target row from target cell to the end of the row
12832
                        for(i=nTargetColKeyIndex; i<elTargetRow.cells.length; i++) {
12833
                            this.selectCell(elTargetRow.cells[i]);
12834
                        }
12835
 
12836
                        // Select all cells on all rows between target row and anchor row
12837
                        for(i=nTargetTrIndex+1; i<oAnchor.trIndex; i++) {
12838
                            for(j=0; j<allRows[i].cells.length; j++){
12839
                                this.selectCell(allRows[i].cells[j]);
12840
                            }
12841
                        }
12842
 
12843
                        // Select all cells on anchor row from first cell to the anchor cell
12844
                        for(i=0; i<oAnchor.colKeyIndex; i++) {
12845
                            this.selectCell(elTargetRow.cells[i]);
12846
                        }
12847
                    }
12848
                }
12849
                // Anchor cell is unselected
12850
                else {
12851
                    // All cells are on the same row
12852
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12853
                        // Unselect all cells between anchor cell and target cell
12854
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12855
                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
12856
                                this.unselectCell(elTargetRow.cells[i]);
12857
                            }
12858
                        }
12859
                        // Select all cells between target cell and anchor cell
12860
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12861
                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
12862
                                this.unselectCell(elTargetRow.cells[i]);
12863
                            }
12864
                        }
12865
                    }
12866
                    // Anchor row is above target row
12867
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
12868
                        // Unselect all cells from anchor cell to target cell
12869
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12870
                            currentRow = allRows[i];
12871
                            for(j=0; j<currentRow.cells.length; j++) {
12872
                                // This is the anchor row, only unselect cells after the anchor cell
12873
                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
12874
                                    if(j>oAnchor.colKeyIndex) {
12875
                                        this.unselectCell(currentRow.cells[j]);
12876
                                    }
12877
                                }
12878
                                // This is the target row, only unelect cells before the target cell
12879
                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
12880
                                    if(j<nTargetColKeyIndex) {
12881
                                        this.unselectCell(currentRow.cells[j]);
12882
                                    }
12883
                                }
12884
                                // Unselect all cells on this row
12885
                                else {
12886
                                    this.unselectCell(currentRow.cells[j]);
12887
                                }
12888
                            }
12889
                        }
12890
                    }
12891
                    // Anchor row is below target row
12892
                    else {
12893
                        // Unselect all cells from target cell to anchor cell
12894
                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12895
                            currentRow = allRows[i];
12896
                            for(j=0; j<currentRow.cells.length; j++) {
12897
                                // This is the target row, only unselect cells after the target cell
12898
                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
12899
                                    if(j>nTargetColKeyIndex) {
12900
                                        this.unselectCell(currentRow.cells[j]);
12901
                                    }
12902
                                }
12903
                                // This is the anchor row, only unselect cells before the anchor cell
12904
                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
12905
                                    if(j<oAnchor.colKeyIndex) {
12906
                                        this.unselectCell(currentRow.cells[j]);
12907
                                    }
12908
                                }
12909
                                // Unselect all cells on this row
12910
                                else {
12911
                                    this.unselectCell(currentRow.cells[j]);
12912
                                }
12913
                            }
12914
                        }
12915
                    }
12916
 
12917
                    // Select the target cell
12918
                    this.selectCell(elTargetCell);
12919
                }
12920
            }
12921
            // Invalid anchor
12922
            else {
12923
                // Set anchor
12924
                this._oAnchorCell = oTargetCell;
12925
 
12926
                // Toggle selection of target
12927
                if(this.isSelected(oTargetCell)) {
12928
                    this.unselectCell(oTargetCell);
12929
                }
12930
                else {
12931
                    this.selectCell(oTargetCell);
12932
                }
12933
            }
12934
        }
12935
         // Only SHIFT
12936
        else if(bSHIFT) {
12937
 
12938
            this.unselectAllCells();
12939
 
12940
            // Validate anchor
12941
            if(oAnchor) {
12942
                // All cells are on the same row
12943
                if(oAnchor.recordIndex === nTargetRecordIndex) {
12944
                    // Select all cells between anchor cell and target cell,
12945
                    // including the anchor cell and target cell
12946
                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12947
                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
12948
                            this.selectCell(elTargetRow.cells[i]);
12949
                        }
12950
                    }
12951
                    // Select all cells between target cell and anchor cell
12952
                    // including the target cell and anchor cell
12953
                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12954
                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
12955
                            this.selectCell(elTargetRow.cells[i]);
12956
                        }
12957
                    }
12958
                }
12959
                // Anchor row is above target row
12960
                else if(oAnchor.recordIndex < nTargetRecordIndex) {
12961
                    // Select all cells from anchor cell to target cell
12962
                    // including the anchor cell and target cell
12963
                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12964
                        currentRow = allRows[i];
12965
                        for(j=0; j<currentRow.cells.length; j++) {
12966
                            // This is the anchor row, only select the anchor cell and after
12967
                            if(currentRow.sectionRowIndex == oAnchor.trIndex) {
12968
                                if(j>=oAnchor.colKeyIndex) {
12969
                                    this.selectCell(currentRow.cells[j]);
12970
                                }
12971
                            }
12972
                            // This is the target row, only select the target cell and before
12973
                            else if(currentRow.sectionRowIndex == nTargetTrIndex) {
12974
                                if(j<=nTargetColKeyIndex) {
12975
                                    this.selectCell(currentRow.cells[j]);
12976
                                }
12977
                            }
12978
                            // Select all cells on this row
12979
                            else {
12980
                                this.selectCell(currentRow.cells[j]);
12981
                            }
12982
                        }
12983
                    }
12984
                }
12985
                // Anchor row is below target row
12986
                else {
12987
                    // Select all cells from target cell to anchor cell,
12988
                    // including the target cell and anchor cell
12989
                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12990
                        currentRow = allRows[i];
12991
                        for(j=0; j<currentRow.cells.length; j++) {
12992
                            // This is the target row, only select the target cell and after
12993
                            if(currentRow.sectionRowIndex == nTargetTrIndex) {
12994
                                if(j>=nTargetColKeyIndex) {
12995
                                    this.selectCell(currentRow.cells[j]);
12996
                                }
12997
                            }
12998
                            // This is the anchor row, only select the anchor cell and before
12999
                            else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
13000
                                if(j<=oAnchor.colKeyIndex) {
13001
                                    this.selectCell(currentRow.cells[j]);
13002
                                }
13003
                            }
13004
                            // Select all cells on this row
13005
                            else {
13006
                                this.selectCell(currentRow.cells[j]);
13007
                            }
13008
                        }
13009
                    }
13010
                }
13011
            }
13012
            // Invalid anchor
13013
            else {
13014
                // Set anchor
13015
                this._oAnchorCell = oTargetCell;
13016
 
13017
                // Select target only
13018
                this.selectCell(oTargetCell);
13019
            }
13020
 
13021
 
13022
        }
13023
        // Only CTRL
13024
        else if(bCTRL) {
13025
 
13026
            // Set anchor
13027
            this._oAnchorCell = oTargetCell;
13028
 
13029
            // Toggle selection of target
13030
            if(this.isSelected(oTargetCell)) {
13031
                this.unselectCell(oTargetCell);
13032
            }
13033
            else {
13034
                this.selectCell(oTargetCell);
13035
            }
13036
 
13037
        }
13038
        // Neither SHIFT nor CTRL
13039
        else {
13040
            this._handleSingleCellSelectionByMouse(oArgs);
13041
        }
13042
    }
13043
},
13044
 
13045
/**
13046
 * Determines selection behavior resulting from a key event when selection mode
13047
 * is set to "cellrange".
13048
 *
13049
 * @method _handleCellRangeSelectionByKey
13050
 * @param e {HTMLEvent} Event object.
13051
 * @private
13052
 */
13053
_handleCellRangeSelectionByKey : function(e) {
13054
    var nKey = Ev.getCharCode(e);
13055
    var bSHIFT = e.shiftKey;
13056
    if((nKey == 9) || !bSHIFT) {
13057
        this._handleSingleCellSelectionByKey(e);
13058
        return;
13059
    }
13060
 
13061
    if((nKey > 36) && (nKey < 41)) {
13062
        // Validate trigger
13063
        var oTrigger = this._getSelectionTrigger();
13064
        // Arrow selection only works if last selected row is on current page
13065
        if(!oTrigger) {
13066
            return null;
13067
        }
13068
 
13069
        Ev.stopEvent(e);
13070
 
13071
        // Validate anchor
13072
        var oAnchor = this._getSelectionAnchor(oTrigger);
13073
 
13074
        var i, elNewRow, elNew;
13075
        var allRows = this.getTbodyEl().rows;
13076
        var elThisRow = oTrigger.el.parentNode;
13077
 
13078
        // Arrow down
13079
        if(nKey == 40) {
13080
            elNewRow = this.getNextTrEl(oTrigger.el);
13081
 
13082
            // Selecting away from anchor cell
13083
            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
13084
                // Select all cells to the end of this row
13085
                for(i=oTrigger.colKeyIndex+1; i<elThisRow.cells.length; i++){
13086
                    elNew = elThisRow.cells[i];
13087
                    this.selectCell(elNew);
13088
                }
13089
 
13090
                // Select some of the cells on the next row down
13091
                if(elNewRow) {
13092
                    for(i=0; i<=oTrigger.colKeyIndex; i++){
13093
                        elNew = elNewRow.cells[i];
13094
                        this.selectCell(elNew);
13095
                    }
13096
                }
13097
            }
13098
            // Unselecting towards anchor cell
13099
            else {
13100
                // Unselect all cells to the end of this row
13101
                for(i=oTrigger.colKeyIndex; i<elThisRow.cells.length; i++){
13102
                    this.unselectCell(elThisRow.cells[i]);
13103
                }
13104
 
13105
                // Unselect some of the cells on the next row down
13106
                if(elNewRow) {
13107
                    for(i=0; i<oTrigger.colKeyIndex; i++){
13108
                        this.unselectCell(elNewRow.cells[i]);
13109
                    }
13110
                }
13111
            }
13112
        }
13113
        // Arrow up
13114
        else if(nKey == 38) {
13115
            elNewRow = this.getPreviousTrEl(oTrigger.el);
13116
 
13117
            // Selecting away from anchor cell
13118
            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
13119
                // Select all the cells to the beginning of this row
13120
                for(i=oTrigger.colKeyIndex-1; i>-1; i--){
13121
                    elNew = elThisRow.cells[i];
13122
                    this.selectCell(elNew);
13123
                }
13124
 
13125
                // Select some of the cells from the end of the previous row
13126
                if(elNewRow) {
13127
                    for(i=elThisRow.cells.length-1; i>=oTrigger.colKeyIndex; i--){
13128
                        elNew = elNewRow.cells[i];
13129
                        this.selectCell(elNew);
13130
                    }
13131
                }
13132
            }
13133
            // Unselecting towards anchor cell
13134
            else {
13135
                // Unselect all the cells to the beginning of this row
13136
                for(i=oTrigger.colKeyIndex; i>-1; i--){
13137
                    this.unselectCell(elThisRow.cells[i]);
13138
                }
13139
 
13140
                // Unselect some of the cells from the end of the previous row
13141
                if(elNewRow) {
13142
                    for(i=elThisRow.cells.length-1; i>oTrigger.colKeyIndex; i--){
13143
                        this.unselectCell(elNewRow.cells[i]);
13144
                    }
13145
                }
13146
            }
13147
        }
13148
        // Arrow right
13149
        else if(nKey == 39) {
13150
            elNewRow = this.getNextTrEl(oTrigger.el);
13151
 
13152
            // Selecting away from anchor cell
13153
            if(oAnchor.recordIndex < oTrigger.recordIndex) {
13154
                // Select the next cell to the right
13155
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
13156
                    elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
13157
                    this.selectCell(elNew);
13158
                }
13159
                // Select the first cell of the next row
13160
                else if(elNewRow) {
13161
                    elNew = elNewRow.cells[0];
13162
                    this.selectCell(elNew);
13163
                }
13164
            }
13165
            // Unselecting towards anchor cell
13166
            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
13167
                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
13168
 
13169
                // Unselect this cell towards the right
13170
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
13171
                }
13172
                // Unselect this cells towards the first cell of the next row
13173
                else {
13174
                }
13175
            }
13176
            // Anchor is on this row
13177
            else {
13178
                // Selecting away from anchor
13179
                if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
13180
                    // Select the next cell to the right
13181
                    if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
13182
                        elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
13183
                        this.selectCell(elNew);
13184
                    }
13185
                    // Select the first cell on the next row
13186
                    else if(oTrigger.trIndex < allRows.length-1){
13187
                        elNew = elNewRow.cells[0];
13188
                        this.selectCell(elNew);
13189
                    }
13190
                }
13191
                // Unselecting towards anchor
13192
                else {
13193
                    // Unselect this cell towards the right
13194
                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
13195
                }
13196
            }
13197
        }
13198
        // Arrow left
13199
        else if(nKey == 37) {
13200
            elNewRow = this.getPreviousTrEl(oTrigger.el);
13201
 
13202
            // Unselecting towards the anchor
13203
            if(oAnchor.recordIndex < oTrigger.recordIndex) {
13204
                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
13205
 
13206
                // Unselect this cell towards the left
13207
                if(oTrigger.colKeyIndex > 0) {
13208
                }
13209
                // Unselect this cell towards the last cell of the previous row
13210
                else {
13211
                }
13212
            }
13213
            // Selecting towards the anchor
13214
            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
13215
                // Select the next cell to the left
13216
                if(oTrigger.colKeyIndex > 0) {
13217
                    elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
13218
                    this.selectCell(elNew);
13219
                }
13220
                // Select the last cell of the previous row
13221
                else if(oTrigger.trIndex > 0){
13222
                    elNew = elNewRow.cells[elNewRow.cells.length-1];
13223
                    this.selectCell(elNew);
13224
                }
13225
            }
13226
            // Anchor is on this row
13227
            else {
13228
                // Selecting away from anchor cell
13229
                if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
13230
                    // Select the next cell to the left
13231
                    if(oTrigger.colKeyIndex > 0) {
13232
                        elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
13233
                        this.selectCell(elNew);
13234
                    }
13235
                    // Select the last cell of the previous row
13236
                    else if(oTrigger.trIndex > 0){
13237
                        elNew = elNewRow.cells[elNewRow.cells.length-1];
13238
                        this.selectCell(elNew);
13239
                    }
13240
                }
13241
                // Unselecting towards anchor cell
13242
                else {
13243
                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
13244
 
13245
                    // Unselect this cell towards the left
13246
                    if(oTrigger.colKeyIndex > 0) {
13247
                    }
13248
                    // Unselect this cell towards the last cell of the previous row
13249
                    else {
13250
                    }
13251
                }
13252
            }
13253
        }
13254
    }
13255
},
13256
 
13257
/**
13258
 * Determines selection behavior resulting from a mouse event when selection mode
13259
 * is set to "singlecell".
13260
 *
13261
 * @method _handleSingleCellSelectionByMouse
13262
 * @param oArgs.event {HTMLEvent} Event object.
13263
 * @param oArgs.target {HTMLElement} Target element.
13264
 * @private
13265
 */
13266
_handleSingleCellSelectionByMouse : function(oArgs) {
13267
    var elTarget = oArgs.target;
13268
 
13269
    // Validate target cell
13270
    var elTargetCell = this.getTdEl(elTarget);
13271
    if(elTargetCell) {
13272
        var elTargetRow = this.getTrEl(elTargetCell);
13273
        var oTargetRecord = this.getRecord(elTargetRow);
13274
        var oTargetColumn = this.getColumn(elTargetCell);
13275
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
13276
 
13277
        // Set anchor
13278
        this._oAnchorCell = oTargetCell;
13279
 
13280
        // Select only target
13281
        this.unselectAllCells();
13282
        this.selectCell(oTargetCell);
13283
    }
13284
},
13285
 
13286
/**
13287
 * Determines selection behavior resulting from a key event when selection mode
13288
 * is set to "singlecell".
13289
 *
13290
 * @method _handleSingleCellSelectionByKey
13291
 * @param e {HTMLEvent} Event object.
13292
 * @private
13293
 */
13294
_handleSingleCellSelectionByKey : function(e) {
13295
    var nKey = Ev.getCharCode(e);
13296
    if((nKey == 9) || ((nKey > 36) && (nKey < 41))) {
13297
        var bSHIFT = e.shiftKey;
13298
 
13299
        // Validate trigger
13300
        var oTrigger = this._getSelectionTrigger();
13301
        // Arrow selection only works if last selected row is on current page
13302
        if(!oTrigger) {
13303
            return null;
13304
        }
13305
 
13306
        // Determine the new cell to select
13307
        var elNew;
13308
        if(nKey == 40) { // Arrow down
13309
            elNew = this.getBelowTdEl(oTrigger.el);
13310
 
13311
            // Validate new cell
13312
            if(elNew === null) {
13313
                //TODO: wrap around to first tr on current page
13314
 
13315
                //TODO: wrap forward to first tr of next page
13316
 
13317
                // Bottom selection is sticky
13318
                elNew = oTrigger.el;
13319
            }
13320
        }
13321
        else if(nKey == 38) { // Arrow up
13322
            elNew = this.getAboveTdEl(oTrigger.el);
13323
 
13324
            // Validate new cell
13325
            if(elNew === null) {
13326
                //TODO: wrap around to last tr on current page
13327
 
13328
                //TODO: wrap back to last tr of previous page
13329
 
13330
                // Top selection is sticky
13331
                elNew = oTrigger.el;
13332
            }
13333
        }
13334
        else if((nKey == 39) || (!bSHIFT && (nKey == 9))) { // Arrow right or tab
13335
            elNew = this.getNextTdEl(oTrigger.el);
13336
 
13337
            // Validate new cell
13338
            if(elNew === null) {
13339
                //TODO: wrap around to first td on current page
13340
 
13341
                //TODO: wrap forward to first td of next page
13342
 
13343
                // Top-left selection is sticky, and release TAB focus
13344
                //elNew = oTrigger.el;
13345
                return;
13346
            }
13347
        }
13348
        else if((nKey == 37) || (bSHIFT && (nKey == 9))) { // Arrow left or shift-tab
13349
            elNew = this.getPreviousTdEl(oTrigger.el);
13350
 
13351
            // Validate new cell
13352
            if(elNew === null) {
13353
                //TODO: wrap around to last td on current page
13354
 
13355
                //TODO: wrap back to last td of previous page
13356
 
13357
                // Bottom-right selection is sticky, and release TAB focus
13358
                //elNew = oTrigger.el;
13359
                return;
13360
            }
13361
        }
13362
 
13363
        Ev.stopEvent(e);
13364
 
13365
        // Unselect all cells
13366
        this.unselectAllCells();
13367
 
13368
        // Select the new cell
13369
        this.selectCell(elNew);
13370
 
13371
        // Set new anchor
13372
        this._oAnchorCell = {record:this.getRecord(elNew), column:this.getColumn(elNew)};
13373
    }
13374
},
13375
 
13376
/**
13377
 * Returns array of selected TR elements on the page.
13378
 *
13379
 * @method getSelectedTrEls
13380
 * @return {HTMLElement[]} Array of selected TR elements.
13381
 */
13382
getSelectedTrEls : function() {
13383
    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
13384
},
13385
 
13386
/**
13387
 * Sets given row to the selected state.
13388
 *
13389
 * @method selectRow
13390
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
13391
 * reference or ID string, Record instance, or RecordSet position index.
13392
 */
13393
selectRow : function(row) {
13394
    var oRecord, elRow;
13395
 
13396
    if(row instanceof YAHOO.widget.Record) {
13397
        oRecord = this._oRecordSet.getRecord(row);
13398
        elRow = this.getTrEl(oRecord);
13399
    }
13400
    else if(lang.isNumber(row)) {
13401
        oRecord = this.getRecord(row);
13402
        elRow = this.getTrEl(oRecord);
13403
    }
13404
    else {
13405
        elRow = this.getTrEl(row);
13406
        oRecord = this.getRecord(elRow);
13407
    }
13408
 
13409
    if(oRecord) {
13410
        // Update selection trackers
13411
        var tracker = this._aSelections || [];
13412
        var sRecordId = oRecord.getId();
13413
        var index = -1;
13414
 
13415
        // Remove if already there:
13416
        // Use Array.indexOf if available...
13417
        /*if(tracker.indexOf && (tracker.indexOf(sRecordId) >  -1)) {
13418
            tracker.splice(tracker.indexOf(sRecordId),1);
13419
        }*/
13420
        if(tracker.indexOf) {
13421
            index = tracker.indexOf(sRecordId);
13422
 
13423
        }
13424
        // ...or do it the old-fashioned way
13425
        else {
13426
            for(var j=tracker.length-1; j>-1; j--) {
13427
                if(tracker[j] === sRecordId){
13428
                    index = j;
13429
                    break;
13430
                }
13431
            }
13432
        }
13433
        if(index > -1) {
13434
            tracker.splice(index,1);
13435
        }
13436
 
13437
        // Add to the end
13438
        tracker.push(sRecordId);
13439
        this._aSelections = tracker;
13440
 
13441
        // Update trackers
13442
        if(!this._oAnchorRecord) {
13443
            this._oAnchorRecord = oRecord;
13444
        }
13445
 
13446
        // Update UI
13447
        if(elRow) {
13448
            Dom.addClass(elRow, DT.CLASS_SELECTED);
13449
        }
13450
 
13451
        this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
13452
        YAHOO.log("Selected " + elRow, "info", this.toString());
13453
    }
13454
    else {
13455
        YAHOO.log("Could not select row " + row, "warn", this.toString());
13456
    }
13457
},
13458
 
13459
/**
13460
 * Sets given row to the unselected state.
13461
 *
13462
 * @method unselectRow
13463
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
13464
 * reference or ID string, Record instance, or RecordSet position index.
13465
 */
13466
unselectRow : function(row) {
13467
    var elRow = this.getTrEl(row);
13468
 
13469
    var oRecord;
13470
    if(row instanceof YAHOO.widget.Record) {
13471
        oRecord = this._oRecordSet.getRecord(row);
13472
    }
13473
    else if(lang.isNumber(row)) {
13474
        oRecord = this.getRecord(row);
13475
    }
13476
    else {
13477
        oRecord = this.getRecord(elRow);
13478
    }
13479
 
13480
    if(oRecord) {
13481
        // Update selection trackers
13482
        var tracker = this._aSelections || [];
13483
        var sRecordId = oRecord.getId();
13484
        var index = -1;
13485
 
13486
        // Use Array.indexOf if available...
13487
        if(tracker.indexOf) {
13488
            index = tracker.indexOf(sRecordId);
13489
        }
13490
        // ...or do it the old-fashioned way
13491
        else {
13492
            for(var j=tracker.length-1; j>-1; j--) {
13493
                if(tracker[j] === sRecordId){
13494
                    index = j;
13495
                    break;
13496
                }
13497
            }
13498
        }
13499
        if(index > -1) {
13500
            // Update tracker
13501
            tracker.splice(index,1);
13502
            this._aSelections = tracker;
13503
 
13504
            // Update the UI
13505
            Dom.removeClass(elRow, DT.CLASS_SELECTED);
13506
 
13507
            this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
13508
            YAHOO.log("Unselected " + elRow, "info", this.toString());
13509
 
13510
            return;
13511
        }
13512
    }
13513
    YAHOO.log("Could not unselect row " + row, "warn", this.toString());
13514
},
13515
 
13516
/**
13517
 * Clears out all row selections.
13518
 *
13519
 * @method unselectAllRows
13520
 */
13521
unselectAllRows : function() {
13522
    // Remove all rows from tracker
13523
    var tracker = this._aSelections || [],
13524
        recId,
13525
        removed = [];
13526
    for(var j=tracker.length-1; j>-1; j--) {
13527
       if(lang.isString(tracker[j])){
13528
            recId = tracker.splice(j,1);
13529
            removed[removed.length] = this.getRecord(lang.isArray(recId) ? recId[0] : recId);
13530
        }
13531
    }
13532
 
13533
    // Update tracker
13534
    this._aSelections = tracker;
13535
 
13536
    // Update UI
13537
    this._unselectAllTrEls();
13538
 
13539
    this.fireEvent("unselectAllRowsEvent", {records: removed});
13540
    YAHOO.log("Unselected all rows", "info", this.toString());
13541
},
13542
 
13543
/**
13544
 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
13545
 * from all TD elements in the internal tracker.
13546
 *
13547
 * @method _unselectAllTdEls
13548
 * @private
13549
 */
13550
_unselectAllTdEls : function() {
13551
    var selectedCells = Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
13552
    Dom.removeClass(selectedCells, DT.CLASS_SELECTED);
13553
},
13554
 
13555
/**
13556
 * Returns array of selected TD elements on the page.
13557
 *
13558
 * @method getSelectedTdEls
13559
 * @return {HTMLElement[]} Array of selected TD elements.
13560
 */
13561
getSelectedTdEls : function() {
13562
    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
13563
},
13564
 
13565
/**
13566
 * Sets given cell to the selected state.
13567
 *
13568
 * @method selectCell
13569
 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
13570
 * object literal of syntax {record:oRecord, column:oColumn}.
13571
 */
13572
selectCell : function(cell) {
13573
//TODO: accept {record} in selectCell()
13574
    var elCell = this.getTdEl(cell);
13575
 
13576
    if(elCell) {
13577
        var oRecord = this.getRecord(elCell);
13578
        var oColumn = this.getColumn(this.getCellIndex(elCell));
13579
        var sColumnKey = oColumn.getKey();
13580
 
13581
        if(oRecord && sColumnKey) {
13582
            // Get Record ID
13583
            var tracker = this._aSelections || [];
13584
            var sRecordId = oRecord.getId();
13585
 
13586
            // Remove if there
13587
            for(var j=tracker.length-1; j>-1; j--) {
13588
               if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
13589
                    tracker.splice(j,1);
13590
                    break;
13591
                }
13592
            }
13593
 
13594
            // Add to the end
13595
            tracker.push({recordId:sRecordId, columnKey:sColumnKey});
13596
 
13597
            // Update trackers
13598
            this._aSelections = tracker;
13599
            if(!this._oAnchorCell) {
13600
                this._oAnchorCell = {record:oRecord, column:oColumn};
13601
            }
13602
 
13603
            // Update the UI
13604
            Dom.addClass(elCell, DT.CLASS_SELECTED);
13605
 
13606
            this.fireEvent("cellSelectEvent", {record:oRecord, column:oColumn, key: sColumnKey, el:elCell});
13607
            YAHOO.log("Selected " + elCell, "info", this.toString());
13608
            return;
13609
        }
13610
    }
13611
    YAHOO.log("Could not select cell " + cell, "warn", this.toString());
13612
},
13613
 
13614
/**
13615
 * Sets given cell to the unselected state.
13616
 *
13617
 * @method unselectCell
13618
 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
13619
 * object literal of syntax {record:oRecord, column:oColumn}.
13620
 * @param cell {HTMLElement | String} DOM element reference or ID string
13621
 * to DataTable page element or RecordSet index.
13622
 */
13623
unselectCell : function(cell) {
13624
    var elCell = this.getTdEl(cell);
13625
 
13626
    if(elCell) {
13627
        var oRecord = this.getRecord(elCell);
13628
        var oColumn = this.getColumn(this.getCellIndex(elCell));
13629
        var sColumnKey = oColumn.getKey();
13630
 
13631
        if(oRecord && sColumnKey) {
13632
            // Get Record ID
13633
            var tracker = this._aSelections || [];
13634
            var id = oRecord.getId();
13635
 
13636
            // Is it selected?
13637
            for(var j=tracker.length-1; j>-1; j--) {
13638
                if((tracker[j].recordId === id) && (tracker[j].columnKey === sColumnKey)){
13639
                    // Remove from tracker
13640
                    tracker.splice(j,1);
13641
 
13642
                    // Update tracker
13643
                    this._aSelections = tracker;
13644
 
13645
                    // Update the UI
13646
                    Dom.removeClass(elCell, DT.CLASS_SELECTED);
13647
 
13648
                    this.fireEvent("cellUnselectEvent", {record:oRecord, column: oColumn, key:sColumnKey, el:elCell});
13649
                    YAHOO.log("Unselected " + elCell, "info", this.toString());
13650
                    return;
13651
                }
13652
            }
13653
        }
13654
    }
13655
    YAHOO.log("Could not unselect cell " + cell, "warn", this.toString());
13656
},
13657
 
13658
/**
13659
 * Clears out all cell selections.
13660
 *
13661
 * @method unselectAllCells
13662
 */
13663
unselectAllCells : function() {
13664
    // Remove all cells from tracker
13665
    var tracker = this._aSelections || [];
13666
    for(var j=tracker.length-1; j>-1; j--) {
13667
       if(lang.isObject(tracker[j])){
13668
            tracker.splice(j,1);
13669
        }
13670
    }
13671
 
13672
    // Update tracker
13673
    this._aSelections = tracker;
13674
 
13675
    // Update UI
13676
    this._unselectAllTdEls();
13677
 
13678
    //TODO: send data to unselectAllCellsEvent handler
13679
    this.fireEvent("unselectAllCellsEvent");
13680
    YAHOO.log("Unselected all cells", "info", this.toString());
13681
},
13682
 
13683
/**
13684
 * Returns true if given item is selected, false otherwise.
13685
 *
13686
 * @method isSelected
13687
 * @param o {String | HTMLElement | YAHOO.widget.Record | Number
13688
 * {record:YAHOO.widget.Record, column:YAHOO.widget.Column} } TR or TD element by
13689
 * reference or ID string, a Record instance, a RecordSet position index,
13690
 * or an object literal representation
13691
 * of a cell.
13692
 * @return {Boolean} True if item is selected.
13693
 */
13694
isSelected : function(o) {
13695
    if(o && (o.ownerDocument == document)) {
13696
        return (Dom.hasClass(this.getTdEl(o),DT.CLASS_SELECTED) || Dom.hasClass(this.getTrEl(o),DT.CLASS_SELECTED));
13697
    }
13698
    else {
13699
        var oRecord, sRecordId, j;
13700
        var tracker = this._aSelections;
13701
        if(tracker && tracker.length > 0) {
13702
            // Looking for a Record?
13703
            if(o instanceof YAHOO.widget.Record) {
13704
                oRecord = o;
13705
            }
13706
            else if(lang.isNumber(o)) {
13707
                oRecord = this.getRecord(o);
13708
            }
13709
            if(oRecord) {
13710
                sRecordId = oRecord.getId();
13711
 
13712
                // Is it there?
13713
                // Use Array.indexOf if available...
13714
                if(tracker.indexOf) {
13715
                    if(tracker.indexOf(sRecordId) >  -1) {
13716
                        return true;
13717
                    }
13718
                }
13719
                // ...or do it the old-fashioned way
13720
                else {
13721
                    for(j=tracker.length-1; j>-1; j--) {
13722
                       if(tracker[j] === sRecordId){
13723
                        return true;
13724
                       }
13725
                    }
13726
                }
13727
            }
13728
            // Looking for a cell
13729
            else if(o.record && o.column){
13730
                sRecordId = o.record.getId();
13731
                var sColumnKey = o.column.getKey();
13732
 
13733
                for(j=tracker.length-1; j>-1; j--) {
13734
                    if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
13735
                        return true;
13736
                    }
13737
                }
13738
            }
13739
        }
13740
    }
13741
    return false;
13742
},
13743
 
13744
/**
13745
 * Returns selected rows as an array of Record IDs.
13746
 *
13747
 * @method getSelectedRows
13748
 * @return {String[]} Array of selected rows by Record ID.
13749
 */
13750
getSelectedRows : function() {
13751
    var aSelectedRows = [];
13752
    var tracker = this._aSelections || [];
13753
    for(var j=0; j<tracker.length; j++) {
13754
       if(lang.isString(tracker[j])){
13755
            aSelectedRows.push(tracker[j]);
13756
        }
13757
    }
13758
    return aSelectedRows;
13759
},
13760
 
13761
/**
13762
 * Returns selected cells as an array of object literals:
13763
 *     {recordId:sRecordId, columnKey:sColumnKey}.
13764
 *
13765
 * @method getSelectedCells
13766
 * @return {Object[]} Array of selected cells by Record ID and Column ID.
13767
 */
13768
getSelectedCells : function() {
13769
    var aSelectedCells = [];
13770
    var tracker = this._aSelections || [];
13771
    for(var j=0; j<tracker.length; j++) {
13772
       if(tracker[j] && lang.isObject(tracker[j])){
13773
            aSelectedCells.push(tracker[j]);
13774
        }
13775
    }
13776
    return aSelectedCells;
13777
},
13778
 
13779
/**
13780
 * Returns last selected Record ID.
13781
 *
13782
 * @method getLastSelectedRecord
13783
 * @return {String} Record ID of last selected row.
13784
 */
13785
getLastSelectedRecord : function() {
13786
    var tracker = this._aSelections;
13787
    if(tracker && tracker.length > 0) {
13788
        for(var i=tracker.length-1; i>-1; i--) {
13789
           if(lang.isString(tracker[i])){
13790
                return tracker[i];
13791
            }
13792
        }
13793
    }
13794
},
13795
 
13796
/**
13797
 * Returns last selected cell as an object literal:
13798
 *     {recordId:sRecordId, columnKey:sColumnKey}.
13799
 *
13800
 * @method getLastSelectedCell
13801
 * @return {Object} Object literal representation of a cell.
13802
 */
13803
getLastSelectedCell : function() {
13804
    var tracker = this._aSelections;
13805
    if(tracker && tracker.length > 0) {
13806
        for(var i=tracker.length-1; i>-1; i--) {
13807
           if(tracker[i].recordId && tracker[i].columnKey){
13808
                return tracker[i];
13809
            }
13810
        }
13811
    }
13812
},
13813
 
13814
/**
13815
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row.
13816
 *
13817
 * @method highlightRow
13818
 * @param row {HTMLElement | String} DOM element reference or ID string.
13819
 */
13820
highlightRow : function(row) {
13821
    var elRow = this.getTrEl(row);
13822
 
13823
    if(elRow) {
13824
        // Make sure previous row is unhighlighted
13825
/*        if(this._sLastHighlightedTrElId) {
13826
            Dom.removeClass(this._sLastHighlightedTrElId,DT.CLASS_HIGHLIGHTED);
13827
        }*/
13828
        var oRecord = this.getRecord(elRow);
13829
        Dom.addClass(elRow,DT.CLASS_HIGHLIGHTED);
13830
        //this._sLastHighlightedTrElId = elRow.id;
13831
        this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow});
13832
        YAHOO.log("Highlighted " + elRow, "info", this.toString());
13833
        return;
13834
    }
13835
    YAHOO.log("Could not highlight row " + row, "warn", this.toString());
13836
},
13837
 
13838
/**
13839
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row.
13840
 *
13841
 * @method unhighlightRow
13842
 * @param row {HTMLElement | String} DOM element reference or ID string.
13843
 */
13844
unhighlightRow : function(row) {
13845
    var elRow = this.getTrEl(row);
13846
 
13847
    if(elRow) {
13848
        var oRecord = this.getRecord(elRow);
13849
        Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED);
13850
        this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
13851
        YAHOO.log("Unhighlighted " + elRow, "info", this.toString());
13852
        return;
13853
    }
13854
    YAHOO.log("Could not unhighlight row " + row, "warn", this.toString());
13855
},
13856
 
13857
/**
13858
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell.
13859
 *
13860
 * @method highlightCell
13861
 * @param cell {HTMLElement | String} DOM element reference or ID string.
13862
 */
13863
highlightCell : function(cell) {
13864
    var elCell = this.getTdEl(cell);
13865
 
13866
    if(elCell) {
13867
        // Make sure previous cell is unhighlighted
13868
        if(this._elLastHighlightedTd) {
13869
            this.unhighlightCell(this._elLastHighlightedTd);
13870
        }
13871
 
13872
        var oRecord = this.getRecord(elCell);
13873
        var oColumn = this.getColumn(this.getCellIndex(elCell));
13874
        var sColumnKey = oColumn.getKey();
13875
        Dom.addClass(elCell,DT.CLASS_HIGHLIGHTED);
13876
        this._elLastHighlightedTd = elCell;
13877
        this.fireEvent("cellHighlightEvent", {record:oRecord, column:oColumn, key:sColumnKey, el:elCell});
13878
        YAHOO.log("Highlighted " + elCell, "info", this.toString());
13879
        return;
13880
    }
13881
    YAHOO.log("Could not highlight cell " + cell, "warn", this.toString());
13882
},
13883
 
13884
/**
13885
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell.
13886
 *
13887
 * @method unhighlightCell
13888
 * @param cell {HTMLElement | String} DOM element reference or ID string.
13889
 */
13890
unhighlightCell : function(cell) {
13891
    var elCell = this.getTdEl(cell);
13892
 
13893
    if(elCell) {
13894
        var oRecord = this.getRecord(elCell);
13895
        Dom.removeClass(elCell,DT.CLASS_HIGHLIGHTED);
13896
        this._elLastHighlightedTd = null;
13897
        this.fireEvent("cellUnhighlightEvent", {record:oRecord, column:this.getColumn(this.getCellIndex(elCell)), key:this.getColumn(this.getCellIndex(elCell)).getKey(), el:elCell});
13898
        YAHOO.log("Unhighlighted " + elCell, "info", this.toString());
13899
        return;
13900
    }
13901
    YAHOO.log("Could not unhighlight cell " + cell, "warn", this.toString());
13902
},
13903
 
13904
 
13905
 
13906
 
13907
 
13908
 
13909
 
13910
 
13911
 
13912
 
13913
 
13914
 
13915
 
13916
 
13917
 
13918
 
13919
 
13920
 
13921
 
13922
 
13923
 
13924
 
13925
 
13926
 
13927
 
13928
 
13929
 
13930
 
13931
 
13932
 
13933
 
13934
 
13935
 
13936
 
13937
 
13938
 
13939
 
13940
 
13941
 
13942
 
13943
 
13944
 
13945
 
13946
 
13947
 
13948
// INLINE EDITING
13949
 
13950
/**
13951
 * Assigns CellEditor instance to existing Column.
13952
 * @method addCellEditor
13953
 * @param oColumn {YAHOO.widget.Column} Column instance.
13954
 * @param oEditor {YAHOO.wdiget.CellEditor} CellEditor instance.
13955
 */
13956
addCellEditor : function(oColumn, oEditor) {
13957
    oColumn.editor = oEditor;
13958
    oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
13959
    oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
13960
    oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
13961
    oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
13962
    oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
13963
    oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
13964
    oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
13965
    oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
13966
},
13967
 
13968
/**
13969
 * Returns current CellEditor instance, or null.
13970
 * @method getCellEditor
13971
 * @return {YAHOO.widget.CellEditor} CellEditor instance.
13972
 */
13973
getCellEditor : function() {
13974
    return this._oCellEditor;
13975
},
13976
 
13977
 
13978
/**
13979
 * Activates and shows CellEditor instance for the given cell while deactivating and
13980
 * canceling previous CellEditor. It is baked into DataTable that only one CellEditor
13981
 * can be active at any given time.
13982
 *
13983
 * @method showCellEditor
13984
 * @param elCell {HTMLElement | String} Cell to edit.
13985
 */
13986
showCellEditor : function(elCell, oRecord, oColumn) {
13987
    // Get a particular CellEditor
13988
    elCell = this.getTdEl(elCell);
13989
    if(elCell) {
13990
        oColumn = this.getColumn(elCell);
13991
        if(oColumn && oColumn.editor) {
13992
            var oCellEditor = this._oCellEditor;
13993
            // Clean up active CellEditor
13994
            if(oCellEditor) {
13995
                if(this._oCellEditor.cancel) {
13996
                    this._oCellEditor.cancel();
13997
                }
13998
                else if(oCellEditor.isActive) {
13999
                    this.cancelCellEditor();
14000
                }
14001
            }
14002
 
14003
            if(oColumn.editor instanceof YAHOO.widget.BaseCellEditor) {
14004
                // Get CellEditor
14005
                oCellEditor = oColumn.editor;
14006
                var ok = oCellEditor.attach(this, elCell);
14007
                if(ok) {
14008
                    oCellEditor.render();
14009
                    oCellEditor.move();
14010
                    ok = this.doBeforeShowCellEditor(oCellEditor);
14011
                    if(ok) {
14012
                        oCellEditor.show();
14013
                        this._oCellEditor = oCellEditor;
14014
                    }
14015
                }
14016
            }
14017
            // Backward compatibility
14018
            else {
14019
                    if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) {
14020
                        oRecord = this.getRecord(elCell);
14021
                    }
14022
                    if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) {
14023
                        oColumn = this.getColumn(elCell);
14024
                    }
14025
                    if(oRecord && oColumn) {
14026
                        if(!this._oCellEditor || this._oCellEditor.container) {
14027
                            this._initCellEditorEl();
14028
                        }
14029
 
14030
                        // Update Editor values
14031
                        oCellEditor = this._oCellEditor;
14032
                        oCellEditor.cell = elCell;
14033
                        oCellEditor.record = oRecord;
14034
                        oCellEditor.column = oColumn;
14035
                        oCellEditor.validator = (oColumn.editorOptions &&
14036
                                lang.isFunction(oColumn.editorOptions.validator)) ?
14037
                                oColumn.editorOptions.validator : null;
14038
                        oCellEditor.value = oRecord.getData(oColumn.key);
14039
                        oCellEditor.defaultValue = null;
14040
 
14041
                        // Move Editor
14042
                        var elContainer = oCellEditor.container;
14043
                        var x = Dom.getX(elCell);
14044
                        var y = Dom.getY(elCell);
14045
 
14046
                        // SF doesn't get xy for cells in scrolling table
14047
                        // when tbody display is set to block
14048
                        if(isNaN(x) || isNaN(y)) {
14049
                            x = elCell.offsetLeft + // cell pos relative to table
14050
                                    Dom.getX(this._elTbody.parentNode) - // plus table pos relative to document
14051
                                    this._elTbody.scrollLeft; // minus tbody scroll
14052
                            y = elCell.offsetTop + // cell pos relative to table
14053
                                    Dom.getY(this._elTbody.parentNode) - // plus table pos relative to document
14054
                                    this._elTbody.scrollTop + // minus tbody scroll
14055
                                    this._elThead.offsetHeight; // account for fixed THEAD cells
14056
                        }
14057
 
14058
                        elContainer.style.left = x + "px";
14059
                        elContainer.style.top = y + "px";
14060
 
14061
                        // Hook to customize the UI
14062
                        this.doBeforeShowCellEditor(this._oCellEditor);
14063
 
14064
                        //TODO: This is temporarily up here due so elements can be focused
14065
                        // Show Editor
14066
                        elContainer.style.display = "";
14067
 
14068
                        // Handle ESC key
14069
                        Ev.addListener(elContainer, "keydown", function(e, oSelf) {
14070
                            // ESC hides Cell Editor
14071
                            if((e.keyCode == 27)) {
14072
                                oSelf.cancelCellEditor();
14073
                                oSelf.focusTbodyEl();
14074
                            }
14075
                            else {
14076
                                oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e});
14077
                            }
14078
                        }, this);
14079
 
14080
                        // Render Editor markup
14081
                        var fnEditor;
14082
                        if(lang.isString(oColumn.editor)) {
14083
                            switch(oColumn.editor) {
14084
                                case "checkbox":
14085
                                    fnEditor = DT.editCheckbox;
14086
                                    break;
14087
                                case "date":
14088
                                    fnEditor = DT.editDate;
14089
                                    break;
14090
                                case "dropdown":
14091
                                    fnEditor = DT.editDropdown;
14092
                                    break;
14093
                                case "radio":
14094
                                    fnEditor = DT.editRadio;
14095
                                    break;
14096
                                case "textarea":
14097
                                    fnEditor = DT.editTextarea;
14098
                                    break;
14099
                                case "textbox":
14100
                                    fnEditor = DT.editTextbox;
14101
                                    break;
14102
                                default:
14103
                                    fnEditor = null;
14104
                            }
14105
                        }
14106
                        else if(lang.isFunction(oColumn.editor)) {
14107
                            fnEditor = oColumn.editor;
14108
                        }
14109
 
14110
                        if(fnEditor) {
14111
                            // Create DOM input elements
14112
                            fnEditor(this._oCellEditor, this);
14113
 
14114
                            // Show Save/Cancel buttons
14115
                            if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) {
14116
                                this.showCellEditorBtns(elContainer);
14117
                            }
14118
 
14119
                            oCellEditor.isActive = true;
14120
 
14121
                            //TODO: verify which args to pass
14122
                            this.fireEvent("editorShowEvent", {editor:oCellEditor});
14123
                            YAHOO.log("Cell Editor shown for " + elCell, "info", this.toString());
14124
                            return;
14125
                        }
14126
                    }
14127
 
14128
 
14129
 
14130
 
14131
            }
14132
        }
14133
    }
14134
},
14135
 
14136
/**
14137
 * Backward compatibility.
14138
 *
14139
 * @method _initCellEditorEl
14140
 * @private
14141
 * @deprecated Use BaseCellEditor class.
14142
 */
14143
_initCellEditorEl : function() {
14144
    // Attach Cell Editor container element as first child of body
14145
    var elCellEditor = document.createElement("div");
14146
    elCellEditor.id = this._sId + "-celleditor";
14147
    elCellEditor.style.display = "none";
14148
    elCellEditor.tabIndex = 0;
14149
    Dom.addClass(elCellEditor, DT.CLASS_EDITOR);
14150
    var elFirstChild = Dom.getFirstChild(document.body);
14151
    if(elFirstChild) {
14152
        elCellEditor = Dom.insertBefore(elCellEditor, elFirstChild);
14153
    }
14154
    else {
14155
        elCellEditor = document.body.appendChild(elCellEditor);
14156
    }
14157
 
14158
    // Internal tracker of Cell Editor values
14159
    var oCellEditor = {};
14160
    oCellEditor.container = elCellEditor;
14161
    oCellEditor.value = null;
14162
    oCellEditor.isActive = false;
14163
    this._oCellEditor = oCellEditor;
14164
},
14165
 
14166
/**
14167
 * Overridable abstract method to customize CellEditor before showing.
14168
 *
14169
 * @method doBeforeShowCellEditor
14170
 * @param oCellEditor {YAHOO.widget.CellEditor} The CellEditor instance.
14171
 * @return {Boolean} Return true to continue showing CellEditor.
14172
 */
14173
doBeforeShowCellEditor : function(oCellEditor) {
14174
    return true;
14175
},
14176
 
14177
/**
14178
 * Saves active CellEditor input to Record and upates DOM UI.
14179
 *
14180
 * @method saveCellEditor
14181
 */
14182
saveCellEditor : function() {
14183
    if(this._oCellEditor) {
14184
        if(this._oCellEditor.save) {
14185
            this._oCellEditor.save();
14186
        }
14187
        // Backward compatibility
14188
        else if(this._oCellEditor.isActive) {
14189
            var newData = this._oCellEditor.value;
14190
            // Copy the data to pass to the event
14191
            //var oldData = YAHOO.widget.DataTable._cloneObject(this._oCellEditor.record.getData(this._oCellEditor.column.key));
14192
            var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key);
14193
 
14194
            // Validate input data
14195
            if(this._oCellEditor.validator) {
14196
                newData = this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData, this._oCellEditor);
14197
                if(newData === null ) {
14198
                    this.resetCellEditor();
14199
                    this.fireEvent("editorRevertEvent",
14200
                            {editor:this._oCellEditor, oldData:oldData, newData:newData});
14201
                    YAHOO.log("Could not save Cell Editor input due to invalid data " +
14202
                            lang.dump(newData), "warn", this.toString());
14203
                    return;
14204
                }
14205
            }
14206
            // Update the Record
14207
            this._oRecordSet.updateRecordValue(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
14208
            // Update the UI
14209
            this.formatCell(this._oCellEditor.cell.firstChild, this._oCellEditor.record, this._oCellEditor.column);
14210
 
14211
            // Bug fix 1764044
14212
            this._oChainRender.add({
14213
                method: function() {
14214
                    this.validateColumnWidths();
14215
                },
14216
                scope: this
14217
            });
14218
            this._oChainRender.run();
14219
            // Clear out the Cell Editor
14220
            this.resetCellEditor();
14221
 
14222
            this.fireEvent("editorSaveEvent",
14223
                    {editor:this._oCellEditor, oldData:oldData, newData:newData});
14224
            YAHOO.log("Cell Editor input saved", "info", this.toString());
14225
        }
14226
    }
14227
},
14228
 
14229
/**
14230
 * Cancels active CellEditor.
14231
 *
14232
 * @method cancelCellEditor
14233
 */
14234
cancelCellEditor : function() {
14235
    if(this._oCellEditor) {
14236
        if(this._oCellEditor.cancel) {
14237
            this._oCellEditor.cancel();
14238
        }
14239
        // Backward compatibility
14240
        else if(this._oCellEditor.isActive) {
14241
            this.resetCellEditor();
14242
            //TODO: preserve values for the event?
14243
            this.fireEvent("editorCancelEvent", {editor:this._oCellEditor});
14244
            YAHOO.log("Cell Editor input canceled", "info", this.toString());
14245
        }
14246
 
14247
        YAHOO.log("CellEditor input canceled", "info", this.toString());
14248
    }
14249
},
14250
 
14251
/**
14252
 * Destroys active CellEditor instance and UI.
14253
 *
14254
 * @method destroyCellEditor
14255
 */
14256
destroyCellEditor : function() {
14257
    if(this._oCellEditor) {
14258
        this._oCellEditor.destroy();
14259
        this._oCellEditor = null;
14260
    }
14261
},
14262
 
14263
/**
14264
 * Passes through showEvent of the active CellEditor.
14265
 *
14266
 * @method _onEditorShowEvent
14267
 * @param oArgs {Object}  Custom Event args.
14268
 * @private
14269
 */
14270
_onEditorShowEvent : function(oArgs) {
14271
    this.fireEvent("editorShowEvent", oArgs);
14272
},
14273
 
14274
/**
14275
 * Passes through keydownEvent of the active CellEditor.
14276
 * @param oArgs {Object}  Custom Event args.
14277
 *
14278
 * @method _onEditorKeydownEvent
14279
 * @private
14280
 */
14281
_onEditorKeydownEvent : function(oArgs) {
14282
    this.fireEvent("editorKeydownEvent", oArgs);
14283
},
14284
 
14285
/**
14286
 * Passes through revertEvent of the active CellEditor.
14287
 *
14288
 * @method _onEditorRevertEvent
14289
 * @param oArgs {Object}  Custom Event args.
14290
 * @private
14291
 */
14292
_onEditorRevertEvent : function(oArgs) {
14293
    this.fireEvent("editorRevertEvent", oArgs);
14294
},
14295
 
14296
/**
14297
 * Passes through saveEvent of the active CellEditor.
14298
 *
14299
 * @method _onEditorSaveEvent
14300
 * @param oArgs {Object}  Custom Event args.
14301
 * @private
14302
 */
14303
_onEditorSaveEvent : function(oArgs) {
14304
    this.fireEvent("editorSaveEvent", oArgs);
14305
},
14306
 
14307
/**
14308
 * Passes through cancelEvent of the active CellEditor.
14309
 *
14310
 * @method _onEditorCancelEvent
14311
 * @param oArgs {Object}  Custom Event args.
14312
 * @private
14313
 */
14314
_onEditorCancelEvent : function(oArgs) {
14315
    this.fireEvent("editorCancelEvent", oArgs);
14316
},
14317
 
14318
/**
14319
 * Passes through blurEvent of the active CellEditor.
14320
 *
14321
 * @method _onEditorBlurEvent
14322
 * @param oArgs {Object}  Custom Event args.
14323
 * @private
14324
 */
14325
_onEditorBlurEvent : function(oArgs) {
14326
    this.fireEvent("editorBlurEvent", oArgs);
14327
},
14328
 
14329
/**
14330
 * Passes through blockEvent of the active CellEditor.
14331
 *
14332
 * @method _onEditorBlockEvent
14333
 * @param oArgs {Object}  Custom Event args.
14334
 * @private
14335
 */
14336
_onEditorBlockEvent : function(oArgs) {
14337
    this.fireEvent("editorBlockEvent", oArgs);
14338
},
14339
 
14340
/**
14341
 * Passes through unblockEvent of the active CellEditor.
14342
 *
14343
 * @method _onEditorUnblockEvent
14344
 * @param oArgs {Object}  Custom Event args.
14345
 * @private
14346
 */
14347
_onEditorUnblockEvent : function(oArgs) {
14348
    this.fireEvent("editorUnblockEvent", oArgs);
14349
},
14350
 
14351
/**
14352
 * Public handler of the editorBlurEvent. By default, saves on blur if
14353
 * disableBtns is true, otherwise cancels on blur.
14354
 *
14355
 * @method onEditorBlurEvent
14356
 * @param oArgs {Object}  Custom Event args.
14357
 */
14358
onEditorBlurEvent : function(oArgs) {
14359
    if(oArgs.editor.disableBtns) {
14360
        // Save on blur
14361
        if(oArgs.editor.save) { // Backward incompatible
14362
            oArgs.editor.save();
14363
        }
14364
    }
14365
    else if(oArgs.editor.cancel) { // Backward incompatible
14366
        // Cancel on blur
14367
        oArgs.editor.cancel();
14368
    }
14369
},
14370
 
14371
/**
14372
 * Public handler of the editorBlockEvent. By default, disables DataTable UI.
14373
 *
14374
 * @method onEditorBlockEvent
14375
 * @param oArgs {Object}  Custom Event args.
14376
 */
14377
onEditorBlockEvent : function(oArgs) {
14378
    this.disable();
14379
},
14380
 
14381
/**
14382
 * Public handler of the editorUnblockEvent. By default, undisables DataTable UI.
14383
 *
14384
 * @method onEditorUnblockEvent
14385
 * @param oArgs {Object}  Custom Event args.
14386
 */
14387
onEditorUnblockEvent : function(oArgs) {
14388
    this.undisable();
14389
},
14390
 
14391
 
14392
 
14393
 
14394
 
14395
 
14396
 
14397
 
14398
 
14399
 
14400
 
14401
 
14402
 
14403
 
14404
 
14405
 
14406
 
14407
 
14408
 
14409
 
14410
 
14411
 
14412
 
14413
 
14414
 
14415
 
14416
 
14417
 
14418
 
14419
 
14420
 
14421
 
14422
 
14423
 
14424
 
14425
 
14426
 
14427
 
14428
// ABSTRACT METHODS
14429
 
14430
/**
14431
 * Overridable method gives implementers a hook to access data before
14432
 * it gets added to RecordSet and rendered to the TBODY.
14433
 *
14434
 * @method doBeforeLoadData
14435
 * @param sRequest {String} Original request.
14436
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14437
 * @param oPayload {MIXED} additional arguments
14438
 * @return {Boolean} Return true to continue loading data into RecordSet and
14439
 * updating DataTable with new Records, false to cancel.
14440
 */
14441
doBeforeLoadData : function(sRequest, oResponse, oPayload) {
14442
    return true;
14443
},
14444
 
14445
 
14446
 
14447
 
14448
 
14449
 
14450
 
14451
 
14452
 
14453
 
14454
 
14455
 
14456
 
14457
 
14458
 
14459
 
14460
 
14461
 
14462
 
14463
 
14464
 
14465
 
14466
 
14467
 
14468
 
14469
 
14470
 
14471
 
14472
 
14473
 
14474
 
14475
 
14476
 
14477
 
14478
 
14479
 
14480
 
14481
 
14482
 
14483
 
14484
 
14485
 
14486
 
14487
 
14488
 
14489
 
14490
 
14491
 
14492
 
14493
 
14494
 
14495
 
14496
 
14497
 
14498
 
14499
 
14500
 
14501
 
14502
 
14503
 
14504
 
14505
 
14506
 
14507
/////////////////////////////////////////////////////////////////////////////
14508
//
14509
// Public Custom Event Handlers
14510
//
14511
/////////////////////////////////////////////////////////////////////////////
14512
 
14513
/**
14514
 * Custom event handler to sort Column.
14515
 *
14516
 * @method onEventSortColumn
14517
 * @param oArgs.event {HTMLEvent} Event object.
14518
 * @param oArgs.target {HTMLElement} Target element.
14519
 */
14520
onEventSortColumn : function(oArgs) {
14521
//TODO: support form elements in sortable columns
14522
    var evt = oArgs.event;
14523
    var target = oArgs.target;
14524
 
14525
    var el = this.getThEl(target) || this.getTdEl(target);
14526
    if(el) {
14527
        var oColumn = this.getColumn(el);
14528
        if(oColumn.sortable) {
14529
            Ev.stopEvent(evt);
14530
            this.sortColumn(oColumn);
14531
        }
14532
    }
14533
    else {
14534
        YAHOO.log("Could not find Column for " + target, "warn", this.toString());
14535
    }
14536
},
14537
 
14538
/**
14539
 * Custom event handler to select Column.
14540
 *
14541
 * @method onEventSelectColumn
14542
 * @param oArgs.event {HTMLEvent} Event object.
14543
 * @param oArgs.target {HTMLElement} Target element.
14544
 */
14545
onEventSelectColumn : function(oArgs) {
14546
    this.selectColumn(oArgs.target);
14547
},
14548
 
14549
/**
14550
 * Custom event handler to highlight Column. Accounts for spurious
14551
 * caused-by-child events.
14552
 *
14553
 * @method onEventHighlightColumn
14554
 * @param oArgs.event {HTMLEvent} Event object.
14555
 * @param oArgs.target {HTMLElement} Target element.
14556
 */
14557
onEventHighlightColumn : function(oArgs) {
14558
    this.highlightColumn(oArgs.target);
14559
},
14560
 
14561
/**
14562
 * Custom event handler to unhighlight Column. Accounts for spurious
14563
 * caused-by-child events.
14564
 *
14565
 * @method onEventUnhighlightColumn
14566
 * @param oArgs.event {HTMLEvent} Event object.
14567
 * @param oArgs.target {HTMLElement} Target element.
14568
 */
14569
onEventUnhighlightColumn : function(oArgs) {
14570
    this.unhighlightColumn(oArgs.target);
14571
},
14572
 
14573
/**
14574
 * Custom event handler to manage selection according to desktop paradigm.
14575
 *
14576
 * @method onEventSelectRow
14577
 * @param oArgs.event {HTMLEvent} Event object.
14578
 * @param oArgs.target {HTMLElement} Target element.
14579
 */
14580
onEventSelectRow : function(oArgs) {
14581
    var sMode = this.get("selectionMode");
14582
    if(sMode == "single") {
14583
        this._handleSingleSelectionByMouse(oArgs);
14584
    }
14585
    else {
14586
        this._handleStandardSelectionByMouse(oArgs);
14587
    }
14588
},
14589
 
14590
/**
14591
 * Custom event handler to select cell.
14592
 *
14593
 * @method onEventSelectCell
14594
 * @param oArgs.event {HTMLEvent} Event object.
14595
 * @param oArgs.target {HTMLElement} Target element.
14596
 */
14597
onEventSelectCell : function(oArgs) {
14598
    var sMode = this.get("selectionMode");
14599
    if(sMode == "cellblock") {
14600
        this._handleCellBlockSelectionByMouse(oArgs);
14601
    }
14602
    else if(sMode == "cellrange") {
14603
        this._handleCellRangeSelectionByMouse(oArgs);
14604
    }
14605
    else {
14606
        this._handleSingleCellSelectionByMouse(oArgs);
14607
    }
14608
},
14609
 
14610
/**
14611
 * Custom event handler to highlight row. Accounts for spurious
14612
 * caused-by-child events.
14613
 *
14614
 * @method onEventHighlightRow
14615
 * @param oArgs.event {HTMLEvent} Event object.
14616
 * @param oArgs.target {HTMLElement} Target element.
14617
 */
14618
onEventHighlightRow : function(oArgs) {
14619
    this.highlightRow(oArgs.target);
14620
},
14621
 
14622
/**
14623
 * Custom event handler to unhighlight row. Accounts for spurious
14624
 * caused-by-child events.
14625
 *
14626
 * @method onEventUnhighlightRow
14627
 * @param oArgs.event {HTMLEvent} Event object.
14628
 * @param oArgs.target {HTMLElement} Target element.
14629
 */
14630
onEventUnhighlightRow : function(oArgs) {
14631
    this.unhighlightRow(oArgs.target);
14632
},
14633
 
14634
/**
14635
 * Custom event handler to highlight cell. Accounts for spurious
14636
 * caused-by-child events.
14637
 *
14638
 * @method onEventHighlightCell
14639
 * @param oArgs.event {HTMLEvent} Event object.
14640
 * @param oArgs.target {HTMLElement} Target element.
14641
 */
14642
onEventHighlightCell : function(oArgs) {
14643
    this.highlightCell(oArgs.target);
14644
},
14645
 
14646
/**
14647
 * Custom event handler to unhighlight cell. Accounts for spurious
14648
 * caused-by-child events.
14649
 *
14650
 * @method onEventUnhighlightCell
14651
 * @param oArgs.event {HTMLEvent} Event object.
14652
 * @param oArgs.target {HTMLElement} Target element.
14653
 */
14654
onEventUnhighlightCell : function(oArgs) {
14655
    this.unhighlightCell(oArgs.target);
14656
},
14657
 
14658
/**
14659
 * Custom event handler to format cell.
14660
 *
14661
 * @method onEventFormatCell
14662
 * @param oArgs.event {HTMLEvent} Event object.
14663
 * @param oArgs.target {HTMLElement} Target element.
14664
 */
14665
onEventFormatCell : function(oArgs) {
14666
    var target = oArgs.target;
14667
 
14668
    var elCell = this.getTdEl(target);
14669
    if(elCell) {
14670
        var oColumn = this.getColumn(this.getCellIndex(elCell));
14671
        this.formatCell(elCell.firstChild, this.getRecord(elCell), oColumn);
14672
    }
14673
    else {
14674
        YAHOO.log("Could not format cell " + target, "warn", this.toString());
14675
    }
14676
},
14677
 
14678
/**
14679
 * Custom event handler to edit cell.
14680
 *
14681
 * @method onEventShowCellEditor
14682
 * @param oArgs.event {HTMLEvent} Event object.
14683
 * @param oArgs.target {HTMLElement} Target element.
14684
 */
14685
onEventShowCellEditor : function(oArgs) {
14686
    if(!this.isDisabled()) {
14687
        this.showCellEditor(oArgs.target);
14688
    }
14689
},
14690
 
14691
/**
14692
 * Custom event handler to save active CellEditor input.
14693
 *
14694
 * @method onEventSaveCellEditor
14695
 */
14696
onEventSaveCellEditor : function(oArgs) {
14697
    if(this._oCellEditor) {
14698
        if(this._oCellEditor.save) {
14699
            this._oCellEditor.save();
14700
        }
14701
        // Backward compatibility
14702
        else {
14703
            this.saveCellEditor();
14704
        }
14705
    }
14706
},
14707
 
14708
/**
14709
 * Custom event handler to cancel active CellEditor.
14710
 *
14711
 * @method onEventCancelCellEditor
14712
 */
14713
onEventCancelCellEditor : function(oArgs) {
14714
    if(this._oCellEditor) {
14715
        if(this._oCellEditor.cancel) {
14716
            this._oCellEditor.cancel();
14717
        }
14718
        // Backward compatibility
14719
        else {
14720
            this.cancelCellEditor();
14721
        }
14722
    }
14723
},
14724
 
14725
/**
14726
 * Callback function receives data from DataSource and populates an entire
14727
 * DataTable with Records and TR elements, clearing previous Records, if any.
14728
 *
14729
 * @method onDataReturnInitializeTable
14730
 * @param sRequest {String} Original request.
14731
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14732
 * @param oPayload {MIXED} (optional) Additional argument(s)
14733
 */
14734
onDataReturnInitializeTable : function(sRequest, oResponse, oPayload) {
14735
    if((this instanceof DT) && this._sId) {
14736
        this.initializeTable();
14737
 
14738
        this.onDataReturnSetRows(sRequest,oResponse,oPayload);
14739
    }
14740
},
14741
 
14742
/**
14743
 * Callback function receives reponse from DataSource, replaces all existing
14744
 * Records in  RecordSet, updates TR elements with new data, and updates state
14745
 * UI for pagination and sorting from payload data, if necessary.
14746
 *
14747
 * @method onDataReturnReplaceRows
14748
 * @param oRequest {MIXED} Original generated request.
14749
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14750
 * @param oPayload {MIXED} (optional) Additional argument(s)
14751
 */
14752
onDataReturnReplaceRows : function(oRequest, oResponse, oPayload) {
14753
    if((this instanceof DT) && this._sId) {
14754
        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
14755
 
14756
        // Pass data through abstract method for any transformations
14757
        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
14758
            pag   = this.get('paginator'),
14759
            index = 0;
14760
 
14761
        // Data ok to set
14762
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14763
            // Update Records
14764
            this._oRecordSet.reset();
14765
 
14766
            if (this.get('dynamicData')) {
14767
                if (oPayload && oPayload.pagination &&
14768
                    lang.isNumber(oPayload.pagination.recordOffset)) {
14769
                    index = oPayload.pagination.recordOffset;
14770
                } else if (pag) {
14771
                    index = pag.getStartIndex();
14772
                }
14773
            }
14774
 
14775
            this._oRecordSet.setRecords(oResponse.results, index | 0);
14776
 
14777
            // Update state
14778
            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
14779
 
14780
            // Update UI
14781
            this.render();
14782
        }
14783
        // Error
14784
        else if(ok && oResponse.error) {
14785
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14786
        }
14787
    }
14788
},
14789
 
14790
/**
14791
 * Callback function receives data from DataSource and appends to an existing
14792
 * DataTable new Records and, if applicable, creates or updates
14793
 * corresponding TR elements.
14794
 *
14795
 * @method onDataReturnAppendRows
14796
 * @param sRequest {String} Original request.
14797
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14798
 * @param oPayload {MIXED} (optional) Additional argument(s)
14799
 */
14800
onDataReturnAppendRows : function(sRequest, oResponse, oPayload) {
14801
    if((this instanceof DT) && this._sId) {
14802
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
14803
 
14804
        // Pass data through abstract method for any transformations
14805
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
14806
 
14807
        // Data ok to append
14808
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14809
            // Append rows
14810
            this.addRows(oResponse.results);
14811
 
14812
            // Update state
14813
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
14814
        }
14815
        // Error
14816
        else if(ok && oResponse.error) {
14817
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14818
        }
14819
    }
14820
},
14821
 
14822
/**
14823
 * Callback function receives data from DataSource and inserts new records
14824
 * starting at the index specified in oPayload.insertIndex. The value for
14825
 * oPayload.insertIndex can be populated when sending the request to the DataSource,
14826
 * or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime.
14827
 * If applicable, creates or updates corresponding TR elements.
14828
 *
14829
 * @method onDataReturnInsertRows
14830
 * @param sRequest {String} Original request.
14831
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14832
 * @param oPayload {MIXED} Argument payload, looks in oPayload.insertIndex.
14833
 */
14834
onDataReturnInsertRows : function(sRequest, oResponse, oPayload) {
14835
    if((this instanceof DT) && this._sId) {
14836
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
14837
 
14838
        // Pass data through abstract method for any transformations
14839
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
14840
 
14841
        // Data ok to append
14842
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14843
            // Insert rows
14844
            this.addRows(oResponse.results, (oPayload ? oPayload.insertIndex : 0));
14845
 
14846
            // Update state
14847
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
14848
        }
14849
        // Error
14850
        else if(ok && oResponse.error) {
14851
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14852
        }
14853
    }
14854
},
14855
 
14856
/**
14857
 * Callback function receives data from DataSource and incrementally updates Records
14858
 * starting at the index specified in oPayload.updateIndex. The value for
14859
 * oPayload.updateIndex can be populated when sending the request to the DataSource,
14860
 * or by accessing oPayload.updateIndex with the doBeforeLoadData() method at runtime.
14861
 * If applicable, creates or updates corresponding TR elements.
14862
 *
14863
 * @method onDataReturnUpdateRows
14864
 * @param sRequest {String} Original request.
14865
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14866
 * @param oPayload {MIXED} Argument payload, looks in oPayload.updateIndex.
14867
 */
14868
onDataReturnUpdateRows : function(sRequest, oResponse, oPayload) {
14869
    if((this instanceof DT) && this._sId) {
14870
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
14871
 
14872
        // Pass data through abstract method for any transformations
14873
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
14874
 
14875
        // Data ok to append
14876
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14877
            // Insert rows
14878
            this.updateRows((oPayload ? oPayload.updateIndex : 0), oResponse.results);
14879
 
14880
            // Update state
14881
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
14882
        }
14883
        // Error
14884
        else if(ok && oResponse.error) {
14885
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14886
        }
14887
    }
14888
},
14889
 
14890
/**
14891
 * Callback function receives reponse from DataSource and populates the
14892
 * RecordSet with the results.
14893
 *
14894
 * @method onDataReturnSetRows
14895
 * @param oRequest {MIXED} Original generated request.
14896
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14897
 * @param oPayload {MIXED} (optional) Additional argument(s)
14898
 */
14899
onDataReturnSetRows : function(oRequest, oResponse, oPayload) {
14900
    if((this instanceof DT) && this._sId) {
14901
        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
14902
 
14903
        // Pass data through abstract method for any transformations
14904
        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
14905
            pag   = this.get('paginator'),
14906
            index = 0;
14907
 
14908
        // Data ok to set
14909
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14910
            // Update Records
14911
            if (this.get('dynamicData')) {
14912
                if (oPayload && oPayload.pagination &&
14913
                    lang.isNumber(oPayload.pagination.recordOffset)) {
14914
                    index = oPayload.pagination.recordOffset;
14915
                } else if (pag) {
14916
                    index = pag.getStartIndex();
14917
                }
14918
 
14919
                this._oRecordSet.reset(); // Bug 2290604: dyanmic data shouldn't keep accumulating by default
14920
            }
14921
 
14922
            this._oRecordSet.setRecords(oResponse.results, index | 0);
14923
 
14924
            // Update state
14925
            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
14926
 
14927
            // Update UI
14928
            this.render();
14929
        }
14930
        // Error
14931
        else if(ok && oResponse.error) {
14932
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14933
        }
14934
    }
14935
    else {
14936
        YAHOO.log("Instance destroyed before data returned.","info",this.toString());
14937
    }
14938
},
14939
 
14940
/**
14941
 * Hook to update oPayload before consumption.
14942
 *
14943
 * @method handleDataReturnPayload
14944
 * @param oRequest {MIXED} Original generated request.
14945
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14946
 * @param oPayload {MIXED} State values.
14947
 * @return oPayload {MIXED} State values.
14948
 */
14949
handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
14950
    return oPayload || {};
14951
},
14952
 
14953
/**
14954
 * Updates the DataTable with state data sent in an onDataReturn* payload.
14955
 *
14956
 * @method _handleDataReturnPayload
14957
 * @param oRequest {MIXED} Original generated request.
14958
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14959
 * @param oPayload {MIXED} State values
14960
 * @private
14961
 */
14962
_handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
14963
    oPayload = this.handleDataReturnPayload(oRequest, oResponse, oPayload);
14964
    if(oPayload) {
14965
        // Update pagination
14966
        var oPaginator = this.get('paginator');
14967
        if (oPaginator) {
14968
            // Update totalRecords
14969
            if(this.get("dynamicData")) {
14970
                if (widget.Paginator.isNumeric(oPayload.totalRecords)) {
14971
                    oPaginator.set('totalRecords',oPayload.totalRecords);
14972
                }
14973
            }
14974
            else {
14975
                oPaginator.set('totalRecords',this._oRecordSet.getLength());
14976
            }
14977
            // Update other paginator values
14978
            if (lang.isObject(oPayload.pagination)) {
14979
                oPaginator.set('rowsPerPage',oPayload.pagination.rowsPerPage);
14980
                oPaginator.set('recordOffset',oPayload.pagination.recordOffset);
14981
            }
14982
        }
14983
 
14984
        // Update sorting
14985
        if (oPayload.sortedBy) {
14986
            // Set the sorting values in preparation for refresh
14987
            this.set('sortedBy', oPayload.sortedBy);
14988
        }
14989
        // Backwards compatibility for sorting
14990
        else if (oPayload.sorting) {
14991
            // Set the sorting values in preparation for refresh
14992
            this.set('sortedBy', oPayload.sorting);
14993
        }
14994
    }
14995
},
14996
 
14997
 
14998
 
14999
 
15000
 
15001
 
15002
 
15003
 
15004
 
15005
 
15006
 
15007
 
15008
 
15009
 
15010
 
15011
 
15012
 
15013
 
15014
 
15015
 
15016
 
15017
 
15018
 
15019
 
15020
 
15021
 
15022
 
15023
 
15024
 
15025
 
15026
 
15027
 
15028
 
15029
    /////////////////////////////////////////////////////////////////////////////
15030
    //
15031
    // Custom Events
15032
    //
15033
    /////////////////////////////////////////////////////////////////////////////
15034
 
15035
    /**
15036
     * Fired when the DataTable's rows are rendered from an initialized state.
15037
     *
15038
     * @event initEvent
15039
     */
15040
 
15041
    /**
15042
     * Fired before the DataTable's DOM is rendered or modified.
15043
     *
15044
     * @event beforeRenderEvent
15045
     */
15046
 
15047
    /**
15048
     * Fired when the DataTable's DOM is rendered or modified.
15049
     *
15050
     * @event renderEvent
15051
     */
15052
 
15053
    /**
15054
     * Fired when the DataTable's post-render routine is complete, including
15055
     * Column width validations.
15056
     *
15057
     * @event postRenderEvent
15058
     */
15059
 
15060
    /**
15061
     * Fired when the DataTable is disabled.
15062
     *
15063
     * @event disableEvent
15064
     */
15065
 
15066
    /**
15067
     * Fired when the DataTable is undisabled.
15068
     *
15069
     * @event undisableEvent
15070
     */
15071
 
15072
    /**
15073
     * Fired when data is returned from DataSource but before it is consumed by
15074
     * DataTable.
15075
     *
15076
     * @event dataReturnEvent
15077
     * @param oArgs.request {String} Original request.
15078
     * @param oArgs.response {Object} Response object.
15079
     */
15080
 
15081
    /**
15082
     * Fired when the DataTable has a focus event.
15083
     *
15084
     * @event tableFocusEvent
15085
     */
15086
 
15087
    /**
15088
     * Fired when the DataTable THEAD element has a focus event.
15089
     *
15090
     * @event theadFocusEvent
15091
     */
15092
 
15093
    /**
15094
     * Fired when the DataTable TBODY element has a focus event.
15095
     *
15096
     * @event tbodyFocusEvent
15097
     */
15098
 
15099
    /**
15100
     * Fired when the DataTable has a blur event.
15101
     *
15102
     * @event tableBlurEvent
15103
     */
15104
 
15105
    /*TODO implement theadBlurEvent
15106
     * Fired when the DataTable THEAD element has a blur event.
15107
     *
15108
     * @event theadBlurEvent
15109
     */
15110
 
15111
    /*TODO: implement tbodyBlurEvent
15112
     * Fired when the DataTable TBODY element has a blur event.
15113
     *
15114
     * @event tbodyBlurEvent
15115
     */
15116
 
15117
    /**
15118
     * Fired when the DataTable has a key event.
15119
     *
15120
     * @event tableKeyEvent
15121
     * @param oArgs.event {HTMLEvent} The event object.
15122
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15123
     */
15124
 
15125
    /**
15126
     * Fired when the DataTable THEAD element has a key event.
15127
     *
15128
     * @event theadKeyEvent
15129
     * @param oArgs.event {HTMLEvent} The event object.
15130
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15131
     */
15132
 
15133
    /**
15134
     * Fired when the DataTable TBODY element has a key event.
15135
     *
15136
     * @event tbodyKeyEvent
15137
     * @param oArgs.event {HTMLEvent} The event object.
15138
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15139
     */
15140
 
15141
    /**
15142
     * Fired when the DataTable has a mouseover.
15143
     *
15144
     * @event tableMouseoverEvent
15145
     * @param oArgs.event {HTMLEvent} The event object.
15146
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15147
     *
15148
     */
15149
 
15150
    /**
15151
     * Fired when the DataTable has a mouseout.
15152
     *
15153
     * @event tableMouseoutEvent
15154
     * @param oArgs.event {HTMLEvent} The event object.
15155
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15156
     *
15157
     */
15158
 
15159
    /**
15160
     * Fired when the DataTable has a mousedown.
15161
     *
15162
     * @event tableMousedownEvent
15163
     * @param oArgs.event {HTMLEvent} The event object.
15164
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15165
     *
15166
     */
15167
 
15168
    /**
15169
     * Fired when the DataTable has a mouseup.
15170
     *
15171
     * @event tableMouseupEvent
15172
     * @param oArgs.event {HTMLEvent} The event object.
15173
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15174
     *
15175
     */
15176
 
15177
    /**
15178
     * Fired when the DataTable has a click.
15179
     *
15180
     * @event tableClickEvent
15181
     * @param oArgs.event {HTMLEvent} The event object.
15182
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15183
     *
15184
     */
15185
 
15186
    /**
15187
     * Fired when the DataTable has a dblclick.
15188
     *
15189
     * @event tableDblclickEvent
15190
     * @param oArgs.event {HTMLEvent} The event object.
15191
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
15192
     *
15193
     */
15194
 
15195
    /**
15196
     * Fired when a message is shown in the DataTable's message element.
15197
     *
15198
     * @event tableMsgShowEvent
15199
     * @param oArgs.html {HTML} The HTML displayed.
15200
     * @param oArgs.className {String} The className assigned.
15201
     *
15202
     */
15203
 
15204
    /**
15205
     * Fired when the DataTable's message element is hidden.
15206
     *
15207
     * @event tableMsgHideEvent
15208
     */
15209
 
15210
    /**
15211
     * Fired when a THEAD row has a mouseover.
15212
     *
15213
     * @event theadRowMouseoverEvent
15214
     * @param oArgs.event {HTMLEvent} The event object.
15215
     * @param oArgs.target {HTMLElement} The TR element.
15216
     */
15217
 
15218
    /**
15219
     * Fired when a THEAD row has a mouseout.
15220
     *
15221
     * @event theadRowMouseoutEvent
15222
     * @param oArgs.event {HTMLEvent} The event object.
15223
     * @param oArgs.target {HTMLElement} The TR element.
15224
     */
15225
 
15226
    /**
15227
     * Fired when a THEAD row has a mousedown.
15228
     *
15229
     * @event theadRowMousedownEvent
15230
     * @param oArgs.event {HTMLEvent} The event object.
15231
     * @param oArgs.target {HTMLElement} The TR element.
15232
     */
15233
 
15234
    /**
15235
     * Fired when a THEAD row has a mouseup.
15236
     *
15237
     * @event theadRowMouseupEvent
15238
     * @param oArgs.event {HTMLEvent} The event object.
15239
     * @param oArgs.target {HTMLElement} The TR element.
15240
     */
15241
 
15242
    /**
15243
     * Fired when a THEAD row has a click.
15244
     *
15245
     * @event theadRowClickEvent
15246
     * @param oArgs.event {HTMLEvent} The event object.
15247
     * @param oArgs.target {HTMLElement} The TR element.
15248
     */
15249
 
15250
    /**
15251
     * Fired when a THEAD row has a dblclick.
15252
     *
15253
     * @event theadRowDblclickEvent
15254
     * @param oArgs.event {HTMLEvent} The event object.
15255
     * @param oArgs.target {HTMLElement} The TR element.
15256
     */
15257
 
15258
    /**
15259
     * Fired when a THEAD cell has a mouseover.
15260
     *
15261
     * @event theadCellMouseoverEvent
15262
     * @param oArgs.event {HTMLEvent} The event object.
15263
     * @param oArgs.target {HTMLElement} The TH element.
15264
     *
15265
     */
15266
 
15267
    /**
15268
     * Fired when a THEAD cell has a mouseout.
15269
     *
15270
     * @event theadCellMouseoutEvent
15271
     * @param oArgs.event {HTMLEvent} The event object.
15272
     * @param oArgs.target {HTMLElement} The TH element.
15273
     *
15274
     */
15275
 
15276
    /**
15277
     * Fired when a THEAD cell has a mousedown.
15278
     *
15279
     * @event theadCellMousedownEvent
15280
     * @param oArgs.event {HTMLEvent} The event object.
15281
     * @param oArgs.target {HTMLElement} The TH element.
15282
     */
15283
 
15284
    /**
15285
     * Fired when a THEAD cell has a mouseup.
15286
     *
15287
     * @event theadCellMouseupEvent
15288
     * @param oArgs.event {HTMLEvent} The event object.
15289
     * @param oArgs.target {HTMLElement} The TH element.
15290
     */
15291
 
15292
    /**
15293
     * Fired when a THEAD cell has a click.
15294
     *
15295
     * @event theadCellClickEvent
15296
     * @param oArgs.event {HTMLEvent} The event object.
15297
     * @param oArgs.target {HTMLElement} The TH element.
15298
     */
15299
 
15300
    /**
15301
     * Fired when a THEAD cell has a dblclick.
15302
     *
15303
     * @event theadCellDblclickEvent
15304
     * @param oArgs.event {HTMLEvent} The event object.
15305
     * @param oArgs.target {HTMLElement} The TH element.
15306
     */
15307
 
15308
    /**
15309
     * Fired when a THEAD label has a mouseover.
15310
     *
15311
     * @event theadLabelMouseoverEvent
15312
     * @param oArgs.event {HTMLEvent} The event object.
15313
     * @param oArgs.target {HTMLElement} The SPAN element.
15314
     *
15315
     */
15316
 
15317
    /**
15318
     * Fired when a THEAD label has a mouseout.
15319
     *
15320
     * @event theadLabelMouseoutEvent
15321
     * @param oArgs.event {HTMLEvent} The event object.
15322
     * @param oArgs.target {HTMLElement} The SPAN element.
15323
     *
15324
     */
15325
 
15326
    /**
15327
     * Fired when a THEAD label has a mousedown.
15328
     *
15329
     * @event theadLabelMousedownEvent
15330
     * @param oArgs.event {HTMLEvent} The event object.
15331
     * @param oArgs.target {HTMLElement} The SPAN element.
15332
     */
15333
 
15334
    /**
15335
     * Fired when a THEAD label has a mouseup.
15336
     *
15337
     * @event theadLabelMouseupEvent
15338
     * @param oArgs.event {HTMLEvent} The event object.
15339
     * @param oArgs.target {HTMLElement} The SPAN element.
15340
     */
15341
 
15342
    /**
15343
     * Fired when a THEAD label has a click.
15344
     *
15345
     * @event theadLabelClickEvent
15346
     * @param oArgs.event {HTMLEvent} The event object.
15347
     * @param oArgs.target {HTMLElement} The SPAN element.
15348
     */
15349
 
15350
    /**
15351
     * Fired when a THEAD label has a dblclick.
15352
     *
15353
     * @event theadLabelDblclickEvent
15354
     * @param oArgs.event {HTMLEvent} The event object.
15355
     * @param oArgs.target {HTMLElement} The SPAN element.
15356
     */
15357
 
15358
    /**
15359
     * Fired when a column is sorted.
15360
     *
15361
     * @event columnSortEvent
15362
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15363
     * @param oArgs.dir {String} Sort direction: YAHOO.widget.DataTable.CLASS_ASC
15364
     * or YAHOO.widget.DataTable.CLASS_DESC.
15365
     */
15366
 
15367
    /**
15368
     * Fired when a column width is set.
15369
     *
15370
     * @event columnSetWidthEvent
15371
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15372
     * @param oArgs.width {Number} The width in pixels.
15373
     */
15374
 
15375
    /**
15376
     * Fired when a column width is unset.
15377
     *
15378
     * @event columnUnsetWidthEvent
15379
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15380
     */
15381
 
15382
    /**
15383
     * Fired when a column is drag-resized.
15384
     *
15385
     * @event columnResizeEvent
15386
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15387
     * @param oArgs.target {HTMLElement} The TH element.
15388
     * @param oArgs.width {Number} Width in pixels.
15389
     */
15390
 
15391
    /**
15392
     * Fired when a Column is moved to a new index.
15393
     *
15394
     * @event columnReorderEvent
15395
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15396
     * @param oArgs.oldIndex {Number} The previous tree index position.
15397
     */
15398
 
15399
    /**
15400
     * Fired when a column is hidden.
15401
     *
15402
     * @event columnHideEvent
15403
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15404
     */
15405
 
15406
    /**
15407
     * Fired when a column is shown.
15408
     *
15409
     * @event columnShowEvent
15410
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15411
     */
15412
 
15413
    /**
15414
     * Fired when a column is selected.
15415
     *
15416
     * @event columnSelectEvent
15417
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15418
     */
15419
 
15420
    /**
15421
     * Fired when a column is unselected.
15422
     *
15423
     * @event columnUnselectEvent
15424
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15425
     */
15426
    /**
15427
     * Fired when a column is removed.
15428
     *
15429
     * @event columnRemoveEvent
15430
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15431
     */
15432
 
15433
    /**
15434
     * Fired when a column is inserted.
15435
     *
15436
     * @event columnInsertEvent
15437
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15438
     * @param oArgs.index {Number} The index position.
15439
     */
15440
 
15441
    /**
15442
     * Fired when a column is highlighted.
15443
     *
15444
     * @event columnHighlightEvent
15445
     * @param oArgs.column {YAHOO.widget.Column} The highlighted Column.
15446
     */
15447
 
15448
    /**
15449
     * Fired when a column is unhighlighted.
15450
     *
15451
     * @event columnUnhighlightEvent
15452
     * @param oArgs.column {YAHOO.widget.Column} The unhighlighted Column.
15453
     */
15454
 
15455
 
15456
    /**
15457
     * Fired when a row has a mouseover.
15458
     *
15459
     * @event rowMouseoverEvent
15460
     * @param oArgs.event {HTMLEvent} The event object.
15461
     * @param oArgs.target {HTMLElement} The TR element.
15462
     */
15463
 
15464
    /**
15465
     * Fired when a row has a mouseout.
15466
     *
15467
     * @event rowMouseoutEvent
15468
     * @param oArgs.event {HTMLEvent} The event object.
15469
     * @param oArgs.target {HTMLElement} The TR element.
15470
     */
15471
 
15472
    /**
15473
     * Fired when a row has a mousedown.
15474
     *
15475
     * @event rowMousedownEvent
15476
     * @param oArgs.event {HTMLEvent} The event object.
15477
     * @param oArgs.target {HTMLElement} The TR element.
15478
     */
15479
 
15480
    /**
15481
     * Fired when a row has a mouseup.
15482
     *
15483
     * @event rowMouseupEvent
15484
     * @param oArgs.event {HTMLEvent} The event object.
15485
     * @param oArgs.target {HTMLElement} The TR element.
15486
     */
15487
 
15488
    /**
15489
     * Fired when a row has a click.
15490
     *
15491
     * @event rowClickEvent
15492
     * @param oArgs.event {HTMLEvent} The event object.
15493
     * @param oArgs.target {HTMLElement} The TR element.
15494
     */
15495
 
15496
    /**
15497
     * Fired when a row has a dblclick.
15498
     *
15499
     * @event rowDblclickEvent
15500
     * @param oArgs.event {HTMLEvent} The event object.
15501
     * @param oArgs.target {HTMLElement} The TR element.
15502
     */
15503
 
15504
    /**
15505
     * Fired when a row is added.
15506
     *
15507
     * @event rowAddEvent
15508
     * @param oArgs.record {YAHOO.widget.Record} The added Record.
15509
     */
15510
 
15511
    /**
15512
     * Fired when rows are added.
15513
     *
15514
     * @event rowsAddEvent
15515
     * @param oArgs.record {YAHOO.widget.Record[]} The added Records.
15516
     */
15517
 
15518
    /**
15519
     * Fired when a row is updated.
15520
     *
15521
     * @event rowUpdateEvent
15522
     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
15523
     * @param oArgs.oldData {Object} Object literal of the old data.
15524
     */
15525
 
15526
    /**
15527
     * Fired when a row is deleted.
15528
     *
15529
     * @event rowDeleteEvent
15530
     * @param oArgs.oldData {Object} Object literal of the deleted data.
15531
     * @param oArgs.recordIndex {Number} Index of the deleted Record.
15532
     * @param oArgs.trElIndex {Number} Index of the deleted TR element, if on current page.
15533
     */
15534
 
15535
    /**
15536
     * Fired when rows are deleted.
15537
     *
15538
     * @event rowsDeleteEvent
15539
     * @param oArgs.oldData {Object[]} Array of object literals of the deleted data.
15540
     * @param oArgs.recordIndex {Number} Index of the first deleted Record.
15541
     * @param oArgs.count {Number} Number of deleted Records.
15542
     */
15543
 
15544
    /**
15545
     * Fired when a row is selected.
15546
     *
15547
     * @event rowSelectEvent
15548
     * @param oArgs.el {HTMLElement} The selected TR element, if applicable.
15549
     * @param oArgs.record {YAHOO.widget.Record} The selected Record.
15550
     */
15551
 
15552
    /**
15553
     * Fired when a row is unselected.
15554
     *
15555
     * @event rowUnselectEvent
15556
     * @param oArgs.el {HTMLElement} The unselected TR element, if applicable.
15557
     * @param oArgs.record {YAHOO.widget.Record} The unselected Record.
15558
     */
15559
 
15560
    /**
15561
     * Fired when all row selections are cleared.
15562
     *
15563
     * @event unselectAllRowsEvent
15564
     */
15565
 
15566
    /**
15567
     * Fired when a row is highlighted.
15568
     *
15569
     * @event rowHighlightEvent
15570
     * @param oArgs.el {HTMLElement} The highlighted TR element.
15571
     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
15572
     */
15573
 
15574
    /**
15575
     * Fired when a row is unhighlighted.
15576
     *
15577
     * @event rowUnhighlightEvent
15578
     * @param oArgs.el {HTMLElement} The highlighted TR element.
15579
     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
15580
     */
15581
 
15582
    /**
15583
     * Fired when a cell is updated.
15584
     *
15585
     * @event cellUpdateEvent
15586
     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
15587
     * @param oArgs.column {YAHOO.widget.Column} The updated Column.
15588
     * @param oArgs.oldData {Object} Original data value of the updated cell.
15589
     */
15590
 
15591
    /**
15592
     * Fired when a cell has a mouseover.
15593
     *
15594
     * @event cellMouseoverEvent
15595
     * @param oArgs.event {HTMLEvent} The event object.
15596
     * @param oArgs.target {HTMLElement} The TD element.
15597
     */
15598
 
15599
    /**
15600
     * Fired when a cell has a mouseout.
15601
     *
15602
     * @event cellMouseoutEvent
15603
     * @param oArgs.event {HTMLEvent} The event object.
15604
     * @param oArgs.target {HTMLElement} The TD element.
15605
     */
15606
 
15607
    /**
15608
     * Fired when a cell has a mousedown.
15609
     *
15610
     * @event cellMousedownEvent
15611
     * @param oArgs.event {HTMLEvent} The event object.
15612
     * @param oArgs.target {HTMLElement} The TD element.
15613
     */
15614
 
15615
    /**
15616
     * Fired when a cell has a mouseup.
15617
     *
15618
     * @event cellMouseupEvent
15619
     * @param oArgs.event {HTMLEvent} The event object.
15620
     * @param oArgs.target {HTMLElement} The TD element.
15621
     */
15622
 
15623
    /**
15624
     * Fired when a cell has a click.
15625
     *
15626
     * @event cellClickEvent
15627
     * @param oArgs.event {HTMLEvent} The event object.
15628
     * @param oArgs.target {HTMLElement} The TD element.
15629
     */
15630
 
15631
    /**
15632
     * Fired when a cell has a dblclick.
15633
     *
15634
     * @event cellDblclickEvent
15635
     * @param oArgs.event {HTMLEvent} The event object.
15636
     * @param oArgs.target {HTMLElement} The TD element.
15637
     */
15638
 
15639
    /**
15640
     * Fired when a cell is formatted.
15641
     *
15642
     * @event cellFormatEvent
15643
     * @param oArgs.el {HTMLElement} The formatted TD element.
15644
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15645
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15646
     * @param oArgs.key {String} (deprecated) The key of the formatted cell.
15647
     */
15648
 
15649
    /**
15650
     * Fired when a cell is selected.
15651
     *
15652
     * @event cellSelectEvent
15653
     * @param oArgs.el {HTMLElement} The selected TD element.
15654
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15655
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15656
     * @param oArgs.key {String} (deprecated) The key of the selected cell.
15657
     */
15658
 
15659
    /**
15660
     * Fired when a cell is unselected.
15661
     *
15662
     * @event cellUnselectEvent
15663
     * @param oArgs.el {HTMLElement} The unselected TD element.
15664
     * @param oArgs.record {YAHOO.widget.Record} The associated Record.
15665
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15666
     * @param oArgs.key {String} (deprecated) The key of the unselected cell.
15667
 
15668
     */
15669
 
15670
    /**
15671
     * Fired when a cell is highlighted.
15672
     *
15673
     * @event cellHighlightEvent
15674
     * @param oArgs.el {HTMLElement} The highlighted TD element.
15675
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15676
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15677
     * @param oArgs.key {String} (deprecated) The key of the highlighted cell.
15678
 
15679
     */
15680
 
15681
    /**
15682
     * Fired when a cell is unhighlighted.
15683
     *
15684
     * @event cellUnhighlightEvent
15685
     * @param oArgs.el {HTMLElement} The unhighlighted TD element.
15686
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15687
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15688
     * @param oArgs.key {String} (deprecated) The key of the unhighlighted cell.
15689
 
15690
     */
15691
 
15692
    /**
15693
     * Fired when all cell selections are cleared.
15694
     *
15695
     * @event unselectAllCellsEvent
15696
     */
15697
 
15698
    /**
15699
     * Fired when a CellEditor is shown.
15700
     *
15701
     * @event editorShowEvent
15702
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15703
     */
15704
 
15705
    /**
15706
     * Fired when a CellEditor has a keydown.
15707
     *
15708
     * @event editorKeydownEvent
15709
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15710
     * @param oArgs.event {HTMLEvent} The event object.
15711
     */
15712
 
15713
    /**
15714
     * Fired when a CellEditor input is reverted.
15715
     *
15716
     * @event editorRevertEvent
15717
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15718
     * @param oArgs.newData {Object} New data value from form input field.
15719
     * @param oArgs.oldData {Object} Old data value.
15720
     */
15721
 
15722
    /**
15723
     * Fired when a CellEditor input is saved.
15724
     *
15725
     * @event editorSaveEvent
15726
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15727
     * @param oArgs.newData {Object} New data value from form input field.
15728
     * @param oArgs.oldData {Object} Old data value.
15729
     */
15730
 
15731
    /**
15732
     * Fired when a CellEditor input is canceled.
15733
     *
15734
     * @event editorCancelEvent
15735
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15736
     */
15737
 
15738
    /**
15739
     * Fired when a CellEditor has a blur event.
15740
     *
15741
     * @event editorBlurEvent
15742
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15743
     */
15744
 
15745
    /**
15746
     * Fired when a CellEditor is blocked.
15747
     *
15748
     * @event editorBlockEvent
15749
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15750
     */
15751
 
15752
    /**
15753
     * Fired when a CellEditor is unblocked.
15754
     *
15755
     * @event editorUnblockEvent
15756
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15757
     */
15758
 
15759
 
15760
 
15761
 
15762
 
15763
    /**
15764
     * Fired when a link is clicked.
15765
     *
15766
     * @event linkClickEvent
15767
     * @param oArgs.event {HTMLEvent} The event object.
15768
     * @param oArgs.target {HTMLElement} The A element.
15769
     */
15770
 
15771
    /**
15772
     * Fired when a BUTTON element or INPUT element of type "button", "image",
15773
     * "submit", "reset" is clicked.
15774
     *
15775
     * @event buttonClickEvent
15776
     * @param oArgs.event {HTMLEvent} The event object.
15777
     * @param oArgs.target {HTMLElement} The BUTTON element.
15778
     */
15779
 
15780
    /**
15781
     * Fired when a CHECKBOX element is clicked.
15782
     *
15783
     * @event checkboxClickEvent
15784
     * @param oArgs.event {HTMLEvent} The event object.
15785
     * @param oArgs.target {HTMLElement} The CHECKBOX element.
15786
     */
15787
 
15788
    /**
15789
     * Fired when a SELECT element is changed.
15790
     *
15791
     * @event dropdownChangeEvent
15792
     * @param oArgs.event {HTMLEvent} The event object.
15793
     * @param oArgs.target {HTMLElement} The SELECT element.
15794
     */
15795
 
15796
    /**
15797
     * Fired when a RADIO element is clicked.
15798
     *
15799
     * @event radioClickEvent
15800
     * @param oArgs.event {HTMLEvent} The event object.
15801
     * @param oArgs.target {HTMLElement} The RADIO element.
15802
     */
15803
 
15804
 
15805
 
15806
 
15807
 
15808
 
15809
 
15810
 
15811
 
15812
 
15813
 
15814
 
15815
 
15816
 
15817
 
15818
 
15819
 
15820
 
15821
 
15822
 
15823
 
15824
 
15825
 
15826
 
15827
 
15828
 
15829
/////////////////////////////////////////////////////////////////////////////
15830
//
15831
// Deprecated APIs
15832
//
15833
/////////////////////////////////////////////////////////////////////////////
15834
 
15835
/*
15836
 * @method showCellEditorBtns
15837
 * @deprecated Use CellEditor.renderBtns()
15838
 */
15839
showCellEditorBtns : function(elContainer) {
15840
    // Buttons
15841
    var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
15842
    Dom.addClass(elBtnsDiv, DT.CLASS_BUTTON);
15843
 
15844
    // Save button
15845
    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
15846
    Dom.addClass(elSaveBtn, DT.CLASS_DEFAULT);
15847
    elSaveBtn.innerHTML = "OK";
15848
    Ev.addListener(elSaveBtn, "click", function(oArgs, oSelf) {
15849
        oSelf.onEventSaveCellEditor(oArgs, oSelf);
15850
        oSelf.focusTbodyEl();
15851
    }, this, true);
15852
 
15853
    // Cancel button
15854
    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
15855
    elCancelBtn.innerHTML = "Cancel";
15856
    Ev.addListener(elCancelBtn, "click", function(oArgs, oSelf) {
15857
        oSelf.onEventCancelCellEditor(oArgs, oSelf);
15858
        oSelf.focusTbodyEl();
15859
    }, this, true);
15860
 
15861
    YAHOO.log("The method showCellEditorBtns() has been deprecated." +
15862
            " Please use the CellEditor class.", "warn", this.toString());
15863
},
15864
 
15865
/**
15866
 * @method resetCellEditor
15867
 * @deprecated Use destroyCellEditor
15868
 */
15869
resetCellEditor : function() {
15870
    var elContainer = this._oCellEditor.container;
15871
    elContainer.style.display = "none";
15872
    Ev.purgeElement(elContainer, true);
15873
    elContainer.innerHTML = "";
15874
    this._oCellEditor.value = null;
15875
    this._oCellEditor.isActive = false;
15876
 
15877
    YAHOO.log("The method resetCellEditor() has been deprecated." +
15878
            " Please use the CellEditor class.", "warn", this.toString());
15879
},
15880
 
15881
/**
15882
 * @event editorUpdateEvent
15883
 * @deprecated Use CellEditor class.
15884
 */
15885
 
15886
/**
15887
 * @method getBody
15888
 * @deprecated Use getTbodyEl().
15889
 */
15890
getBody : function() {
15891
    // Backward compatibility
15892
    YAHOO.log("The method getBody() has been deprecated" +
15893
            " in favor of getTbodyEl()", "warn", this.toString());
15894
    return this.getTbodyEl();
15895
},
15896
 
15897
/**
15898
 * @method getCell
15899
 * @deprecated Use getTdEl().
15900
 */
15901
getCell : function(index) {
15902
    // Backward compatibility
15903
    YAHOO.log("The method getCell() has been deprecated" +
15904
            " in favor of getTdEl()", "warn", this.toString());
15905
    return this.getTdEl(index);
15906
},
15907
 
15908
/**
15909
 * @method getRow
15910
 * @deprecated Use getTrEl().
15911
 */
15912
getRow : function(index) {
15913
    // Backward compatibility
15914
    YAHOO.log("The method getRow() has been deprecated" +
15915
            " in favor of getTrEl()", "warn", this.toString());
15916
    return this.getTrEl(index);
15917
},
15918
 
15919
/**
15920
 * @method refreshView
15921
 * @deprecated Use render.
15922
 */
15923
refreshView : function() {
15924
    // Backward compatibility
15925
    YAHOO.log("The method refreshView() has been deprecated" +
15926
            " in favor of render()", "warn", this.toString());
15927
    this.render();
15928
},
15929
 
15930
/**
15931
 * @method select
15932
 * @deprecated Use selectRow.
15933
 */
15934
select : function(els) {
15935
    // Backward compatibility
15936
    YAHOO.log("The method select() has been deprecated" +
15937
            " in favor of selectRow()", "warn", this.toString());
15938
    if(!lang.isArray(els)) {
15939
        els = [els];
15940
    }
15941
    for(var i=0; i<els.length; i++) {
15942
        this.selectRow(els[i]);
15943
    }
15944
},
15945
 
15946
/**
15947
 * @method onEventEditCell
15948
 * @deprecated Use onEventShowCellEditor.
15949
 */
15950
onEventEditCell : function(oArgs) {
15951
    // Backward compatibility
15952
    YAHOO.log("The method onEventEditCell() has been deprecated" +
15953
        " in favor of onEventShowCellEditor()", "warn", this.toString());
15954
    this.onEventShowCellEditor(oArgs);
15955
},
15956
 
15957
/**
15958
 * @method _syncColWidths
15959
 * @deprecated Use validateColumnWidths.
15960
 */
15961
_syncColWidths : function() {
15962
    // Backward compatibility
15963
    YAHOO.log("The method _syncColWidths() has been deprecated" +
15964
        " in favor of validateColumnWidths()", "warn", this.toString());
15965
    this.validateColumnWidths();
15966
}
15967
 
15968
/**
15969
 * @event headerRowMouseoverEvent
15970
 * @deprecated Use theadRowMouseoverEvent.
15971
 */
15972
 
15973
/**
15974
 * @event headerRowMouseoutEvent
15975
 * @deprecated Use theadRowMouseoutEvent.
15976
 */
15977
 
15978
/**
15979
 * @event headerRowMousedownEvent
15980
 * @deprecated Use theadRowMousedownEvent.
15981
 */
15982
 
15983
/**
15984
 * @event headerRowClickEvent
15985
 * @deprecated Use theadRowClickEvent.
15986
 */
15987
 
15988
/**
15989
 * @event headerRowDblclickEvent
15990
 * @deprecated Use theadRowDblclickEvent.
15991
 */
15992
 
15993
/**
15994
 * @event headerCellMouseoverEvent
15995
 * @deprecated Use theadCellMouseoverEvent.
15996
 */
15997
 
15998
/**
15999
 * @event headerCellMouseoutEvent
16000
 * @deprecated Use theadCellMouseoutEvent.
16001
 */
16002
 
16003
/**
16004
 * @event headerCellMousedownEvent
16005
 * @deprecated Use theadCellMousedownEvent.
16006
 */
16007
 
16008
/**
16009
 * @event headerCellClickEvent
16010
 * @deprecated Use theadCellClickEvent.
16011
 */
16012
 
16013
/**
16014
 * @event headerCellDblclickEvent
16015
 * @deprecated Use theadCellDblclickEvent.
16016
 */
16017
 
16018
/**
16019
 * @event headerLabelMouseoverEvent
16020
 * @deprecated Use theadLabelMouseoverEvent.
16021
 */
16022
 
16023
/**
16024
 * @event headerLabelMouseoutEvent
16025
 * @deprecated Use theadLabelMouseoutEvent.
16026
 */
16027
 
16028
/**
16029
 * @event headerLabelMousedownEvent
16030
 * @deprecated Use theadLabelMousedownEvent.
16031
 */
16032
 
16033
/**
16034
 * @event headerLabelClickEvent
16035
 * @deprecated Use theadLabelClickEvent.
16036
 */
16037
 
16038
/**
16039
 * @event headerLabelDbllickEvent
16040
 * @deprecated Use theadLabelDblclickEvent.
16041
 */
16042
 
16043
});
16044
 
16045
/**
16046
 * Alias for onDataReturnSetRows for backward compatibility
16047
 * @method onDataReturnSetRecords
16048
 * @deprecated Use onDataReturnSetRows
16049
 */
16050
DT.prototype.onDataReturnSetRecords = DT.prototype.onDataReturnSetRows;
16051
 
16052
/**
16053
 * Alias for onPaginatorChange for backward compatibility
16054
 * @method onPaginatorChange
16055
 * @deprecated Use onPaginatorChangeRequest
16056
 */
16057
DT.prototype.onPaginatorChange = DT.prototype.onPaginatorChangeRequest;
16058
 
16059
/////////////////////////////////////////////////////////////////////////////
16060
//
16061
// Deprecated static APIs
16062
//
16063
/////////////////////////////////////////////////////////////////////////////
16064
/**
16065
 * @method DataTable.editCheckbox
16066
 * @deprecated  Use YAHOO.widget.CheckboxCellEditor.
16067
 */
16068
DT.editCheckbox = function() {};
16069
 
16070
/**
16071
 * @method DataTable.editDate
16072
 * @deprecated Use YAHOO.widget.DateCellEditor.
16073
 */
16074
DT.editDate = function() {};
16075
 
16076
/**
16077
 * @method DataTable.editDropdown
16078
 * @deprecated Use YAHOO.widget.DropdownCellEditor.
16079
 */
16080
DT.editDropdown = function() {};
16081
 
16082
/**
16083
 * @method DataTable.editRadio
16084
 * @deprecated Use YAHOO.widget.RadioCellEditor.
16085
 */
16086
DT.editRadio = function() {};
16087
 
16088
/**
16089
 * @method DataTable.editTextarea
16090
 * @deprecated Use YAHOO.widget.TextareaCellEditor
16091
 */
16092
DT.editTextarea = function() {};
16093
 
16094
/**
16095
 * @method DataTable.editTextbox
16096
 * @deprecated Use YAHOO.widget.TextboxCellEditor
16097
 */
16098
DT.editTextbox= function() {};
16099
 
16100
})();
16101
 
16102
(function () {
16103
 
16104
var lang   = YAHOO.lang,
16105
    util   = YAHOO.util,
16106
    widget = YAHOO.widget,
16107
    ua     = YAHOO.env.ua,
16108
 
16109
    Dom    = util.Dom,
16110
    Ev     = util.Event,
16111
    DS     = util.DataSourceBase,
16112
    DT     = widget.DataTable,
16113
    Pag    = widget.Paginator;
16114
 
16115
/**
16116
 * The ScrollingDataTable class extends the DataTable class to provide
16117
 * functionality for x-scrolling, y-scrolling, and xy-scrolling.
16118
 *
16119
 * @namespace YAHOO.widget
16120
 * @class ScrollingDataTable
16121
 * @extends YAHOO.widget.DataTable
16122
 * @constructor
16123
 * @param elContainer {HTMLElement} Container element for the TABLE.
16124
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
16125
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
16126
 * @param oConfigs {object} (optional) Object literal of configuration values.
16127
 */
16128
widget.ScrollingDataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
16129
    oConfigs = oConfigs || {};
16130
 
16131
    // Prevent infinite loop
16132
    if(oConfigs.scrollable) {
16133
        oConfigs.scrollable = false;
16134
    }
16135
 
16136
    this._init();
16137
 
16138
    widget.ScrollingDataTable.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs);
16139
 
16140
    // Once per instance
16141
    this.subscribe("columnShowEvent", this._onColumnChange);
16142
};
16143
 
16144
var SDT = widget.ScrollingDataTable;
16145
 
16146
/////////////////////////////////////////////////////////////////////////////
16147
//
16148
// Public constants
16149
//
16150
/////////////////////////////////////////////////////////////////////////////
16151
lang.augmentObject(SDT, {
16152
 
16153
    /**
16154
     * Class name assigned to inner DataTable header container.
16155
     *
16156
     * @property DataTable.CLASS_HEADER
16157
     * @type String
16158
     * @static
16159
     * @final
16160
     * @default "yui-dt-hd"
16161
     */
16162
    CLASS_HEADER : "yui-dt-hd",
16163
 
16164
    /**
16165
     * Class name assigned to inner DataTable body container.
16166
     *
16167
     * @property DataTable.CLASS_BODY
16168
     * @type String
16169
     * @static
16170
     * @final
16171
     * @default "yui-dt-bd"
16172
     */
16173
    CLASS_BODY : "yui-dt-bd"
16174
});
16175
 
16176
lang.extend(SDT, DT, {
16177
 
16178
/**
16179
 * Container for fixed header TABLE element.
16180
 *
16181
 * @property _elHdContainer
16182
 * @type HTMLElement
16183
 * @private
16184
 */
16185
_elHdContainer : null,
16186
 
16187
/**
16188
 * Fixed header TABLE element.
16189
 *
16190
 * @property _elHdTable
16191
 * @type HTMLElement
16192
 * @private
16193
 */
16194
_elHdTable : null,
16195
 
16196
/**
16197
 * Container for scrolling body TABLE element.
16198
 *
16199
 * @property _elBdContainer
16200
 * @type HTMLElement
16201
 * @private
16202
 */
16203
_elBdContainer : null,
16204
 
16205
/**
16206
 * Body THEAD element.
16207
 *
16208
 * @property _elBdThead
16209
 * @type HTMLElement
16210
 * @private
16211
 */
16212
_elBdThead : null,
16213
 
16214
/**
16215
 * Offscreen container to temporarily clone SDT for auto-width calculation.
16216
 *
16217
 * @property _elTmpContainer
16218
 * @type HTMLElement
16219
 * @private
16220
 */
16221
_elTmpContainer : null,
16222
 
16223
/**
16224
 * Offscreen TABLE element for auto-width calculation.
16225
 *
16226
 * @property _elTmpTable
16227
 * @type HTMLElement
16228
 * @private
16229
 */
16230
_elTmpTable : null,
16231
 
16232
/**
16233
 * True if x-scrollbar is currently visible.
16234
 * @property _bScrollbarX
16235
 * @type Boolean
16236
 * @private
16237
 */
16238
_bScrollbarX : null,
16239
 
16240
 
16241
 
16242
 
16243
 
16244
 
16245
 
16246
 
16247
 
16248
 
16249
 
16250
 
16251
 
16252
 
16253
 
16254
/////////////////////////////////////////////////////////////////////////////
16255
//
16256
// Superclass methods
16257
//
16258
/////////////////////////////////////////////////////////////////////////////
16259
 
16260
/**
16261
 * Implementation of Element's abstract method. Sets up config values.
16262
 *
16263
 * @method initAttributes
16264
 * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
16265
 * @private
16266
 */
16267
 
16268
initAttributes : function(oConfigs) {
16269
    oConfigs = oConfigs || {};
16270
    SDT.superclass.initAttributes.call(this, oConfigs);
16271
 
16272
    /**
16273
    * @attribute width
16274
    * @description Table width for scrollable tables (e.g., "40em").
16275
    * @type String
16276
    */
16277
    this.setAttributeConfig("width", {
16278
        value: null,
16279
        validator: lang.isString,
16280
        method: function(oParam) {
16281
            if(this._elHdContainer && this._elBdContainer) {
16282
                this._elHdContainer.style.width = oParam;
16283
                this._elBdContainer.style.width = oParam;
16284
                this._syncScrollX();
16285
                this._syncScrollOverhang();
16286
            }
16287
        }
16288
    });
16289
 
16290
    /**
16291
    * @attribute height
16292
    * @description Table body height for scrollable tables, not including headers (e.g., "40em").
16293
    * @type String
16294
    */
16295
    this.setAttributeConfig("height", {
16296
        value: null,
16297
        validator: lang.isString,
16298
        method: function(oParam) {
16299
            if(this._elHdContainer && this._elBdContainer) {
16300
                this._elBdContainer.style.height = oParam;
16301
                this._syncScrollX();
16302
                this._syncScrollY();
16303
                this._syncScrollOverhang();
16304
            }
16305
        }
16306
    });
16307
 
16308
    /**
16309
    * @attribute COLOR_COLUMNFILLER
16310
    * @description CSS color value assigned to header filler on scrollable tables.
16311
    * @type String
16312
    * @default "#F2F2F2"
16313
    */
16314
    this.setAttributeConfig("COLOR_COLUMNFILLER", {
16315
        value: "#F2F2F2",
16316
        validator: lang.isString,
16317
        method: function(oParam) {
16318
            if(this._elHdContainer) {
16319
                this._elHdContainer.style.backgroundColor = oParam;
16320
            }
16321
        }
16322
    });
16323
},
16324
 
16325
/**
16326
 * Initializes internal variables.
16327
 *
16328
 * @method _init
16329
 * @private
16330
 */
16331
_init : function() {
16332
    this._elHdContainer = null;
16333
    this._elHdTable = null;
16334
    this._elBdContainer = null;
16335
    this._elBdThead = null;
16336
    this._elTmpContainer = null;
16337
    this._elTmpTable = null;
16338
},
16339
 
16340
/**
16341
 * Initializes DOM elements for a ScrollingDataTable, including creation of
16342
 * two separate TABLE elements.
16343
 *
16344
 * @method _initDomElements
16345
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
16346
 * return {Boolean} False in case of error, otherwise true
16347
 * @private
16348
 */
16349
_initDomElements : function(elContainer) {
16350
    // Outer and inner containers
16351
    this._initContainerEl(elContainer);
16352
    if(this._elContainer && this._elHdContainer && this._elBdContainer) {
16353
        // TABLEs
16354
        this._initTableEl();
16355
 
16356
        if(this._elHdTable && this._elTable) {
16357
            // COLGROUPs
16358
            ///this._initColgroupEl(this._elHdTable, this._elTable);
16359
            this._initColgroupEl(this._elHdTable);
16360
 
16361
            // THEADs
16362
            this._initTheadEl(this._elHdTable, this._elTable);
16363
 
16364
            // Primary TBODY
16365
            this._initTbodyEl(this._elTable);
16366
            // Message TBODY
16367
            this._initMsgTbodyEl(this._elTable);
16368
        }
16369
    }
16370
    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody ||
16371
            !this._elHdTable || !this._elBdThead) {
16372
        YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
16373
        return false;
16374
    }
16375
    else {
16376
        return true;
16377
    }
16378
},
16379
 
16380
/**
16381
 * Destroy's the DataTable outer and inner container elements, if available.
16382
 *
16383
 * @method _destroyContainerEl
16384
 * @param elContainer {HTMLElement} Reference to the container element.
16385
 * @private
16386
 */
16387
_destroyContainerEl : function(elContainer) {
16388
    Dom.removeClass(elContainer, DT.CLASS_SCROLLABLE);
16389
    SDT.superclass._destroyContainerEl.call(this, elContainer);
16390
    this._elHdContainer = null;
16391
    this._elBdContainer = null;
16392
},
16393
 
16394
/**
16395
 * Initializes the DataTable outer container element and creates inner header
16396
 * and body container elements.
16397
 *
16398
 * @method _initContainerEl
16399
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
16400
 * @private
16401
 */
16402
_initContainerEl : function(elContainer) {
16403
    SDT.superclass._initContainerEl.call(this, elContainer);
16404
 
16405
    if(this._elContainer) {
16406
        elContainer = this._elContainer; // was constructor input, now is DOM ref
16407
        Dom.addClass(elContainer, DT.CLASS_SCROLLABLE);
16408
 
16409
        // Container for header TABLE
16410
        var elHdContainer = document.createElement("div");
16411
        elHdContainer.style.width = this.get("width") || "";
16412
        elHdContainer.style.backgroundColor = this.get("COLOR_COLUMNFILLER");
16413
        Dom.addClass(elHdContainer, SDT.CLASS_HEADER);
16414
        this._elHdContainer = elHdContainer;
16415
        elContainer.appendChild(elHdContainer);
16416
 
16417
        // Container for body TABLE
16418
        var elBdContainer = document.createElement("div");
16419
        elBdContainer.style.width = this.get("width") || "";
16420
        elBdContainer.style.height = this.get("height") || "";
16421
        Dom.addClass(elBdContainer, SDT.CLASS_BODY);
16422
        Ev.addListener(elBdContainer, "scroll", this._onScroll, this); // to sync horiz scroll headers
16423
        this._elBdContainer = elBdContainer;
16424
        elContainer.appendChild(elBdContainer);
16425
    }
16426
},
16427
 
16428
/**
16429
 * Creates HTML markup CAPTION element.
16430
 *
16431
 * @method _initCaptionEl
16432
 * @param sCaption {String} Text for caption.
16433
 * @private
16434
 */
16435
_initCaptionEl : function(sCaption) {
16436
    // Not yet supported
16437
    /*if(this._elHdTable && sCaption) {
16438
        // Create CAPTION element
16439
        if(!this._elCaption) {
16440
            this._elCaption = this._elHdTable.createCaption();
16441
        }
16442
        // Set CAPTION value
16443
        this._elCaption.innerHTML = sCaption;
16444
    }
16445
    else if(this._elCaption) {
16446
        this._elCaption.parentNode.removeChild(this._elCaption);
16447
    }*/
16448
},
16449
 
16450
/**
16451
 * Destroy's the DataTable head TABLE element, if available.
16452
 *
16453
 * @method _destroyHdTableEl
16454
 * @private
16455
 */
16456
_destroyHdTableEl : function() {
16457
    var elTable = this._elHdTable;
16458
    if(elTable) {
16459
        Ev.purgeElement(elTable, true);
16460
        elTable.parentNode.removeChild(elTable);
16461
 
16462
        // A little out of place, but where else can we null out these extra elements?
16463
        ///this._elBdColgroup = null;
16464
        this._elBdThead = null;
16465
    }
16466
},
16467
 
16468
/**
16469
 * Initializes ScrollingDataTable TABLE elements into the two inner containers.
16470
 *
16471
 * @method _initTableEl
16472
 * @private
16473
 */
16474
_initTableEl : function() {
16475
    // Head TABLE
16476
    if(this._elHdContainer) {
16477
        this._destroyHdTableEl();
16478
 
16479
        // Create TABLE
16480
        this._elHdTable = this._elHdContainer.appendChild(document.createElement("table"));
16481
 
16482
        // Set up mouseover/mouseout events via mouseenter/mouseleave delegation
16483
        Ev.delegate(this._elHdTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this);
16484
        Ev.delegate(this._elHdTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this);
16485
    }
16486
    // Body TABLE
16487
    SDT.superclass._initTableEl.call(this, this._elBdContainer);
16488
},
16489
 
16490
/**
16491
 * Initializes ScrollingDataTable THEAD elements into the two inner containers.
16492
 *
16493
 * @method _initTheadEl
16494
 * @param elHdTable {HTMLElement} (optional) Fixed header TABLE element reference.
16495
 * @param elTable {HTMLElement} (optional) TABLE element reference.
16496
 * @private
16497
 */
16498
_initTheadEl : function(elHdTable, elTable) {
16499
    elHdTable = elHdTable || this._elHdTable;
16500
    elTable = elTable || this._elTable;
16501
 
16502
    // Scrolling body's THEAD
16503
    this._initBdTheadEl(elTable);
16504
    // Standard fixed head THEAD
16505
    SDT.superclass._initTheadEl.call(this, elHdTable);
16506
},
16507
 
16508
/**
16509
 * SDT changes ID so as not to duplicate the accessibility TH IDs.
16510
 *
16511
 * @method _initThEl
16512
 * @param elTh {HTMLElement} TH element reference.
16513
 * @param oColumn {YAHOO.widget.Column} Column object.
16514
 * @private
16515
 */
16516
_initThEl : function(elTh, oColumn) {
16517
    SDT.superclass._initThEl.call(this, elTh, oColumn);
16518
    elTh.id = this.getId() +"-fixedth-" + oColumn.getSanitizedKey(); // Needed for getColumn by TH and ColumnDD
16519
},
16520
 
16521
/**
16522
 * Destroy's the DataTable body THEAD element, if available.
16523
 *
16524
 * @method _destroyBdTheadEl
16525
 * @private
16526
 */
16527
_destroyBdTheadEl : function() {
16528
    var elBdThead = this._elBdThead;
16529
    if(elBdThead) {
16530
        var elTable = elBdThead.parentNode;
16531
        Ev.purgeElement(elBdThead, true);
16532
        elTable.removeChild(elBdThead);
16533
        this._elBdThead = null;
16534
 
16535
        this._destroyColumnHelpers();
16536
    }
16537
},
16538
 
16539
/**
16540
 * Initializes body THEAD element.
16541
 *
16542
 * @method _initBdTheadEl
16543
 * @param elTable {HTMLElement} TABLE element into which to create THEAD.
16544
 * @return {HTMLElement} Initialized THEAD element.
16545
 * @private
16546
 */
16547
_initBdTheadEl : function(elTable) {
16548
    if(elTable) {
16549
        // Destroy previous
16550
        this._destroyBdTheadEl();
16551
 
16552
        var elThead = elTable.insertBefore(document.createElement("thead"), elTable.firstChild);
16553
 
16554
        // Add TRs to the THEAD;
16555
        var oColumnSet = this._oColumnSet,
16556
            colTree = oColumnSet.tree,
16557
            elTh, elTheadTr, oColumn, i, j, k, len;
16558
 
16559
        for(i=0, k=colTree.length; i<k; i++) {
16560
            elTheadTr = elThead.appendChild(document.createElement("tr"));
16561
 
16562
            // ...and create TH cells
16563
            for(j=0, len=colTree[i].length; j<len; j++) {
16564
                oColumn = colTree[i][j];
16565
                elTh = elTheadTr.appendChild(document.createElement("th"));
16566
                this._initBdThEl(elTh,oColumn,i,j);
16567
            }
16568
        }
16569
        this._elBdThead = elThead;
16570
        YAHOO.log("Accessibility TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
16571
    }
16572
},
16573
 
16574
/**
16575
 * Populates TH element for the body THEAD element.
16576
 *
16577
 * @method _initBdThEl
16578
 * @param elTh {HTMLElement} TH element reference.
16579
 * @param oColumn {YAHOO.widget.Column} Column object.
16580
 * @private
16581
 */
16582
_initBdThEl : function(elTh, oColumn) {
16583
    elTh.id = this.getId()+"-th-" + oColumn.getSanitizedKey(); // Needed for accessibility
16584
    elTh.rowSpan = oColumn.getRowspan();
16585
    elTh.colSpan = oColumn.getColspan();
16586
    // Assign abbr attribute
16587
    if(oColumn.abbr) {
16588
        elTh.abbr = oColumn.abbr;
16589
    }
16590
 
16591
    // TODO: strip links and form elements
16592
    var sKey = oColumn.getKey();
16593
    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
16594
    elTh.innerHTML = sLabel;
16595
},
16596
 
16597
/**
16598
 * Initializes ScrollingDataTable TBODY element for data
16599
 *
16600
 * @method _initTbodyEl
16601
 * @param elTable {HTMLElement} TABLE element into which to create TBODY .
16602
 * @private
16603
 */
16604
_initTbodyEl : function(elTable) {
16605
    SDT.superclass._initTbodyEl.call(this, elTable);
16606
 
16607
    // Bug 2105534 - Safari 3 gap
16608
    // Bug 2492591 - IE8 offsetTop
16609
    elTable.style.marginTop = (this._elTbody.offsetTop > 0) ?
16610
            "-"+this._elTbody.offsetTop+"px" : 0;
16611
},
16612
 
16613
 
16614
 
16615
 
16616
 
16617
 
16618
 
16619
 
16620
 
16621
 
16622
 
16623
 
16624
 
16625
 
16626
 
16627
 
16628
 
16629
 
16630
 
16631
 
16632
 
16633
 
16634
 
16635
 
16636
 
16637
 
16638
 
16639
 
16640
 
16641
/**
16642
 * Sets focus on the given element.
16643
 *
16644
 * @method _focusEl
16645
 * @param el {HTMLElement} Element.
16646
 * @private
16647
 */
16648
_focusEl : function(el) {
16649
    el = el || this._elTbody;
16650
    var oSelf = this;
16651
    this._storeScrollPositions();
16652
    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
16653
    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
16654
    // strange unexpected things as the user clicks on buttons and other controls.
16655
 
16656
    // Bug 1921135: Wrap the whole thing in a setTimeout
16657
    setTimeout(function() {
16658
        setTimeout(function() {
16659
            try {
16660
                el.focus();
16661
                oSelf._restoreScrollPositions();
16662
            }
16663
            catch(e) {
16664
            }
16665
        },0);
16666
    }, 0);
16667
},
16668
 
16669
 
16670
 
16671
 
16672
 
16673
 
16674
 
16675
 
16676
 
16677
 
16678
 
16679
 
16680
 
16681
 
16682
 
16683
 
16684
 
16685
 
16686
 
16687
/**
16688
 * Internal wrapper calls run() on render Chain instance.
16689
 *
16690
 * @method _runRenderChain
16691
 * @private
16692
 */
16693
_runRenderChain : function() {
16694
    this._storeScrollPositions();
16695
    this._oChainRender.run();
16696
},
16697
 
16698
/**
16699
 * Stores scroll positions so they can be restored after a render.
16700
 *
16701
 * @method _storeScrollPositions
16702
 * @private
16703
 */
16704
 _storeScrollPositions : function() {
16705
    this._nScrollTop = this._elBdContainer.scrollTop;
16706
    this._nScrollLeft = this._elBdContainer.scrollLeft;
16707
},
16708
 
16709
/**
16710
 * Clears stored scroll positions to interrupt the automatic restore mechanism.
16711
 * Useful for setting scroll positions programmatically rather than as part of
16712
 * the post-render cleanup process.
16713
 *
16714
 * @method clearScrollPositions
16715
 * @private
16716
 */
16717
 clearScrollPositions : function() {
16718
    this._nScrollTop = 0;
16719
    this._nScrollLeft = 0;
16720
},
16721
 
16722
/**
16723
 * Restores scroll positions to stored value.
16724
 *
16725
 * @method _retoreScrollPositions
16726
 * @private
16727
 */
16728
 _restoreScrollPositions : function() {
16729
    // Reset scroll positions
16730
    if(this._nScrollTop) {
16731
        this._elBdContainer.scrollTop = this._nScrollTop;
16732
        this._nScrollTop = null;
16733
    }
16734
    if(this._nScrollLeft) {
16735
        this._elBdContainer.scrollLeft = this._nScrollLeft;
16736
        // Bug 2529024
16737
        this._elHdContainer.scrollLeft = this._nScrollLeft;
16738
        this._nScrollLeft = null;
16739
    }
16740
},
16741
 
16742
/**
16743
 * Helper function calculates and sets a validated width for a Column in a ScrollingDataTable.
16744
 *
16745
 * @method _validateColumnWidth
16746
 * @param oColumn {YAHOO.widget.Column} Column instance.
16747
 * @param elTd {HTMLElement} TD element to validate against.
16748
 * @private
16749
 */
16750
_validateColumnWidth : function(oColumn, elTd) {
16751
    // Only Columns without widths that are not hidden
16752
    if(!oColumn.width && !oColumn.hidden) {
16753
        var elTh = oColumn.getThEl();
16754
        // Unset a calculated auto-width
16755
        if(oColumn._calculatedWidth) {
16756
            this._setColumnWidth(oColumn, "auto", "visible");
16757
        }
16758
        // Compare auto-widths
16759
        if(elTh.offsetWidth !== elTd.offsetWidth) {
16760
            var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
16761
                    oColumn.getThLinerEl() : elTd.firstChild;
16762
 
16763
            // Grab the wider liner width, unless the minWidth is wider
16764
            var newWidth = Math.max(0,
16765
                (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
16766
                oColumn.minWidth);
16767
 
16768
            var sOverflow = 'visible';
16769
 
16770
            // Now validate against maxAutoWidth
16771
            if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
16772
                newWidth = oColumn.maxAutoWidth;
16773
                sOverflow = "hidden";
16774
            }
16775
 
16776
            // Set to the wider auto-width
16777
            this._elTbody.style.display = "none";
16778
            this._setColumnWidth(oColumn, newWidth+'px', sOverflow);
16779
            oColumn._calculatedWidth = newWidth;
16780
            this._elTbody.style.display = "";
16781
        }
16782
    }
16783
},
16784
 
16785
/**
16786
 * For one or all Columns of a ScrollingDataTable, when Column is not hidden,
16787
 * and width is not set, syncs widths of header and body cells and
16788
 * validates that width against minWidth and/or maxAutoWidth as necessary.
16789
 *
16790
 * @method validateColumnWidths
16791
 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
16792
 */
16793
validateColumnWidths : function(oColumn) {
16794
    // Validate there is at least one TR with proper TDs
16795
    var allKeys   = this._oColumnSet.keys,
16796
        allKeysLength = allKeys.length,
16797
        elRow     = this.getFirstTrEl();
16798
 
16799
    // Reset overhang for IE
16800
    if(ua.ie) {
16801
        this._setOverhangValue(1);
16802
    }
16803
 
16804
    if(allKeys && elRow && (elRow.childNodes.length === allKeysLength)) {
16805
        // Temporarily unsnap container since it causes inaccurate calculations
16806
        var sWidth = this.get("width");
16807
        if(sWidth) {
16808
            this._elHdContainer.style.width = "";
16809
            this._elBdContainer.style.width = "";
16810
        }
16811
        this._elContainer.style.width = "";
16812
 
16813
        //Validate just one Column
16814
        if(oColumn && lang.isNumber(oColumn.getKeyIndex())) {
16815
            this._validateColumnWidth(oColumn, elRow.childNodes[oColumn.getKeyIndex()]);
16816
        }
16817
        // Iterate through all Columns to unset calculated widths in one pass
16818
        else {
16819
            var elTd, todos = [], thisTodo, i, len;
16820
            for(i=0; i<allKeysLength; i++) {
16821
                oColumn = allKeys[i];
16822
                // Only Columns without widths that are not hidden, unset a calculated auto-width
16823
                if(!oColumn.width && !oColumn.hidden && oColumn._calculatedWidth) {
16824
                    todos[todos.length] = oColumn;
16825
                }
16826
            }
16827
 
16828
            this._elTbody.style.display = "none";
16829
            for(i=0, len=todos.length; i<len; i++) {
16830
                this._setColumnWidth(todos[i], "auto", "visible");
16831
            }
16832
            this._elTbody.style.display = "";
16833
 
16834
            todos = [];
16835
 
16836
            // Iterate through all Columns and make the store the adjustments to make in one pass
16837
            for(i=0; i<allKeysLength; i++) {
16838
                oColumn = allKeys[i];
16839
                elTd = elRow.childNodes[i];
16840
                // Only Columns without widths that are not hidden
16841
                if(!oColumn.width && !oColumn.hidden) {
16842
                    var elTh = oColumn.getThEl();
16843
 
16844
                    // Compare auto-widths
16845
                    if(elTh.offsetWidth !== elTd.offsetWidth) {
16846
                        var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
16847
                                oColumn.getThLinerEl() : elTd.firstChild;
16848
 
16849
                        // Grab the wider liner width, unless the minWidth is wider
16850
                        var newWidth = Math.max(0,
16851
                            (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
16852
                            oColumn.minWidth);
16853
 
16854
                        var sOverflow = 'visible';
16855
 
16856
                        // Now validate against maxAutoWidth
16857
                        if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
16858
                            newWidth = oColumn.maxAutoWidth;
16859
                            sOverflow = "hidden";
16860
                        }
16861
 
16862
                        todos[todos.length] = [oColumn, newWidth, sOverflow];
16863
                    }
16864
                }
16865
            }
16866
 
16867
            this._elTbody.style.display = "none";
16868
            for(i=0, len=todos.length; i<len; i++) {
16869
                thisTodo = todos[i];
16870
                // Set to the wider auto-width
16871
                this._setColumnWidth(thisTodo[0], thisTodo[1]+"px", thisTodo[2]);
16872
                thisTodo[0]._calculatedWidth = thisTodo[1];
16873
            }
16874
            this._elTbody.style.display = "";
16875
        }
16876
 
16877
        // Resnap unsnapped containers
16878
        if(sWidth) {
16879
            this._elHdContainer.style.width = sWidth;
16880
            this._elBdContainer.style.width = sWidth;
16881
        }
16882
    }
16883
 
16884
    this._syncScroll();
16885
    this._restoreScrollPositions();
16886
},
16887
 
16888
/**
16889
 * Syncs padding around scrollable tables, including Column header right-padding
16890
 * and container width and height.
16891
 *
16892
 * @method _syncScroll
16893
 * @private
16894
 */
16895
_syncScroll : function() {
16896
    this._syncScrollX();
16897
    this._syncScrollY();
16898
    this._syncScrollOverhang();
16899
    if(ua.opera) {
16900
        // Bug 1925874
16901
        this._elHdContainer.scrollLeft = this._elBdContainer.scrollLeft;
16902
        if(!this.get("width")) {
16903
            // Bug 1926125
16904
            document.body.style += '';
16905
        }
16906
    }
16907
 },
16908
 
16909
/**
16910
 * Snaps container width for y-scrolling tables.
16911
 *
16912
 * @method _syncScrollY
16913
 * @private
16914
 */
16915
_syncScrollY : function() {
16916
    var elTbody = this._elTbody,
16917
        elBdContainer = this._elBdContainer;
16918
 
16919
    // X-scrolling not enabled
16920
    if(!this.get("width")) {
16921
        // Snap outer container width to content
16922
        this._elContainer.style.width =
16923
                (elBdContainer.scrollHeight > elBdContainer.clientHeight) ?
16924
                // but account for y-scrollbar since it is visible
16925
                (elTbody.parentNode.clientWidth + 19) + "px" :
16926
                // no y-scrollbar, just borders
16927
                (elTbody.parentNode.clientWidth + 2) + "px";
16928
    }
16929
},
16930
 
16931
/**
16932
 * Snaps container height for x-scrolling tables in IE. Syncs message TBODY width.
16933
 *
16934
 * @method _syncScrollX
16935
 * @private
16936
 */
16937
_syncScrollX : function() {
16938
    var elTbody = this._elTbody,
16939
        elBdContainer = this._elBdContainer;
16940
 
16941
    // IE 6 and 7 only when y-scrolling not enabled
16942
    if(!this.get("height") && (ua.ie)) {
16943
        // Snap outer container height to content
16944
        elBdContainer.style.height =
16945
                // but account for x-scrollbar if it is visible
16946
                (elBdContainer.scrollWidth > elBdContainer.offsetWidth ) ?
16947
                (elTbody.parentNode.offsetHeight + 18) + "px" :
16948
                elTbody.parentNode.offsetHeight + "px";
16949
    }
16950
 
16951
    // Sync message tbody
16952
    if(this._elTbody.rows.length === 0) {
16953
        this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
16954
    }
16955
    else {
16956
        this._elMsgTbody.parentNode.style.width = "";
16957
    }
16958
},
16959
 
16960
/**
16961
 * Adds/removes Column header overhang as necesary.
16962
 *
16963
 * @method _syncScrollOverhang
16964
 * @private
16965
 */
16966
_syncScrollOverhang : function() {
16967
    var elBdContainer = this._elBdContainer,
16968
        // Overhang should be either 1 (default) or 18px, depending on the location of the right edge of the table
16969
        nPadding = 1;
16970
 
16971
    // Y-scrollbar is visible, which is when the overhang needs to jut out
16972
    if((elBdContainer.scrollHeight > elBdContainer.clientHeight) &&
16973
        // X-scrollbar is also visible, which means the right is jagged, not flush with the Column
16974
        (elBdContainer.scrollWidth > elBdContainer.clientWidth)) {
16975
        nPadding = 18;
16976
    }
16977
 
16978
    this._setOverhangValue(nPadding);
16979
 
16980
},
16981
 
16982
/**
16983
 * Sets Column header overhang to given width.
16984
 *
16985
 * @method _setOverhangValue
16986
 * @param nBorderWidth {Number} Value of new border for overhang.
16987
 * @private
16988
 */
16989
_setOverhangValue : function(nBorderWidth) {
16990
    var aLastHeaders = this._oColumnSet.headers[this._oColumnSet.headers.length-1] || [],
16991
        len = aLastHeaders.length,
16992
        sPrefix = this._sId+"-fixedth-",
16993
        sValue = nBorderWidth + "px solid " + this.get("COLOR_COLUMNFILLER");
16994
 
16995
    this._elThead.style.display = "none";
16996
    for(var i=0; i<len; i++) {
16997
        Dom.get(sPrefix+aLastHeaders[i]).style.borderRight = sValue;
16998
    }
16999
    this._elThead.style.display = "";
17000
},
17001
 
17002
 
17003
 
17004
 
17005
 
17006
 
17007
 
17008
 
17009
 
17010
 
17011
 
17012
 
17013
 
17014
 
17015
 
17016
 
17017
 
17018
 
17019
 
17020
 
17021
 
17022
 
17023
 
17024
 
17025
 
17026
 
17027
 
17028
 
17029
 
17030
 
17031
 
17032
 
17033
 
17034
 
17035
 
17036
 
17037
 
17038
 
17039
/**
17040
 * Returns DOM reference to the DataTable's fixed header container element.
17041
 *
17042
 * @method getHdContainerEl
17043
 * @return {HTMLElement} Reference to DIV element.
17044
 */
17045
getHdContainerEl : function() {
17046
    return this._elHdContainer;
17047
},
17048
 
17049
/**
17050
 * Returns DOM reference to the DataTable's scrolling body container element.
17051
 *
17052
 * @method getBdContainerEl
17053
 * @return {HTMLElement} Reference to DIV element.
17054
 */
17055
getBdContainerEl : function() {
17056
    return this._elBdContainer;
17057
},
17058
 
17059
/**
17060
 * Returns DOM reference to the DataTable's fixed header TABLE element.
17061
 *
17062
 * @method getHdTableEl
17063
 * @return {HTMLElement} Reference to TABLE element.
17064
 */
17065
getHdTableEl : function() {
17066
    return this._elHdTable;
17067
},
17068
 
17069
/**
17070
 * Returns DOM reference to the DataTable's scrolling body TABLE element.
17071
 *
17072
 * @method getBdTableEl
17073
 * @return {HTMLElement} Reference to TABLE element.
17074
 */
17075
getBdTableEl : function() {
17076
    return this._elTable;
17077
},
17078
 
17079
/**
17080
 * Disables ScrollingDataTable UI.
17081
 *
17082
 * @method disable
17083
 */
17084
disable : function() {
17085
    var elMask = this._elMask;
17086
    elMask.style.width = this._elBdContainer.offsetWidth + "px";
17087
    elMask.style.height = this._elHdContainer.offsetHeight + this._elBdContainer.offsetHeight + "px";
17088
    elMask.style.display = "";
17089
    this.fireEvent("disableEvent");
17090
},
17091
 
17092
/**
17093
 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
17094
 * non-nested Columns, and top-level parent Columns (which will remove all
17095
 * children Columns).
17096
 *
17097
 * @method removeColumn
17098
 * @param oColumn {YAHOO.widget.Column} Column instance.
17099
 * @return oColumn {YAHOO.widget.Column} Removed Column instance.
17100
 */
17101
removeColumn : function(oColumn) {
17102
    // Store scroll pos
17103
    var hdPos = this._elHdContainer.scrollLeft;
17104
    var bdPos = this._elBdContainer.scrollLeft;
17105
 
17106
    // Call superclass method
17107
    oColumn = SDT.superclass.removeColumn.call(this, oColumn);
17108
 
17109
    // Restore scroll pos
17110
    this._elHdContainer.scrollLeft = hdPos;
17111
    this._elBdContainer.scrollLeft = bdPos;
17112
 
17113
    return oColumn;
17114
},
17115
 
17116
/**
17117
 * Inserts given Column at the index if given, otherwise at the end. NOTE: You
17118
 * can only add non-nested Columns and top-level parent Columns. You cannot add
17119
 * a nested Column to an existing parent.
17120
 *
17121
 * @method insertColumn
17122
 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
17123
 * definition or a Column instance.
17124
 * @param index {Number} (optional) New tree index.
17125
 * @return oColumn {YAHOO.widget.Column} Inserted Column instance.
17126
 */
17127
insertColumn : function(oColumn, index) {
17128
    // Store scroll pos
17129
    var hdPos = this._elHdContainer.scrollLeft;
17130
    var bdPos = this._elBdContainer.scrollLeft;
17131
 
17132
    // Call superclass method
17133
    var oNewColumn = SDT.superclass.insertColumn.call(this, oColumn, index);
17134
 
17135
    // Restore scroll pos
17136
    this._elHdContainer.scrollLeft = hdPos;
17137
    this._elBdContainer.scrollLeft = bdPos;
17138
 
17139
    return oNewColumn;
17140
},
17141
 
17142
/**
17143
 * Removes given Column and inserts into given tree index. NOTE: You
17144
 * can only reorder non-nested Columns and top-level parent Columns. You cannot
17145
 * reorder a nested Column to an existing parent.
17146
 *
17147
 * @method reorderColumn
17148
 * @param oColumn {YAHOO.widget.Column} Column instance.
17149
 * @param index {Number} New tree index.
17150
 */
17151
reorderColumn : function(oColumn, index) {
17152
    // Store scroll pos
17153
    var hdPos = this._elHdContainer.scrollLeft;
17154
    var bdPos = this._elBdContainer.scrollLeft;
17155
 
17156
    // Call superclass method
17157
    var oNewColumn = SDT.superclass.reorderColumn.call(this, oColumn, index);
17158
 
17159
    // Restore scroll pos
17160
    this._elHdContainer.scrollLeft = hdPos;
17161
    this._elBdContainer.scrollLeft = bdPos;
17162
 
17163
    return oNewColumn;
17164
},
17165
 
17166
/**
17167
 * Sets given Column to given pixel width. If new width is less than minWidth
17168
 * width, sets to minWidth. Updates oColumn.width value.
17169
 *
17170
 * @method setColumnWidth
17171
 * @param oColumn {YAHOO.widget.Column} Column instance.
17172
 * @param nWidth {Number} New width in pixels.
17173
 */
17174
setColumnWidth : function(oColumn, nWidth) {
17175
    oColumn = this.getColumn(oColumn);
17176
    if(oColumn) {
17177
        this._storeScrollPositions();
17178
 
17179
        // Validate new width against minWidth
17180
        if(lang.isNumber(nWidth)) {
17181
            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
17182
 
17183
            // Save state
17184
            oColumn.width = nWidth;
17185
 
17186
            // Resize the DOM elements
17187
            this._setColumnWidth(oColumn, nWidth+"px");
17188
            this._syncScroll();
17189
 
17190
            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
17191
            YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
17192
        }
17193
        // Unsets a width to auto-size
17194
        else if(nWidth === null) {
17195
            // Save state
17196
            oColumn.width = nWidth;
17197
 
17198
            // Resize the DOM elements
17199
            this._setColumnWidth(oColumn, "auto");
17200
            this.validateColumnWidths(oColumn);
17201
            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
17202
            YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
17203
        }
17204
 
17205
        // Bug 2339454: resize then sort misaligment
17206
        this._clearTrTemplateEl();
17207
    }
17208
    else {
17209
        YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
17210
    }
17211
},
17212
 
17213
/**
17214
 * Scrolls to given row or cell
17215
 *
17216
 * @method scrollTo
17217
 * @param to {YAHOO.widget.Record | HTMLElement } Itme to scroll to.
17218
 */
17219
scrollTo : function(to) {
17220
        var td = this.getTdEl(to);
17221
        if(td) {
17222
            this.clearScrollPositions();
17223
            this.getBdContainerEl().scrollLeft = td.offsetLeft;
17224
            this.getBdContainerEl().scrollTop = td.parentNode.offsetTop;
17225
        }
17226
        else {
17227
            var tr = this.getTrEl(to);
17228
            if(tr) {
17229
                this.clearScrollPositions();
17230
                this.getBdContainerEl().scrollTop = tr.offsetTop;
17231
            }
17232
        }
17233
},
17234
 
17235
/**
17236
 * Displays message within secondary TBODY.
17237
 *
17238
 * @method showTableMessage
17239
 * @param sHTML {String} (optional) Value for innerHTMlang.
17240
 * @param sClassName {String} (optional) Classname.
17241
 */
17242
showTableMessage : function(sHTML, sClassName) {
17243
    var elCell = this._elMsgTd;
17244
    if(lang.isString(sHTML)) {
17245
        elCell.firstChild.innerHTML = sHTML;
17246
    }
17247
    if(lang.isString(sClassName)) {
17248
        Dom.addClass(elCell.firstChild, sClassName);
17249
    }
17250
 
17251
    // Needed for SDT only
17252
    var elThead = this.getTheadEl();
17253
    var elTable = elThead.parentNode;
17254
    var newWidth = elTable.offsetWidth;
17255
    this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
17256
 
17257
    this._elMsgTbody.style.display = "";
17258
 
17259
    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
17260
    YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
17261
},
17262
 
17263
 
17264
 
17265
 
17266
 
17267
 
17268
 
17269
 
17270
 
17271
 
17272
 
17273
 
17274
 
17275
/////////////////////////////////////////////////////////////////////////////
17276
//
17277
// Private Custom Event Handlers
17278
//
17279
/////////////////////////////////////////////////////////////////////////////
17280
 
17281
/**
17282
 * Handles Column mutations
17283
 *
17284
 * @method onColumnChange
17285
 * @param oArgs {Object} Custom Event data.
17286
 */
17287
_onColumnChange : function(oArg) {
17288
    // Figure out which Column changed
17289
    var oColumn = (oArg.column) ? oArg.column :
17290
            (oArg.editor) ? oArg.editor.column : null;
17291
    this._storeScrollPositions();
17292
    this.validateColumnWidths(oColumn);
17293
},
17294
 
17295
 
17296
 
17297
 
17298
 
17299
 
17300
 
17301
 
17302
 
17303
 
17304
 
17305
 
17306
 
17307
 
17308
 
17309
/////////////////////////////////////////////////////////////////////////////
17310
//
17311
// Private DOM Event Handlers
17312
//
17313
/////////////////////////////////////////////////////////////////////////////
17314
 
17315
/**
17316
 * Syncs scrolltop and scrollleft of all TABLEs.
17317
 *
17318
 * @method _onScroll
17319
 * @param e {HTMLEvent} The scroll event.
17320
 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
17321
 * @private
17322
 */
17323
_onScroll : function(e, oSelf) {
17324
    oSelf._elHdContainer.scrollLeft = oSelf._elBdContainer.scrollLeft;
17325
 
17326
    if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
17327
        oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
17328
        oSelf.cancelCellEditor();
17329
    }
17330
 
17331
    var elTarget = Ev.getTarget(e);
17332
    var elTag = elTarget.nodeName.toLowerCase();
17333
    oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget});
17334
},
17335
 
17336
/**
17337
 * Handles keydown events on the THEAD element.
17338
 *
17339
 * @method _onTheadKeydown
17340
 * @param e {HTMLEvent} The key event.
17341
 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
17342
 * @private
17343
 */
17344
_onTheadKeydown : function(e, oSelf) {
17345
    // If tabbing to next TH label link causes THEAD to scroll,
17346
    // need to sync scrollLeft with TBODY
17347
    if(Ev.getCharCode(e) === 9) {
17348
        setTimeout(function() {
17349
            if((oSelf instanceof SDT) && oSelf._sId) {
17350
                oSelf._elBdContainer.scrollLeft = oSelf._elHdContainer.scrollLeft;
17351
            }
17352
        },0);
17353
    }
17354
 
17355
    var elTarget = Ev.getTarget(e);
17356
    var elTag = elTarget.nodeName.toLowerCase();
17357
    var bKeepBubbling = true;
17358
    while(elTarget && (elTag != "table")) {
17359
        switch(elTag) {
17360
            case "body":
17361
                return;
17362
            case "input":
17363
            case "textarea":
17364
                // TODO: implement textareaKeyEvent
17365
                break;
17366
            case "thead":
17367
                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
17368
                break;
17369
            default:
17370
                break;
17371
        }
17372
        if(bKeepBubbling === false) {
17373
            return;
17374
        }
17375
        else {
17376
            elTarget = elTarget.parentNode;
17377
            if(elTarget) {
17378
                elTag = elTarget.nodeName.toLowerCase();
17379
            }
17380
        }
17381
    }
17382
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
17383
}
17384
 
17385
 
17386
 
17387
 
17388
/**
17389
 * Fired when a fixed scrolling DataTable has a scroll.
17390
 *
17391
 * @event tableScrollEvent
17392
 * @param oArgs.event {HTMLEvent} The event object.
17393
 * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE)
17394
 * or the DataTable's TBODY element (everyone else).
17395
 *
17396
 */
17397
 
17398
 
17399
 
17400
 
17401
});
17402
 
17403
})();
17404
 
17405
(function () {
17406
 
17407
var lang   = YAHOO.lang,
17408
    util   = YAHOO.util,
17409
    widget = YAHOO.widget,
17410
    ua     = YAHOO.env.ua,
17411
 
17412
    Dom    = util.Dom,
17413
    Ev     = util.Event,
17414
 
17415
    DT     = widget.DataTable;
17416
/****************************************************************************/
17417
/****************************************************************************/
17418
/****************************************************************************/
17419
 
17420
/**
17421
 * The BaseCellEditor class provides base functionality common to all inline cell
17422
 * editors for a DataTable widget.
17423
 *
17424
 * @namespace YAHOO.widget
17425
 * @class BaseCellEditor
17426
 * @uses YAHOO.util.EventProvider
17427
 * @constructor
17428
 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
17429
 * @param oConfigs {Object} (Optional) Object literal of configs.
17430
 */
17431
widget.BaseCellEditor = function(sType, oConfigs) {
17432
    this._sId = this._sId || Dom.generateId(null, "yui-ceditor"); // "yui-ceditor" + YAHOO.widget.BaseCellEditor._nCount++;
17433
    YAHOO.widget.BaseCellEditor._nCount++;
17434
    this._sType = sType;
17435
 
17436
    // Validate inputs
17437
    this._initConfigs(oConfigs);
17438
 
17439
    // Create Custom Events
17440
    this._initEvents();
17441
 
17442
    // UI needs to be drawn
17443
    this._needsRender = true;
17444
};
17445
 
17446
var BCE = widget.BaseCellEditor;
17447
 
17448
/////////////////////////////////////////////////////////////////////////////
17449
//
17450
// Static members
17451
//
17452
/////////////////////////////////////////////////////////////////////////////
17453
lang.augmentObject(BCE, {
17454
 
17455
/**
17456
 * Global instance counter.
17457
 *
17458
 * @property CellEditor._nCount
17459
 * @type Number
17460
 * @static
17461
 * @default 0
17462
 * @private
17463
 */
17464
_nCount : 0,
17465
 
17466
/**
17467
 * Class applied to CellEditor container.
17468
 *
17469
 * @property CellEditor.CLASS_CELLEDITOR
17470
 * @type String
17471
 * @static
17472
 * @default "yui-ceditor"
17473
 */
17474
CLASS_CELLEDITOR : "yui-ceditor"
17475
 
17476
});
17477
 
17478
BCE.prototype = {
17479
/////////////////////////////////////////////////////////////////////////////
17480
//
17481
// Private members
17482
//
17483
/////////////////////////////////////////////////////////////////////////////
17484
/**
17485
 * Unique id assigned to instance "yui-ceditorN", useful prefix for generating unique
17486
 * DOM ID strings and log messages.
17487
 *
17488
 * @property _sId
17489
 * @type String
17490
 * @private
17491
 */
17492
_sId : null,
17493
 
17494
/**
17495
 * Editor type.
17496
 *
17497
 * @property _sType
17498
 * @type String
17499
 * @private
17500
 */
17501
_sType : null,
17502
 
17503
/**
17504
 * DataTable instance.
17505
 *
17506
 * @property _oDataTable
17507
 * @type YAHOO.widget.DataTable
17508
 * @private
17509
 */
17510
_oDataTable : null,
17511
 
17512
/**
17513
 * Column instance.
17514
 *
17515
 * @property _oColumn
17516
 * @type YAHOO.widget.Column
17517
 * @default null
17518
 * @private
17519
 */
17520
_oColumn : null,
17521
 
17522
/**
17523
 * Record instance.
17524
 *
17525
 * @property _oRecord
17526
 * @type YAHOO.widget.Record
17527
 * @default null
17528
 * @private
17529
 */
17530
_oRecord : null,
17531
 
17532
/**
17533
 * TD element.
17534
 *
17535
 * @property _elTd
17536
 * @type HTMLElement
17537
 * @default null
17538
 * @private
17539
 */
17540
_elTd : null,
17541
 
17542
/**
17543
 * Container for inline editor.
17544
 *
17545
 * @property _elContainer
17546
 * @type HTMLElement
17547
 * @private
17548
 */
17549
_elContainer : null,
17550
 
17551
/**
17552
 * Reference to Cancel button, if available.
17553
 *
17554
 * @property _elCancelBtn
17555
 * @type HTMLElement
17556
 * @default null
17557
 * @private
17558
 */
17559
_elCancelBtn : null,
17560
 
17561
/**
17562
 * Reference to Save button, if available.
17563
 *
17564
 * @property _elSaveBtn
17565
 * @type HTMLElement
17566
 * @default null
17567
 * @private
17568
 */
17569
_elSaveBtn : null,
17570
 
17571
 
17572
 
17573
 
17574
 
17575
 
17576
 
17577
 
17578
/////////////////////////////////////////////////////////////////////////////
17579
//
17580
// Private methods
17581
//
17582
/////////////////////////////////////////////////////////////////////////////
17583
 
17584
/**
17585
 * Initialize configs.
17586
 *
17587
 * @method _initConfigs
17588
 * @private
17589
 */
17590
_initConfigs : function(oConfigs) {
17591
    // Object literal defines CellEditor configs
17592
    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
17593
        for(var sConfig in oConfigs) {
17594
            if(sConfig) {
17595
                this[sConfig] = oConfigs[sConfig];
17596
            }
17597
        }
17598
    }
17599
},
17600
 
17601
/**
17602
 * Initialize Custom Events.
17603
 *
17604
 * @method _initEvents
17605
 * @private
17606
 */
17607
_initEvents : function() {
17608
    this.createEvent("showEvent");
17609
    this.createEvent("keydownEvent");
17610
    this.createEvent("invalidDataEvent");
17611
    this.createEvent("revertEvent");
17612
    this.createEvent("saveEvent");
17613
    this.createEvent("cancelEvent");
17614
    this.createEvent("blurEvent");
17615
    this.createEvent("blockEvent");
17616
    this.createEvent("unblockEvent");
17617
},
17618
 
17619
/**
17620
 * Initialize container element.
17621
 *
17622
 * @method _initContainerEl
17623
 * @private
17624
 */
17625
_initContainerEl : function() {
17626
    if(this._elContainer) {
17627
        YAHOO.util.Event.purgeElement(this._elContainer, true);
17628
        this._elContainer.innerHTML = "";
17629
    }
17630
 
17631
    var elContainer = document.createElement("div");
17632
    elContainer.id = this.getId() + "-container"; // Needed for tracking blur event
17633
    elContainer.style.display = "none";
17634
    elContainer.tabIndex = 0;
17635
 
17636
    this.className = lang.isArray(this.className) ? this.className : this.className ? [this.className] : [];
17637
    this.className[this.className.length] = DT.CLASS_EDITOR;
17638
    elContainer.className = this.className.join(" ");
17639
 
17640
    document.body.insertBefore(elContainer, document.body.firstChild);
17641
    this._elContainer = elContainer;
17642
},
17643
 
17644
/**
17645
 * Initialize container shim element.
17646
 *
17647
 * @method _initShimEl
17648
 * @private
17649
 */
17650
_initShimEl : function() {
17651
    // Iframe shim
17652
    if(this.useIFrame) {
17653
        if(!this._elIFrame) {
17654
            var elIFrame = document.createElement("iframe");
17655
            elIFrame.src = "javascript:false";
17656
            elIFrame.frameBorder = 0;
17657
            elIFrame.scrolling = "no";
17658
            elIFrame.style.display = "none";
17659
            elIFrame.className = DT.CLASS_EDITOR_SHIM;
17660
            elIFrame.tabIndex = -1;
17661
            elIFrame.role = "presentation";
17662
            elIFrame.title = "Presentational iframe shim";
17663
            document.body.insertBefore(elIFrame, document.body.firstChild);
17664
            this._elIFrame = elIFrame;
17665
        }
17666
    }
17667
},
17668
 
17669
/**
17670
 * Hides CellEditor UI at end of interaction.
17671
 *
17672
 * @method _hide
17673
 */
17674
_hide : function() {
17675
    this.getContainerEl().style.display = "none";
17676
    if(this._elIFrame) {
17677
        this._elIFrame.style.display = "none";
17678
    }
17679
    this.isActive = false;
17680
    this.getDataTable()._oCellEditor =  null;
17681
},
17682
 
17683
 
17684
 
17685
 
17686
 
17687
 
17688
 
17689
 
17690
 
17691
 
17692
 
17693
/////////////////////////////////////////////////////////////////////////////
17694
//
17695
// Public properties
17696
//
17697
/////////////////////////////////////////////////////////////////////////////
17698
/**
17699
 * Implementer defined function that can submit the input value to a server. This
17700
 * function must accept the arguments fnCallback and oNewValue. When the submission
17701
 * is complete, the function must also call fnCallback(bSuccess, oNewValue) to
17702
 * finish the save routine in the CellEditor. This function can also be used to
17703
 * perform extra validation or input value manipulation.
17704
 *
17705
 * @property asyncSubmitter
17706
 * @type HTMLFunction
17707
 */
17708
asyncSubmitter : null,
17709
 
17710
/**
17711
 * Current value.
17712
 *
17713
 * @property value
17714
 * @type MIXED
17715
 */
17716
value : null,
17717
 
17718
/**
17719
 * Default value in case Record data is undefined. NB: Null values will not trigger
17720
 * the default value.
17721
 *
17722
 * @property defaultValue
17723
 * @type MIXED
17724
 * @default null
17725
 */
17726
defaultValue : null,
17727
 
17728
/**
17729
 * Validator function for input data, called from the DataTable instance scope,
17730
 * receives the arguments (inputValue, currentValue, editorInstance) and returns
17731
 * either the validated (or type-converted) value or undefined.
17732
 *
17733
 * @property validator
17734
 * @type HTMLFunction
17735
 * @default null
17736
 */
17737
validator : null,
17738
 
17739
/**
17740
 * If validation is enabled, resets input field of invalid data.
17741
 *
17742
 * @property resetInvalidData
17743
 * @type Boolean
17744
 * @default true
17745
 */
17746
resetInvalidData : true,
17747
 
17748
/**
17749
 * True if currently active.
17750
 *
17751
 * @property isActive
17752
 * @type Boolean
17753
 */
17754
isActive : false,
17755
 
17756
/**
17757
 * Text to display on Save button.
17758
 *
17759
 * @property LABEL_SAVE
17760
 * @type HTML
17761
 * @default "Save"
17762
 */
17763
LABEL_SAVE : "Save",
17764
 
17765
/**
17766
 * Text to display on Cancel button.
17767
 *
17768
 * @property LABEL_CANCEL
17769
 * @type HTML
17770
 * @default "Cancel"
17771
 */
17772
LABEL_CANCEL : "Cancel",
17773
 
17774
/**
17775
 * True if Save/Cancel buttons should not be displayed in the CellEditor.
17776
 *
17777
 * @property disableBtns
17778
 * @type Boolean
17779
 * @default false
17780
 */
17781
disableBtns : false,
17782
 
17783
/**
17784
 * True if iframe shim for container element should be enabled.
17785
 *
17786
 * @property useIFrame
17787
 * @type Boolean
17788
 * @default false
17789
 */
17790
useIFrame : false,
17791
 
17792
/**
17793
 * Custom CSS class or array of classes applied to the container element.
17794
 *
17795
 * @property className
17796
 * @type String || String[]
17797
 */
17798
className : null,
17799
 
17800
 
17801
 
17802
 
17803
 
17804
/////////////////////////////////////////////////////////////////////////////
17805
//
17806
// Public methods
17807
//
17808
/////////////////////////////////////////////////////////////////////////////
17809
/**
17810
 * CellEditor instance name, for logging.
17811
 *
17812
 * @method toString
17813
 * @return {String} Unique name of the CellEditor instance.
17814
 */
17815
 
17816
toString : function() {
17817
    return "CellEditor instance " + this._sId;
17818
},
17819
 
17820
/**
17821
 * CellEditor unique ID.
17822
 *
17823
 * @method getId
17824
 * @return {String} Unique ID of the CellEditor instance.
17825
 */
17826
 
17827
getId : function() {
17828
    return this._sId;
17829
},
17830
 
17831
/**
17832
 * Returns reference to associated DataTable instance.
17833
 *
17834
 * @method getDataTable
17835
 * @return {YAHOO.widget.DataTable} DataTable instance.
17836
 */
17837
 
17838
getDataTable : function() {
17839
    return this._oDataTable;
17840
},
17841
 
17842
/**
17843
 * Returns reference to associated Column instance.
17844
 *
17845
 * @method getColumn
17846
 * @return {YAHOO.widget.Column} Column instance.
17847
 */
17848
 
17849
getColumn : function() {
17850
    return this._oColumn;
17851
},
17852
 
17853
/**
17854
 * Returns reference to associated Record instance.
17855
 *
17856
 * @method getRecord
17857
 * @return {YAHOO.widget.Record} Record instance.
17858
 */
17859
 
17860
getRecord : function() {
17861
    return this._oRecord;
17862
},
17863
 
17864
 
17865
 
17866
/**
17867
 * Returns reference to associated TD element.
17868
 *
17869
 * @method getTdEl
17870
 * @return {HTMLElement} TD element.
17871
 */
17872
 
17873
getTdEl : function() {
17874
    return this._elTd;
17875
},
17876
 
17877
/**
17878
 * Returns container element.
17879
 *
17880
 * @method getContainerEl
17881
 * @return {HTMLElement} Reference to container element.
17882
 */
17883
 
17884
getContainerEl : function() {
17885
    return this._elContainer;
17886
},
17887
 
17888
/**
17889
 * Nulls out the entire CellEditor instance and related objects, removes attached
17890
 * event listeners, and clears out DOM elements inside the container, removes
17891
 * container from the DOM.
17892
 *
17893
 * @method destroy
17894
 */
17895
destroy : function() {
17896
    this.unsubscribeAll();
17897
 
17898
    // Column is late-binding in attach()
17899
    var oColumn = this.getColumn();
17900
    if(oColumn) {
17901
        oColumn.editor = null;
17902
    }
17903
 
17904
    var elContainer = this.getContainerEl();
17905
    if (elContainer) {
17906
        Ev.purgeElement(elContainer, true);
17907
        elContainer.parentNode.removeChild(elContainer);
17908
    }
17909
},
17910
 
17911
/**
17912
 * Renders DOM elements and attaches event listeners.
17913
 *
17914
 * @method render
17915
 */
17916
render : function() {
17917
    if (!this._needsRender) {
17918
        return;
17919
    }
17920
 
17921
    this._initContainerEl();
17922
    this._initShimEl();
17923
 
17924
    // Handle ESC key
17925
    Ev.addListener(this.getContainerEl(), "keydown", function(e, oSelf) {
17926
        // ESC cancels Cell Editor
17927
        if((e.keyCode == 27)) {
17928
            var target = Ev.getTarget(e);
17929
            // workaround for Mac FF3 bug that disabled clicks when ESC hit when
17930
            // select is open. [bug 2273056]
17931
            if (target.nodeName && target.nodeName.toLowerCase() === 'select') {
17932
                target.blur();
17933
            }
17934
            oSelf.cancel();
17935
        }
17936
        // Pass through event
17937
        oSelf.fireEvent("keydownEvent", {editor:oSelf, event:e});
17938
    }, this);
17939
 
17940
    this.renderForm();
17941
 
17942
    // Show Save/Cancel buttons
17943
    if(!this.disableBtns) {
17944
        this.renderBtns();
17945
    }
17946
 
17947
    this.doAfterRender();
17948
    this._needsRender = false;
17949
},
17950
 
17951
/**
17952
 * Renders Save/Cancel buttons.
17953
 *
17954
 * @method renderBtns
17955
 */
17956
renderBtns : function() {
17957
    // Buttons
17958
    var elBtnsDiv = this.getContainerEl().appendChild(document.createElement("div"));
17959
    elBtnsDiv.className = DT.CLASS_BUTTON;
17960
 
17961
    // Save button
17962
    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
17963
    elSaveBtn.className = DT.CLASS_DEFAULT;
17964
    elSaveBtn.innerHTML = this.LABEL_SAVE;
17965
    Ev.addListener(elSaveBtn, "click", function(oArgs) {
17966
        this.save();
17967
    }, this, true);
17968
    this._elSaveBtn = elSaveBtn;
17969
 
17970
    // Cancel button
17971
    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
17972
    elCancelBtn.innerHTML = this.LABEL_CANCEL;
17973
    Ev.addListener(elCancelBtn, "click", function(oArgs) {
17974
        this.cancel();
17975
    }, this, true);
17976
    this._elCancelBtn = elCancelBtn;
17977
},
17978
 
17979
/**
17980
 * Attach CellEditor for a new interaction.
17981
 *
17982
 * @method attach
17983
 * @param oDataTable {YAHOO.widget.DataTable} Associated DataTable instance.
17984
 * @param elCell {HTMLElement} Cell to edit.
17985
 */
17986
attach : function(oDataTable, elCell) {
17987
    // Validate
17988
    if(oDataTable instanceof YAHOO.widget.DataTable) {
17989
        this._oDataTable = oDataTable;
17990
 
17991
        // Validate cell
17992
        elCell = oDataTable.getTdEl(elCell);
17993
        if(elCell) {
17994
            this._elTd = elCell;
17995
 
17996
            // Validate Column
17997
            var oColumn = oDataTable.getColumn(elCell);
17998
            if(oColumn) {
17999
                this._oColumn = oColumn;
18000
 
18001
                // Validate Record
18002
                var oRecord = oDataTable.getRecord(elCell);
18003
                if(oRecord) {
18004
                    this._oRecord = oRecord;
18005
                    var value = oRecord.getData(this.getColumn().getField());
18006
                    this.value = (value !== undefined) ? value : this.defaultValue;
18007
                    return true;
18008
                }
18009
            }
18010
        }
18011
    }
18012
    YAHOO.log("Could not attach CellEditor","error",this.toString());
18013
    return false;
18014
},
18015
 
18016
/**
18017
 * Moves container into position for display.
18018
 *
18019
 * @method move
18020
 */
18021
move : function() {
18022
    // Move Editor
18023
    var elContainer = this.getContainerEl(),
18024
        elTd = this.getTdEl(),
18025
        x = Dom.getX(elTd),
18026
        y = Dom.getY(elTd);
18027
 
18028
    //TODO: remove scrolling logic
18029
    // SF doesn't get xy for cells in scrolling table
18030
    // when tbody display is set to block
18031
    if(isNaN(x) || isNaN(y)) {
18032
        var elTbody = this.getDataTable().getTbodyEl();
18033
        x = elTd.offsetLeft + // cell pos relative to table
18034
                Dom.getX(elTbody.parentNode) - // plus table pos relative to document
18035
                elTbody.scrollLeft; // minus tbody scroll
18036
        y = elTd.offsetTop + // cell pos relative to table
18037
                Dom.getY(elTbody.parentNode) - // plus table pos relative to document
18038
                elTbody.scrollTop + // minus tbody scroll
18039
                this.getDataTable().getTheadEl().offsetHeight; // account for fixed THEAD cells
18040
    }
18041
 
18042
    elContainer.style.left = x + "px";
18043
    elContainer.style.top = y + "px";
18044
 
18045
    if(this._elIFrame) {
18046
        this._elIFrame.style.left = x + "px";
18047
        this._elIFrame.style.top = y + "px";
18048
    }
18049
},
18050
 
18051
/**
18052
 * Displays CellEditor UI in the correct position.
18053
 *
18054
 * @method show
18055
 */
18056
show : function() {
18057
    var elContainer = this.getContainerEl(),
18058
        elIFrame = this._elIFrame;
18059
    this.resetForm();
18060
    this.isActive = true;
18061
    elContainer.style.display = "";
18062
    if(elIFrame) {
18063
        elIFrame.style.width = elContainer.offsetWidth + "px";
18064
        elIFrame.style.height = elContainer.offsetHeight + "px";
18065
        elIFrame.style.display = "";
18066
    }
18067
    this.focus();
18068
    this.fireEvent("showEvent", {editor:this});
18069
    YAHOO.log("CellEditor shown", "info", this.toString());
18070
},
18071
 
18072
/**
18073
 * Fires blockEvent
18074
 *
18075
 * @method block
18076
 */
18077
block : function() {
18078
    this.fireEvent("blockEvent", {editor:this});
18079
    YAHOO.log("CellEditor blocked", "info", this.toString());
18080
},
18081
 
18082
/**
18083
 * Fires unblockEvent
18084
 *
18085
 * @method unblock
18086
 */
18087
unblock : function() {
18088
    this.fireEvent("unblockEvent", {editor:this});
18089
    YAHOO.log("CellEditor unblocked", "info", this.toString());
18090
},
18091
 
18092
/**
18093
 * Saves value of CellEditor and hides UI.
18094
 *
18095
 * @method save
18096
 */
18097
save : function() {
18098
    // Get new value
18099
    var inputValue = this.getInputValue();
18100
    var validValue = inputValue;
18101
 
18102
    // Validate new value
18103
    if(this.validator) {
18104
        validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this);
18105
        if(validValue === undefined ) {
18106
            if(this.resetInvalidData) {
18107
                this.resetForm();
18108
            }
18109
            this.fireEvent("invalidDataEvent",
18110
                    {editor:this, oldData:this.value, newData:inputValue});
18111
            YAHOO.log("Could not save Cell Editor input due to invalid data " +
18112
                    lang.dump(inputValue), "warn", this.toString());
18113
            return;
18114
        }
18115
    }
18116
 
18117
    var oSelf = this;
18118
    var finishSave = function(bSuccess, oNewValue) {
18119
        var oOrigValue = oSelf.value;
18120
        if(bSuccess) {
18121
            // Update new value
18122
            oSelf.value = oNewValue;
18123
            oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue);
18124
 
18125
            // Hide CellEditor
18126
            oSelf._hide();
18127
 
18128
            oSelf.fireEvent("saveEvent",
18129
                    {editor:oSelf, oldData:oOrigValue, newData:oSelf.value});
18130
            YAHOO.log("Cell Editor input saved", "info", this.toString());
18131
        }
18132
        else {
18133
            oSelf.resetForm();
18134
            oSelf.fireEvent("revertEvent",
18135
                    {editor:oSelf, oldData:oOrigValue, newData:oNewValue});
18136
            YAHOO.log("Could not save Cell Editor input " +
18137
                    lang.dump(oNewValue), "warn", oSelf.toString());
18138
        }
18139
        oSelf.unblock();
18140
    };
18141
 
18142
    this.block();
18143
    if(lang.isFunction(this.asyncSubmitter)) {
18144
        this.asyncSubmitter.call(this, finishSave, validValue);
18145
    }
18146
    else {
18147
        finishSave(true, validValue);
18148
    }
18149
},
18150
 
18151
/**
18152
 * Cancels CellEditor input and hides UI.
18153
 *
18154
 * @method cancel
18155
 */
18156
cancel : function() {
18157
    if(this.isActive) {
18158
        this._hide();
18159
        this.fireEvent("cancelEvent", {editor:this});
18160
        YAHOO.log("CellEditor canceled", "info", this.toString());
18161
    }
18162
    else {
18163
        YAHOO.log("Unable to cancel CellEditor", "warn", this.toString());
18164
    }
18165
},
18166
 
18167
/**
18168
 * Renders form elements.
18169
 *
18170
 * @method renderForm
18171
 */
18172
renderForm : function() {
18173
    // To be implemented by subclass
18174
},
18175
 
18176
/**
18177
 * Access to add additional event listeners.
18178
 *
18179
 * @method doAfterRender
18180
 */
18181
doAfterRender : function() {
18182
    // To be implemented by subclass
18183
},
18184
 
18185
 
18186
/**
18187
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18188
 * to save input without them.
18189
 *
18190
 * @method handleDisabledBtns
18191
 */
18192
handleDisabledBtns : function() {
18193
    // To be implemented by subclass
18194
},
18195
 
18196
/**
18197
 * Resets CellEditor UI to initial state.
18198
 *
18199
 * @method resetForm
18200
 */
18201
resetForm : function() {
18202
    // To be implemented by subclass
18203
},
18204
 
18205
/**
18206
 * Sets focus in CellEditor.
18207
 *
18208
 * @method focus
18209
 */
18210
focus : function() {
18211
    // To be implemented by subclass
18212
},
18213
 
18214
/**
18215
 * Retrieves input value from CellEditor.
18216
 *
18217
 * @method getInputValue
18218
 */
18219
getInputValue : function() {
18220
    // To be implemented by subclass
18221
}
18222
 
18223
};
18224
 
18225
lang.augmentProto(BCE, util.EventProvider);
18226
 
18227
 
18228
/////////////////////////////////////////////////////////////////////////////
18229
//
18230
// Custom Events
18231
//
18232
/////////////////////////////////////////////////////////////////////////////
18233
 
18234
/**
18235
 * Fired when a CellEditor is shown.
18236
 *
18237
 * @event showEvent
18238
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18239
 */
18240
 
18241
/**
18242
 * Fired when a CellEditor has a keydown.
18243
 *
18244
 * @event keydownEvent
18245
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18246
 * @param oArgs.event {HTMLEvent} The event object.
18247
 */
18248
 
18249
/**
18250
 * Fired when a CellEditor input is reverted due to invalid data.
18251
 *
18252
 * @event invalidDataEvent
18253
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18254
 * @param oArgs.newData {Object} New data value from form input field.
18255
 * @param oArgs.oldData {Object} Old data value.
18256
 */
18257
 
18258
/**
18259
 * Fired when a CellEditor input is reverted due to asyncSubmitter failure.
18260
 *
18261
 * @event revertEvent
18262
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18263
 * @param oArgs.newData {Object} New data value from form input field.
18264
 * @param oArgs.oldData {Object} Old data value.
18265
 */
18266
 
18267
/**
18268
 * Fired when a CellEditor input is saved.
18269
 *
18270
 * @event saveEvent
18271
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18272
 * @param oArgs.newData {Object} New data value from form input field.
18273
 * @param oArgs.oldData {Object} Old data value.
18274
 */
18275
 
18276
/**
18277
 * Fired when a CellEditor input is canceled.
18278
 *
18279
 * @event cancelEvent
18280
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18281
 */
18282
 
18283
/**
18284
 * Fired when a CellEditor has a blur event.
18285
 *
18286
 * @event blurEvent
18287
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18288
 */
18289
 
18290
 
18291
 
18292
 
18293
 
18294
 
18295
 
18296
 
18297
 
18298
 
18299
 
18300
 
18301
 
18302
 
18303
/****************************************************************************/
18304
/****************************************************************************/
18305
/****************************************************************************/
18306
 
18307
/**
18308
 * The CheckboxCellEditor class provides functionality for inline editing
18309
 * DataTable cell data with checkboxes.
18310
 *
18311
 * @namespace YAHOO.widget
18312
 * @class CheckboxCellEditor
18313
 * @extends YAHOO.widget.BaseCellEditor
18314
 * @constructor
18315
 * @param oConfigs {Object} (Optional) Object literal of configs.
18316
 */
18317
widget.CheckboxCellEditor = function(oConfigs) {
18318
    oConfigs = oConfigs || {};
18319
    this._sId = this._sId || Dom.generateId(null, "yui-checkboxceditor"); // "yui-checkboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18320
    YAHOO.widget.BaseCellEditor._nCount++;
18321
    widget.CheckboxCellEditor.superclass.constructor.call(this, oConfigs.type || "checkbox", oConfigs);
18322
};
18323
 
18324
// CheckboxCellEditor extends BaseCellEditor
18325
lang.extend(widget.CheckboxCellEditor, BCE, {
18326
 
18327
/////////////////////////////////////////////////////////////////////////////
18328
//
18329
// CheckboxCellEditor public properties
18330
//
18331
/////////////////////////////////////////////////////////////////////////////
18332
/**
18333
 * Array of checkbox values. Can either be a simple array (e.g., ["red","green","blue"])
18334
 * or a an array of objects (e.g., [{label:"red", value:"#FF0000"},
18335
 * {label:"green", value:"#00FF00"}, {label:"blue", value:"#0000FF"}]). String
18336
 * values are treated as markup and inserted into the DOM as innerHTML.
18337
 *
18338
 * @property checkboxOptions
18339
 * @type HTML[] | Object[]
18340
 */
18341
checkboxOptions : null,
18342
 
18343
/**
18344
 * Reference to the checkbox elements.
18345
 *
18346
 * @property checkboxes
18347
 * @type HTMLElement[]
18348
 */
18349
checkboxes : null,
18350
 
18351
/**
18352
 * Array of checked values
18353
 *
18354
 * @property value
18355
 * @type String[]
18356
 */
18357
value : null,
18358
 
18359
/////////////////////////////////////////////////////////////////////////////
18360
//
18361
// CheckboxCellEditor public methods
18362
//
18363
/////////////////////////////////////////////////////////////////////////////
18364
 
18365
/**
18366
 * Render a form with input(s) type=checkbox.
18367
 *
18368
 * @method renderForm
18369
 */
18370
renderForm : function() {
18371
    if(lang.isArray(this.checkboxOptions)) {
18372
        var checkboxOption, checkboxValue, checkboxId, elLabel, j, len;
18373
 
18374
        // Create the checkbox buttons in an IE-friendly way...
18375
        for(j=0,len=this.checkboxOptions.length; j<len; j++) {
18376
            checkboxOption = this.checkboxOptions[j];
18377
            checkboxValue = lang.isValue(checkboxOption.value) ?
18378
                    checkboxOption.value : checkboxOption;
18379
 
18380
            checkboxId = this.getId() + "-chk" + j;
18381
            this.getContainerEl().innerHTML += "<input type=\"checkbox\"" +
18382
                    " id=\"" + checkboxId + "\"" + // Needed for label
18383
                    " value=\"" + checkboxValue + "\" />";
18384
 
18385
            // Create the labels in an IE-friendly way
18386
            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
18387
            elLabel.htmlFor = checkboxId;
18388
            elLabel.innerHTML = lang.isValue(checkboxOption.label) ?
18389
                    checkboxOption.label : checkboxOption;
18390
        }
18391
 
18392
        // Store the reference to the checkbox elements
18393
        var allCheckboxes = [];
18394
        for(j=0; j<len; j++) {
18395
            allCheckboxes[allCheckboxes.length] = this.getContainerEl().childNodes[j*2];
18396
        }
18397
        this.checkboxes = allCheckboxes;
18398
 
18399
        if(this.disableBtns) {
18400
            this.handleDisabledBtns();
18401
        }
18402
    }
18403
    else {
18404
        YAHOO.log("Could not find checkboxOptions", "error", this.toString());
18405
    }
18406
},
18407
 
18408
/**
18409
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18410
 * to save input without them.
18411
 *
18412
 * @method handleDisabledBtns
18413
 */
18414
handleDisabledBtns : function() {
18415
    Ev.addListener(this.getContainerEl(), "click", function(v){
18416
        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
18417
            // Save on blur
18418
            this.save();
18419
        }
18420
    }, this, true);
18421
},
18422
 
18423
/**
18424
 * Resets CheckboxCellEditor UI to initial state.
18425
 *
18426
 * @method resetForm
18427
 */
18428
resetForm : function() {
18429
    // Normalize to array
18430
    var originalValues = lang.isArray(this.value) ? this.value : [this.value];
18431
 
18432
    // Match checks to value
18433
    for(var i=0, j=this.checkboxes.length; i<j; i++) {
18434
        this.checkboxes[i].checked = false;
18435
        for(var k=0, len=originalValues.length; k<len; k++) {
18436
            if(this.checkboxes[i].value == originalValues[k]) {
18437
                this.checkboxes[i].checked = true;
18438
            }
18439
        }
18440
    }
18441
},
18442
 
18443
/**
18444
 * Sets focus in CheckboxCellEditor.
18445
 *
18446
 * @method focus
18447
 */
18448
focus : function() {
18449
    this.checkboxes[0].focus();
18450
},
18451
 
18452
/**
18453
 * Retrieves input value from CheckboxCellEditor.
18454
 *
18455
 * @method getInputValue
18456
 */
18457
getInputValue : function() {
18458
    var checkedValues = [];
18459
    for(var i=0, j=this.checkboxes.length; i<j; i++) {
18460
        if(this.checkboxes[i].checked) {
18461
            checkedValues[checkedValues.length] = this.checkboxes[i].value;
18462
        }
18463
    }
18464
    return checkedValues;
18465
}
18466
 
18467
});
18468
 
18469
// Copy static members to CheckboxCellEditor class
18470
lang.augmentObject(widget.CheckboxCellEditor, BCE);
18471
 
18472
 
18473
 
18474
 
18475
 
18476
 
18477
 
18478
 
18479
/****************************************************************************/
18480
/****************************************************************************/
18481
/****************************************************************************/
18482
 
18483
/**
18484
 * The DataCellEditor class provides functionality for inline editing
18485
 * DataTable cell data with a YUI Calendar.
18486
 *
18487
 * @namespace YAHOO.widget
18488
 * @class DateCellEditor
18489
 * @extends YAHOO.widget.BaseCellEditor
18490
 * @constructor
18491
 * @param oConfigs {Object} (Optional) Object literal of configs.
18492
 */
18493
widget.DateCellEditor = function(oConfigs) {
18494
    oConfigs = oConfigs || {};
18495
    this._sId = this._sId || Dom.generateId(null, "yui-dateceditor"); // "yui-dateceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18496
    YAHOO.widget.BaseCellEditor._nCount++;
18497
    widget.DateCellEditor.superclass.constructor.call(this, oConfigs.type || "date", oConfigs);
18498
};
18499
 
18500
// CheckboxCellEditor extends BaseCellEditor
18501
lang.extend(widget.DateCellEditor, BCE, {
18502
 
18503
/////////////////////////////////////////////////////////////////////////////
18504
//
18505
// DateCellEditor public properties
18506
//
18507
/////////////////////////////////////////////////////////////////////////////
18508
/**
18509
 * Reference to Calendar instance.
18510
 *
18511
 * @property calendar
18512
 * @type YAHOO.widget.Calendar
18513
 */
18514
calendar : null,
18515
 
18516
/**
18517
 * Configs for the calendar instance, to be passed to Calendar constructor.
18518
 *
18519
 * @property calendarOptions
18520
 * @type Object
18521
 */
18522
calendarOptions : null,
18523
 
18524
/**
18525
 * Default value.
18526
 *
18527
 * @property defaultValue
18528
 * @type Date
18529
 * @default new Date()
18530
 */
18531
defaultValue : new Date(),
18532
 
18533
 
18534
/////////////////////////////////////////////////////////////////////////////
18535
//
18536
// DateCellEditor public methods
18537
//
18538
/////////////////////////////////////////////////////////////////////////////
18539
 
18540
/**
18541
 * Render a Calendar.
18542
 *
18543
 * @method renderForm
18544
 */
18545
renderForm : function() {
18546
    // Calendar widget
18547
    if(YAHOO.widget.Calendar) {
18548
        var calContainer = this.getContainerEl().appendChild(document.createElement("div"));
18549
        calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor
18550
        var calendar =
18551
                new YAHOO.widget.Calendar(this.getId() + "-date",
18552
                calContainer.id, this.calendarOptions);
18553
        calendar.render();
18554
        calContainer.style.cssFloat = "none";
18555
 
18556
        // Bug 2528576
18557
        calendar.hideEvent.subscribe(function() {this.cancel();}, this, true);
18558
 
18559
        if(ua.ie) {
18560
            var calFloatClearer = this.getContainerEl().appendChild(document.createElement("div"));
18561
            calFloatClearer.style.clear = "both";
18562
        }
18563
 
18564
        this.calendar = calendar;
18565
 
18566
        if(this.disableBtns) {
18567
            this.handleDisabledBtns();
18568
        }
18569
    }
18570
    else {
18571
        YAHOO.log("Could not find YUI Calendar", "error", this.toString());
18572
    }
18573
 
18574
},
18575
 
18576
/**
18577
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18578
 * to save input without them.
18579
 *
18580
 * @method handleDisabledBtns
18581
 */
18582
handleDisabledBtns : function() {
18583
    this.calendar.selectEvent.subscribe(function(v){
18584
        // Save on select
18585
        this.save();
18586
    }, this, true);
18587
},
18588
 
18589
/**
18590
 * Resets DateCellEditor UI to initial state.
18591
 *
18592
 * @method resetForm
18593
 */
18594
resetForm : function() {
18595
    var value = this.value || (new Date());
18596
    this.calendar.select(value);
18597
    this.calendar.cfg.setProperty("pagedate",value,false);
18598
	this.calendar.render();
18599
	// Bug 2528576
18600
	this.calendar.show();
18601
},
18602
 
18603
/**
18604
 * Sets focus in DateCellEditor.
18605
 *
18606
 * @method focus
18607
 */
18608
focus : function() {
18609
    // To be impmlemented by subclass
18610
},
18611
 
18612
/**
18613
 * Retrieves input value from DateCellEditor.
18614
 *
18615
 * @method getInputValue
18616
 */
18617
getInputValue : function() {
18618
    return this.calendar.getSelectedDates()[0];
18619
}
18620
 
18621
});
18622
 
18623
// Copy static members to DateCellEditor class
18624
lang.augmentObject(widget.DateCellEditor, BCE);
18625
 
18626
 
18627
 
18628
 
18629
 
18630
 
18631
 
18632
 
18633
 
18634
/****************************************************************************/
18635
/****************************************************************************/
18636
/****************************************************************************/
18637
 
18638
/**
18639
 * The DropdownCellEditor class provides functionality for inline editing
18640
 * DataTable cell data a SELECT element.
18641
 *
18642
 * @namespace YAHOO.widget
18643
 * @class DropdownCellEditor
18644
 * @extends YAHOO.widget.BaseCellEditor
18645
 * @constructor
18646
 * @param oConfigs {Object} (Optional) Object literal of configs.
18647
 */
18648
widget.DropdownCellEditor = function(oConfigs) {
18649
    oConfigs = oConfigs || {};
18650
    this._sId = this._sId || Dom.generateId(null, "yui-dropdownceditor"); // "yui-dropdownceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18651
    YAHOO.widget.BaseCellEditor._nCount++;
18652
    widget.DropdownCellEditor.superclass.constructor.call(this, oConfigs.type || "dropdown", oConfigs);
18653
};
18654
 
18655
// DropdownCellEditor extends BaseCellEditor
18656
lang.extend(widget.DropdownCellEditor, BCE, {
18657
 
18658
/////////////////////////////////////////////////////////////////////////////
18659
//
18660
// DropdownCellEditor public properties
18661
//
18662
/////////////////////////////////////////////////////////////////////////////
18663
/**
18664
 * Array of dropdown values. Can either be a simple array (e.g.,
18665
 * ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g.,
18666
 * [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"},
18667
 * {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]). String
18668
 * values are treated as markup and inserted into the DOM as innerHTML.
18669
 *
18670
 * @property dropdownOptions
18671
 * @type HTML[] | Object[]
18672
 */
18673
dropdownOptions : null,
18674
 
18675
/**
18676
 * Reference to Dropdown element.
18677
 *
18678
 * @property dropdown
18679
 * @type HTMLElement
18680
 */
18681
dropdown : null,
18682
 
18683
/**
18684
 * Enables multi-select.
18685
 *
18686
 * @property multiple
18687
 * @type Boolean
18688
 */
18689
multiple : false,
18690
 
18691
/**
18692
 * Specifies number of visible options.
18693
 *
18694
 * @property size
18695
 * @type Number
18696
 */
18697
size : null,
18698
 
18699
/////////////////////////////////////////////////////////////////////////////
18700
//
18701
// DropdownCellEditor public methods
18702
//
18703
/////////////////////////////////////////////////////////////////////////////
18704
 
18705
/**
18706
 * Render a form with select element.
18707
 *
18708
 * @method renderForm
18709
 */
18710
renderForm : function() {
18711
    var elDropdown = this.getContainerEl().appendChild(document.createElement("select"));
18712
    elDropdown.style.zoom = 1;
18713
    if(this.multiple) {
18714
        elDropdown.multiple = "multiple";
18715
    }
18716
    if(lang.isNumber(this.size)) {
18717
        elDropdown.size = this.size;
18718
    }
18719
    this.dropdown = elDropdown;
18720
 
18721
    if(lang.isArray(this.dropdownOptions)) {
18722
        var dropdownOption, elOption;
18723
        for(var i=0, j=this.dropdownOptions.length; i<j; i++) {
18724
            dropdownOption = this.dropdownOptions[i];
18725
            elOption = document.createElement("option");
18726
            elOption.value = (lang.isValue(dropdownOption.value)) ?
18727
                    dropdownOption.value : dropdownOption;
18728
            elOption.innerHTML = (lang.isValue(dropdownOption.label)) ?
18729
                    dropdownOption.label : dropdownOption;
18730
            elOption = elDropdown.appendChild(elOption);
18731
        }
18732
 
18733
        if(this.disableBtns) {
18734
            this.handleDisabledBtns();
18735
        }
18736
    }
18737
},
18738
 
18739
/**
18740
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18741
 * to save input without them.
18742
 *
18743
 * @method handleDisabledBtns
18744
 */
18745
handleDisabledBtns : function() {
18746
    // Save on blur for multi-select
18747
    if(this.multiple) {
18748
        Ev.addListener(this.dropdown, "blur", function(v){
18749
            // Save on change
18750
            this.save();
18751
        }, this, true);
18752
    }
18753
    // Save on change for single-select
18754
    else {
18755
        if(!ua.ie) {
18756
            Ev.addListener(this.dropdown, "change", function(v){
18757
                // Save on change
18758
                this.save();
18759
            }, this, true);
18760
        }
18761
        else {
18762
            // Bug 2529274: "change" event is not keyboard accessible in IE6
18763
            Ev.addListener(this.dropdown, "blur", function(v){
18764
                this.save();
18765
            }, this, true);
18766
            Ev.addListener(this.dropdown, "click", function(v){
18767
                this.save();
18768
            }, this, true);
18769
        }
18770
    }
18771
},
18772
 
18773
/**
18774
 * Resets DropdownCellEditor UI to initial state.
18775
 *
18776
 * @method resetForm
18777
 */
18778
resetForm : function() {
18779
    var allOptions = this.dropdown.options,
18780
        i=0, j=allOptions.length;
18781
 
18782
    // Look for multi-select selections
18783
    if(lang.isArray(this.value)) {
18784
        var allValues = this.value,
18785
            m=0, n=allValues.length,
18786
            hash = {};
18787
        // Reset all selections and stash options in a value hash
18788
        for(; i<j; i++) {
18789
            allOptions[i].selected = false;
18790
            hash[allOptions[i].value] = allOptions[i];
18791
        }
18792
        for(; m<n; m++) {
18793
            if(hash[allValues[m]]) {
18794
                hash[allValues[m]].selected = true;
18795
            }
18796
        }
18797
    }
18798
    // Only need to look for a single selection
18799
    else {
18800
        for(; i<j; i++) {
18801
            if(this.value == allOptions[i].value) {
18802
                allOptions[i].selected = true;
18803
            }
18804
        }
18805
    }
18806
},
18807
 
18808
/**
18809
 * Sets focus in DropdownCellEditor.
18810
 *
18811
 * @method focus
18812
 */
18813
focus : function() {
18814
    this.getDataTable()._focusEl(this.dropdown);
18815
},
18816
 
18817
/**
18818
 * Retrieves input value from DropdownCellEditor.
18819
 *
18820
 * @method getInputValue
18821
 */
18822
getInputValue : function() {
18823
    var allOptions = this.dropdown.options;
18824
 
18825
    // Look for multiple selections
18826
    if(this.multiple) {
18827
        var values = [],
18828
            i=0, j=allOptions.length;
18829
        for(; i<j; i++) {
18830
            if(allOptions[i].selected) {
18831
                values.push(allOptions[i].value);
18832
            }
18833
        }
18834
        return values;
18835
    }
18836
    // Only need to look for single selection
18837
    else {
18838
        return allOptions[allOptions.selectedIndex].value;
18839
    }
18840
}
18841
 
18842
});
18843
 
18844
// Copy static members to DropdownCellEditor class
18845
lang.augmentObject(widget.DropdownCellEditor, BCE);
18846
 
18847
 
18848
 
18849
 
18850
 
18851
 
18852
/****************************************************************************/
18853
/****************************************************************************/
18854
/****************************************************************************/
18855
 
18856
/**
18857
 * The RadioCellEditor class provides functionality for inline editing
18858
 * DataTable cell data with radio buttons.
18859
 *
18860
 * @namespace YAHOO.widget
18861
 * @class RadioCellEditor
18862
 * @extends YAHOO.widget.BaseCellEditor
18863
 * @constructor
18864
 * @param oConfigs {Object} (Optional) Object literal of configs.
18865
 */
18866
widget.RadioCellEditor = function(oConfigs) {
18867
    oConfigs = oConfigs || {};
18868
    this._sId = this._sId || Dom.generateId(null, "yui-radioceditor"); // "yui-radioceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18869
    YAHOO.widget.BaseCellEditor._nCount++;
18870
    widget.RadioCellEditor.superclass.constructor.call(this, oConfigs.type || "radio", oConfigs);
18871
};
18872
 
18873
// RadioCellEditor extends BaseCellEditor
18874
lang.extend(widget.RadioCellEditor, BCE, {
18875
 
18876
/////////////////////////////////////////////////////////////////////////////
18877
//
18878
// RadioCellEditor public properties
18879
//
18880
/////////////////////////////////////////////////////////////////////////////
18881
/**
18882
 * Reference to radio elements.
18883
 *
18884
 * @property radios
18885
 * @type HTMLElement[]
18886
 */
18887
radios : null,
18888
 
18889
/**
18890
 * Array of radio values. Can either be a simple array (e.g., ["yes","no","maybe"])
18891
 * or a an array of objects (e.g., [{label:"yes", value:1}, {label:"no", value:-1},
18892
 * {label:"maybe", value:0}]). String values are treated as markup and inserted
18893
 * into the DOM as innerHTML.
18894
 *
18895
 * @property radioOptions
18896
 * @type HTML[] | Object[]
18897
 */
18898
radioOptions : null,
18899
 
18900
/////////////////////////////////////////////////////////////////////////////
18901
//
18902
// RadioCellEditor public methods
18903
//
18904
/////////////////////////////////////////////////////////////////////////////
18905
 
18906
/**
18907
 * Render a form with input(s) type=radio.
18908
 *
18909
 * @method renderForm
18910
 */
18911
renderForm : function() {
18912
    if(lang.isArray(this.radioOptions)) {
18913
        var radioOption, radioValue, radioId, elLabel;
18914
 
18915
        // Create the radio buttons in an IE-friendly way
18916
        for(var i=0, len=this.radioOptions.length; i<len; i++) {
18917
            radioOption = this.radioOptions[i];
18918
            radioValue = lang.isValue(radioOption.value) ?
18919
                    radioOption.value : radioOption;
18920
            radioId = this.getId() + "-radio" + i;
18921
            this.getContainerEl().innerHTML += "<input type=\"radio\"" +
18922
                    " name=\"" + this.getId() + "\"" +
18923
                    " value=\"" + radioValue + "\"" +
18924
                    " id=\"" +  radioId + "\" />"; // Needed for label
18925
 
18926
            // Create the labels in an IE-friendly way
18927
            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
18928
            elLabel.htmlFor = radioId;
18929
            elLabel.innerHTML = (lang.isValue(radioOption.label)) ?
18930
                    radioOption.label : radioOption;
18931
        }
18932
 
18933
        // Store the reference to the checkbox elements
18934
        var allRadios = [],
18935
            elRadio;
18936
        for(var j=0; j<len; j++) {
18937
            elRadio = this.getContainerEl().childNodes[j*2];
18938
            allRadios[allRadios.length] = elRadio;
18939
        }
18940
        this.radios = allRadios;
18941
 
18942
        if(this.disableBtns) {
18943
            this.handleDisabledBtns();
18944
        }
18945
    }
18946
    else {
18947
        YAHOO.log("Could not find radioOptions", "error", this.toString());
18948
    }
18949
},
18950
 
18951
/**
18952
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18953
 * to save input without them.
18954
 *
18955
 * @method handleDisabledBtns
18956
 */
18957
handleDisabledBtns : function() {
18958
    Ev.addListener(this.getContainerEl(), "click", function(v){
18959
        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
18960
            // Save on blur
18961
            this.save();
18962
        }
18963
    }, this, true);
18964
},
18965
 
18966
/**
18967
 * Resets RadioCellEditor UI to initial state.
18968
 *
18969
 * @method resetForm
18970
 */
18971
resetForm : function() {
18972
    for(var i=0, j=this.radios.length; i<j; i++) {
18973
        var elRadio = this.radios[i];
18974
        if(this.value == elRadio.value) {
18975
            elRadio.checked = true;
18976
            return;
18977
        }
18978
    }
18979
},
18980
 
18981
/**
18982
 * Sets focus in RadioCellEditor.
18983
 *
18984
 * @method focus
18985
 */
18986
focus : function() {
18987
    for(var i=0, j=this.radios.length; i<j; i++) {
18988
        if(this.radios[i].checked) {
18989
            this.radios[i].focus();
18990
            return;
18991
        }
18992
    }
18993
},
18994
 
18995
/**
18996
 * Retrieves input value from RadioCellEditor.
18997
 *
18998
 * @method getInputValue
18999
 */
19000
getInputValue : function() {
19001
    for(var i=0, j=this.radios.length; i<j; i++) {
19002
        if(this.radios[i].checked) {
19003
            return this.radios[i].value;
19004
        }
19005
    }
19006
}
19007
 
19008
});
19009
 
19010
// Copy static members to RadioCellEditor class
19011
lang.augmentObject(widget.RadioCellEditor, BCE);
19012
 
19013
 
19014
 
19015
 
19016
 
19017
 
19018
/****************************************************************************/
19019
/****************************************************************************/
19020
/****************************************************************************/
19021
 
19022
/**
19023
 * The TextareaCellEditor class provides functionality for inline editing
19024
 * DataTable cell data with a TEXTAREA element.
19025
 *
19026
 * @namespace YAHOO.widget
19027
 * @class TextareaCellEditor
19028
 * @extends YAHOO.widget.BaseCellEditor
19029
 * @constructor
19030
 * @param oConfigs {Object} (Optional) Object literal of configs.
19031
 */
19032
widget.TextareaCellEditor = function(oConfigs) {
19033
    oConfigs = oConfigs || {};
19034
    this._sId = this._sId || Dom.generateId(null, "yui-textareaceditor");// "yui-textareaceditor" + ;
19035
    YAHOO.widget.BaseCellEditor._nCount++;
19036
    widget.TextareaCellEditor.superclass.constructor.call(this, oConfigs.type || "textarea", oConfigs);
19037
};
19038
 
19039
// TextareaCellEditor extends BaseCellEditor
19040
lang.extend(widget.TextareaCellEditor, BCE, {
19041
 
19042
/////////////////////////////////////////////////////////////////////////////
19043
//
19044
// TextareaCellEditor public properties
19045
//
19046
/////////////////////////////////////////////////////////////////////////////
19047
/**
19048
 * Reference to textarea element.
19049
 *
19050
 * @property textarea
19051
 * @type HTMLElement
19052
 */
19053
textarea : null,
19054
 
19055
 
19056
/////////////////////////////////////////////////////////////////////////////
19057
//
19058
// TextareaCellEditor public methods
19059
//
19060
/////////////////////////////////////////////////////////////////////////////
19061
 
19062
/**
19063
 * Render a form with textarea.
19064
 *
19065
 * @method renderForm
19066
 */
19067
renderForm : function() {
19068
    var elTextarea = this.getContainerEl().appendChild(document.createElement("textarea"));
19069
    this.textarea = elTextarea;
19070
 
19071
    if(this.disableBtns) {
19072
        this.handleDisabledBtns();
19073
    }
19074
},
19075
 
19076
/**
19077
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
19078
 * to save input without them.
19079
 *
19080
 * @method handleDisabledBtns
19081
 */
19082
handleDisabledBtns : function() {
19083
    Ev.addListener(this.textarea, "blur", function(v){
19084
        // Save on blur
19085
        this.save();
19086
    }, this, true);
19087
},
19088
 
19089
/**
19090
 * Moves TextareaCellEditor UI to a cell.
19091
 *
19092
 * @method move
19093
 */
19094
move : function() {
19095
    this.textarea.style.width = this.getTdEl().offsetWidth + "px";
19096
    this.textarea.style.height = "3em";
19097
    YAHOO.widget.TextareaCellEditor.superclass.move.call(this);
19098
},
19099
 
19100
/**
19101
 * Resets TextareaCellEditor UI to initial state.
19102
 *
19103
 * @method resetForm
19104
 */
19105
resetForm : function() {
19106
    this.textarea.value = this.value;
19107
},
19108
 
19109
/**
19110
 * Sets focus in TextareaCellEditor.
19111
 *
19112
 * @method focus
19113
 */
19114
focus : function() {
19115
    // Bug 2303181, Bug 2263600
19116
    this.getDataTable()._focusEl(this.textarea);
19117
    this.textarea.select();
19118
},
19119
 
19120
/**
19121
 * Retrieves input value from TextareaCellEditor.
19122
 *
19123
 * @method getInputValue
19124
 */
19125
getInputValue : function() {
19126
    return this.textarea.value;
19127
}
19128
 
19129
});
19130
 
19131
// Copy static members to TextareaCellEditor class
19132
lang.augmentObject(widget.TextareaCellEditor, BCE);
19133
 
19134
 
19135
 
19136
 
19137
 
19138
 
19139
 
19140
 
19141
 
19142
/****************************************************************************/
19143
/****************************************************************************/
19144
/****************************************************************************/
19145
 
19146
/**
19147
 * The TextboxCellEditor class provides functionality for inline editing
19148
 * DataTable cell data with an INPUT TYPE=TEXT element.
19149
 *
19150
 * @namespace YAHOO.widget
19151
 * @class TextboxCellEditor
19152
 * @extends YAHOO.widget.BaseCellEditor
19153
 * @constructor
19154
 * @param oConfigs {Object} (Optional) Object literal of configs.
19155
 */
19156
widget.TextboxCellEditor = function(oConfigs) {
19157
    oConfigs = oConfigs || {};
19158
    this._sId = this._sId || Dom.generateId(null, "yui-textboxceditor");// "yui-textboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
19159
    YAHOO.widget.BaseCellEditor._nCount++;
19160
    widget.TextboxCellEditor.superclass.constructor.call(this, oConfigs.type || "textbox", oConfigs);
19161
};
19162
 
19163
// TextboxCellEditor extends BaseCellEditor
19164
lang.extend(widget.TextboxCellEditor, BCE, {
19165
 
19166
/////////////////////////////////////////////////////////////////////////////
19167
//
19168
// TextboxCellEditor public properties
19169
//
19170
/////////////////////////////////////////////////////////////////////////////
19171
/**
19172
 * Reference to the textbox element.
19173
 *
19174
 * @property textbox
19175
 */
19176
textbox : null,
19177
 
19178
/////////////////////////////////////////////////////////////////////////////
19179
//
19180
// TextboxCellEditor public methods
19181
//
19182
/////////////////////////////////////////////////////////////////////////////
19183
 
19184
/**
19185
 * Render a form with input type=text.
19186
 *
19187
 * @method renderForm
19188
 */
19189
renderForm : function() {
19190
    var elTextbox;
19191
    // Bug 1802582: SF3/Mac needs a form element wrapping the input
19192
    if(ua.webkit>420) {
19193
        elTextbox = this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input"));
19194
    }
19195
    else {
19196
        elTextbox = this.getContainerEl().appendChild(document.createElement("input"));
19197
    }
19198
    elTextbox.type = "text";
19199
    this.textbox = elTextbox;
19200
 
19201
    // Save on enter by default
19202
    // Bug: 1802582 Set up a listener on each textbox to track on keypress
19203
    // since SF/OP can't preventDefault on keydown
19204
    Ev.addListener(elTextbox, "keypress", function(v){
19205
        if((v.keyCode === 13)) {
19206
            // Prevent form submit
19207
            YAHOO.util.Event.preventDefault(v);
19208
            this.save();
19209
        }
19210
    }, this, true);
19211
 
19212
    if(this.disableBtns) {
19213
        // By default this is no-op since enter saves by default
19214
        this.handleDisabledBtns();
19215
    }
19216
},
19217
 
19218
/**
19219
 * Moves TextboxCellEditor UI to a cell.
19220
 *
19221
 * @method move
19222
 */
19223
move : function() {
19224
    this.textbox.style.width = this.getTdEl().offsetWidth + "px";
19225
    widget.TextboxCellEditor.superclass.move.call(this);
19226
},
19227
 
19228
/**
19229
 * Resets TextboxCellEditor UI to initial state.
19230
 *
19231
 * @method resetForm
19232
 */
19233
resetForm : function() {
19234
    this.textbox.value = lang.isValue(this.value) ? this.value.toString() : "";
19235
},
19236
 
19237
/**
19238
 * Sets focus in TextboxCellEditor.
19239
 *
19240
 * @method focus
19241
 */
19242
focus : function() {
19243
    // Bug 2303181, Bug 2263600
19244
    this.getDataTable()._focusEl(this.textbox);
19245
    this.textbox.select();
19246
},
19247
 
19248
/**
19249
 * Returns new value for TextboxCellEditor.
19250
 *
19251
 * @method getInputValue
19252
 */
19253
getInputValue : function() {
19254
    return this.textbox.value;
19255
}
19256
 
19257
});
19258
 
19259
// Copy static members to TextboxCellEditor class
19260
lang.augmentObject(widget.TextboxCellEditor, BCE);
19261
 
19262
 
19263
 
19264
 
19265
 
19266
 
19267
 
19268
/////////////////////////////////////////////////////////////////////////////
19269
//
19270
// DataTable extension
19271
//
19272
/////////////////////////////////////////////////////////////////////////////
19273
 
19274
/**
19275
 * CellEditor subclasses.
19276
 * @property DataTable.Editors
19277
 * @type Object
19278
 * @static
19279
 */
19280
DT.Editors = {
19281
    checkbox : widget.CheckboxCellEditor,
19282
    "date"   : widget.DateCellEditor,
19283
    dropdown : widget.DropdownCellEditor,
19284
    radio    : widget.RadioCellEditor,
19285
    textarea : widget.TextareaCellEditor,
19286
    textbox  : widget.TextboxCellEditor
19287
};
19288
 
19289
/****************************************************************************/
19290
/****************************************************************************/
19291
/****************************************************************************/
19292
 
19293
/**
19294
 * Factory class for instantiating a BaseCellEditor subclass.
19295
 *
19296
 * @namespace YAHOO.widget
19297
 * @class CellEditor
19298
 * @extends YAHOO.widget.BaseCellEditor
19299
 * @constructor
19300
 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
19301
 * @param oConfigs {Object} (Optional) Object literal of configs.
19302
 */
19303
widget.CellEditor = function(sType, oConfigs) {
19304
    // Point to one of the subclasses
19305
    if(sType && DT.Editors[sType]) {
19306
        lang.augmentObject(BCE, DT.Editors[sType]);
19307
        return new DT.Editors[sType](oConfigs);
19308
    }
19309
    else {
19310
        return new BCE(null, oConfigs);
19311
    }
19312
};
19313
 
19314
var CE = widget.CellEditor;
19315
 
19316
// Copy static members to CellEditor class
19317
lang.augmentObject(CE, BCE);
19318
 
19319
 
19320
})();
19321
 
19322
YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.9.0", build: "2800"});
19323
 
19324
}, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-skin-sam-datatable", "yui2-element", "yui2-datasource"], "optional": ["yui2-skin-sam-paginator", "yui2-paginator", "yui2-dragdrop", "yui2-skin-sam-calendar", "yui2-calendar"]});