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
                return false;
360
            }
361
 
362
 
363
            if (type == "mouseenter" || type == "mouseleave") {
364
 
365
                if (!Event._createMouseDelegate) {
366
                    return false;
367
                }
368
 
369
                //    Look up the real event--either mouseover or mouseout
370
                sType = Event._getType(type);
371
 
372
                fnMouseDelegate = Event._createMouseDelegate(fn, obj, overrideContext);
373
 
374
                fnDelegate = Event._createDelegate(function (event, matchedEl, container) {
375
 
376
                    return fnMouseDelegate.call(matchedEl, event, container);
377
 
378
                }, filter, obj, overrideContext);
379
 
380
            }
381
            else {
382
 
383
                fnDelegate = Event._createDelegate(fn, filter, obj, overrideContext);
384
 
385
            }
386
 
387
            delegates.push([container, sType, fn, fnDelegate]);
388
 
389
            return Event.on(container, sType, fnDelegate);
390
 
391
        },
392
 
393
 
394
        /**
395
         * Removes a delegated event listener.
396
         *
397
         * @method removeDelegate
398
         *
399
         * @param {String|HTMLElement|Array|NodeList} container An id, an element
400
         *  reference, or a collection of ids and/or elements to remove
401
         *  the listener from.
402
         * @param {String} type The type of event to remove.
403
         * @param {Function} fn The method the event invokes.  If fn is
404
         *  undefined, then all event listeners for the type of event are
405
         *  removed.
406
         * @return {boolean} Returns true if the unbind was successful, false
407
         *  otherwise.
408
         * @static
409
         * @for Event
410
         */
411
        removeDelegate: function (container, type, fn) {
412
 
413
            var sType = type,
414
                returnVal = false,
415
                index,
416
                cacheItem;
417
 
418
            //    Look up the real event--either mouseover or mouseout
419
            if (type == "mouseenter" || type == "mouseleave") {
420
                sType = Event._getType(type);
421
            }
422
 
423
            index = Event._getCacheIndex(delegates, container, sType, fn);
424
 
425
            if (index >= 0) {
426
                cacheItem = delegates[index];
427
            }
428
 
429
 
430
            if (container && cacheItem) {
431
 
432
                returnVal = Event.removeListener(cacheItem[0], cacheItem[1], cacheItem[3]);
433
 
434
                if (returnVal) {
435
                    delete delegates[index][2];
436
                    delete delegates[index][3];
437
                    delegates.splice(index, 1);
438
                }
439
 
440
            }
441
 
442
            return returnVal;
443
 
444
        }
445
 
446
    });
447
 
448
}());
449
 
450
 
451
/**
452
 * Augments the Event Utility with support for the mouseenter and mouseleave
453
 * events:  A mouseenter event fires the first time the mouse enters an
454
 * element; a mouseleave event first the first time the mouse leaves an
455
 * element.
456
 *
457
 * @module event-mouseenter
458
 * @title Event Utility mouseenter and mouseout Module
459
 * @namespace YAHOO.util
460
 * @requires event
461
 */
462
 
463
(function () {
464
 
465
    var Event = YAHOO.util.Event,
466
        Lang = YAHOO.lang,
467
 
468
        addListener = Event.addListener,
469
        removeListener = Event.removeListener,
470
        getListeners = Event.getListeners,
471
 
472
        delegates = [],
473
 
474
        specialTypes = {
475
            mouseenter: "mouseover",
476
            mouseleave: "mouseout"
477
        },
478
 
479
        remove = function(el, type, fn) {
480
 
481
            var index = Event._getCacheIndex(delegates, el, type, fn),
482
                cacheItem,
483
                returnVal;
484
 
485
            if (index >= 0) {
486
                cacheItem = delegates[index];
487
            }
488
 
489
            if (el && cacheItem) {
490
 
491
                //    removeListener will translate the value of type
492
                returnVal = removeListener.call(Event, cacheItem[0], type, cacheItem[3]);
493
 
494
                if (returnVal) {
495
                    delete delegates[index][2];
496
                    delete delegates[index][3];
497
                    delegates.splice(index, 1);
498
                }
499
 
500
            }
501
 
502
            return returnVal;
503
 
504
        };
505
 
506
 
507
    Lang.augmentObject(Event._specialTypes, specialTypes);
508
 
509
    Lang.augmentObject(Event, {
510
 
511
        /**
512
         * Creates a delegate function used to call mouseover and mouseleave
513
         * event listeners specified via the
514
         * <code>YAHOO.util.Event.addListener</code>
515
         * or <code>YAHOO.util.Event.on</code> method.
516
         *
517
         * @method _createMouseDelegate
518
         *
519
         * @param {Function} fn        The method (event listener) to call
520
         * @param {Object}   obj    An arbitrary object that will be
521
         *                             passed as a parameter to the listener
522
         * @param {Boolean|object}  overrideContext  If true, the value of the
523
         *                             obj parameter becomes the execution context
524
         *                          of the listener. If an object, this object
525
         *                          becomes the execution context.
526
         * @return {Function} Function that will call the event listener
527
         * specified by either the <code>YAHOO.util.Event.addListener</code>
528
         * or <code>YAHOO.util.Event.on</code> method.
529
         * @private
530
         * @static
531
         * @for Event
532
         */
533
        _createMouseDelegate: function (fn, obj, overrideContext) {
534
 
535
            return function (event, container) {
536
 
537
                var el = this,
538
                    relatedTarget = Event.getRelatedTarget(event),
539
                    context,
540
                    args;
541
 
542
                if (el != relatedTarget && !YAHOO.util.Dom.isAncestor(el, relatedTarget)) {
543
 
544
                    context = el;
545
 
546
                    if (overrideContext) {
547
                        if (overrideContext === true) {
548
                            context = obj;
549
                        } else {
550
                            context = overrideContext;
551
                        }
552
                    }
553
 
554
                    // The default args passed back to a mouseenter or
555
                    // mouseleave listener are: the event, and any object
556
                    // the user passed when subscribing
557
 
558
                    args = [event, obj];
559
 
560
                    // Add the element and delegation container as arguments
561
                    // when delegating mouseenter and mouseleave
562
 
563
                    if (container) {
564
                        args.splice(1, 0, el, container);
565
                    }
566
 
567
                    return fn.apply(context, args);
568
 
569
                }
570
 
571
            };
572
 
573
        },
574
 
575
        addListener: function (el, type, fn, obj, overrideContext) {
576
 
577
            var fnDelegate,
578
                returnVal;
579
 
580
            if (specialTypes[type]) {
581
 
582
                fnDelegate = Event._createMouseDelegate(fn, obj, overrideContext);
583
 
584
                fnDelegate.mouseDelegate = true;
585
 
586
                delegates.push([el, type, fn, fnDelegate]);
587
 
588
                //    addListener will translate the value of type
589
                returnVal = addListener.call(Event, el, type, fnDelegate);
590
 
591
            }
592
            else {
593
                returnVal = addListener.apply(Event, arguments);
594
            }
595
 
596
            return returnVal;
597
 
598
        },
599
 
600
        removeListener: function (el, type, fn) {
601
 
602
            var returnVal;
603
 
604
            if (specialTypes[type]) {
605
                returnVal = remove.apply(Event, arguments);
606
            }
607
            else {
608
                returnVal = removeListener.apply(Event, arguments);
609
            }
610
 
611
            return returnVal;
612
 
613
        },
614
 
615
        getListeners: function (el, type) {
616
 
617
            //    If the user specified the type as mouseover or mouseout,
618
            //    need to filter out those used by mouseenter and mouseleave.
619
            //    If the user specified the type as mouseenter or mouseleave,
620
            //    need to filter out the true mouseover and mouseout listeners.
621
 
622
            var listeners = [],
623
                elListeners,
624
                bMouseOverOrOut = (type === "mouseover" || type === "mouseout"),
625
                bMouseDelegate,
626
                i,
627
                l;
628
 
629
            if (type && (bMouseOverOrOut || specialTypes[type])) {
630
 
631
                elListeners = getListeners.call(Event, el, this._getType(type));
632
 
633
                if (elListeners) {
634
 
635
                    for (i=elListeners.length-1; i>-1; i--) {
636
 
637
                        l = elListeners[i];
638
                        bMouseDelegate = l.fn.mouseDelegate;
639
 
640
                        if ((specialTypes[type] && bMouseDelegate) || (bMouseOverOrOut && !bMouseDelegate)) {
641
                            listeners.push(l);
642
                        }
643
 
644
                    }
645
 
646
                }
647
 
648
            }
649
            else {
650
                listeners = getListeners.apply(Event, arguments);
651
            }
652
 
653
            return (listeners && listeners.length) ? listeners : null;
654
 
655
        }
656
 
657
    }, true);
658
 
659
    Event.on = Event.addListener;
660
 
661
}());
662
YAHOO.register("event-mouseenter", YAHOO.util.Event, {version: "2.9.0", build: "2800"});
663
 
664
var Y = YAHOO,
665
    Y_DOM = YAHOO.util.Dom,
666
    EMPTY_ARRAY = [],
667
    Y_UA = Y.env.ua,
668
    Y_Lang = Y.lang,
669
    Y_DOC = document,
670
    Y_DOCUMENT_ELEMENT = Y_DOC.documentElement,
671
 
672
    Y_DOM_inDoc = Y_DOM.inDocument,
673
    Y_mix = Y_Lang.augmentObject,
674
    Y_guid = Y_DOM.generateId,
675
 
676
    Y_getDoc = function(element) {
677
        var doc = Y_DOC;
678
        if (element) {
679
            doc = (element.nodeType === 9) ? element : // element === document
680
                element.ownerDocument || // element === DOM node
681
                element.document || // element === window
682
                Y_DOC; // default
683
        }
684
 
685
        return doc;
686
    },
687
 
688
    Y_Array = function(o, startIdx) {
689
        var l, a, start = startIdx || 0;
690
 
691
        // IE errors when trying to slice HTMLElement collections
692
        try {
693
            return Array.prototype.slice.call(o, start);
694
        } catch (e) {
695
            a = [];
696
            l = o.length;
697
            for (; start < l; start++) {
698
                a.push(o[start]);
699
            }
700
            return a;
701
        }
702
    },
703
 
704
    Y_DOM_allById = function(id, root) {
705
        root = root || Y_DOC;
706
        var nodes = [],
707
            ret = [],
708
            i,
709
            node;
710
 
711
        if (root.querySelectorAll) {
712
            ret = root.querySelectorAll('[id="' + id + '"]');
713
        } else if (root.all) {
714
            nodes = root.all(id);
715
 
716
            if (nodes) {
717
                // root.all may return HTMLElement or HTMLCollection.
718
                // some elements are also HTMLCollection (FORM, SELECT).
719
                if (nodes.nodeName) {
720
                    if (nodes.id === id) { // avoid false positive on name
721
                        ret.push(nodes);
722
                        nodes = EMPTY_ARRAY; // done, no need to filter
723
                    } else { //  prep for filtering
724
                        nodes = [nodes];
725
                    }
726
                }
727
 
728
                if (nodes.length) {
729
                    // filter out matches on node.name
730
                    // and element.id as reference to element with id === 'id'
731
                    for (i = 0; node = nodes[i++];) {
732
                        if (node.id === id  ||
733
                                (node.attributes && node.attributes.id &&
734
                                node.attributes.id.value === id)) {
735
                            ret.push(node);
736
                        }
737
                    }
738
                }
739
            }
740
        } else {
741
            ret = [Y_getDoc(root).getElementById(id)];
742
        }
743
 
744
        return ret;
745
    };
746
 
747
/**
748
 * The selector-native module provides support for native querySelector
749
 * @module dom
750
 * @submodule selector-native
751
 * @for Selector
752
 */
753
 
754
/**
755
 * Provides support for using CSS selectors to query the DOM
756
 * @class Selector
757
 * @static
758
 * @for Selector
759
 */
760
 
761
var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
762
    OWNER_DOCUMENT = 'ownerDocument',
763
 
764
Selector = {
765
    _foundCache: [],
766
 
767
    useNative: true,
768
 
769
    _compare: ('sourceIndex' in Y_DOCUMENT_ELEMENT) ?
770
        function(nodeA, nodeB) {
771
            var a = nodeA.sourceIndex,
772
                b = nodeB.sourceIndex;
773
 
774
            if (a === b) {
775
                return 0;
776
            } else if (a > b) {
777
                return 1;
778
            }
779
 
780
            return -1;
781
 
782
        } : (Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION] ?
783
        function(nodeA, nodeB) {
784
            if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
785
                return -1;
786
            } else {
787
                return 1;
788
            }
789
        } :
790
        function(nodeA, nodeB) {
791
            var rangeA, rangeB, compare;
792
            if (nodeA && nodeB) {
793
                rangeA = nodeA[OWNER_DOCUMENT].createRange();
794
                rangeA.setStart(nodeA, 0);
795
                rangeB = nodeB[OWNER_DOCUMENT].createRange();
796
                rangeB.setStart(nodeB, 0);
797
                compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
798
            }
799
 
800
            return compare;
801
 
802
    }),
803
 
804
    _sort: function(nodes) {
805
        if (nodes) {
806
            nodes = Y_Array(nodes, 0, true);
807
            if (nodes.sort) {
808
                nodes.sort(Selector._compare);
809
            }
810
        }
811
 
812
        return nodes;
813
    },
814
 
815
    _deDupe: function(nodes) {
816
        var ret = [],
817
            i, node;
818
 
819
        for (i = 0; (node = nodes[i++]);) {
820
            if (!node._found) {
821
                ret[ret.length] = node;
822
                node._found = true;
823
            }
824
        }
825
 
826
        for (i = 0; (node = ret[i++]);) {
827
            node._found = null;
828
            node.removeAttribute('_found');
829
        }
830
 
831
        return ret;
832
    },
833
 
834
    /**
835
     * Retrieves a set of nodes based on a given CSS selector.
836
     * @method query
837
     *
838
     * @param {string} selector The CSS Selector to test the node against.
839
     * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
840
     * @param {Boolean} firstOnly optional Whether or not to return only the first match.
841
     * @return {Array} An array of nodes that match the given selector.
842
     * @static
843
     */
844
    query: function(selector, root, firstOnly, skipNative) {
845
        if (typeof root == 'string') {
846
            root = Y_DOM.get(root);
847
            if (!root) {
848
                return (firstOnly) ? null : [];
849
            }
850
        } else {
851
            root = root || Y_DOC;
852
        }
853
 
854
        var ret = [],
855
            useNative = (Selector.useNative && Y_DOC.querySelector && !skipNative),
856
            queries = [[selector, root]],
857
            query,
858
            result,
859
            i,
860
            fn = (useNative) ? Selector._nativeQuery : Selector._bruteQuery;
861
 
862
        if (selector && fn) {
863
            // split group into seperate queries
864
            if (!skipNative && // already done if skipping
865
                    (!useNative || root.tagName)) { // split native when element scoping is needed
866
                queries = Selector._splitQueries(selector, root);
867
            }
868
 
869
            for (i = 0; (query = queries[i++]);) {
870
                result = fn(query[0], query[1], firstOnly);
871
                if (!firstOnly) { // coerce DOM Collection to Array
872
                    result = Y_Array(result, 0, true);
873
                }
874
                if (result) {
875
                    ret = ret.concat(result);
876
                }
877
            }
878
 
879
            if (queries.length > 1) { // remove dupes and sort by doc order
880
                ret = Selector._sort(Selector._deDupe(ret));
881
            }
882
        }
883
 
884
        Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
885
        return (firstOnly) ? (ret[0] || null) : ret;
886
 
887
    },
888
 
889
    // allows element scoped queries to begin with combinator
890
    // e.g. query('> p', document.body) === query('body > p')
891
    _splitQueries: function(selector, node) {
892
        var groups = selector.split(','),
893
            queries = [],
894
            prefix = '',
895
            i, len;
896
 
897
        if (node) {
898
            // enforce for element scoping
899
            if (node.tagName) {
900
                node.id = node.id || Y_guid();
901
                prefix = '[id="' + node.id + '"] ';
902
            }
903
 
904
            for (i = 0, len = groups.length; i < len; ++i) {
905
                selector =  prefix + groups[i];
906
                queries.push([selector, node]);
907
            }
908
        }
909
 
910
        return queries;
911
    },
912
 
913
    _nativeQuery: function(selector, root, one) {
914
        if (Y_UA.webkit && selector.indexOf(':checked') > -1 &&
915
                (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
916
            return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
917
        }
918
        try {
919
            //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
920
            return root['querySelector' + (one ? '' : 'All')](selector);
921
        } catch(e) { // fallback to brute if available
922
            //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
923
            return Selector.query(selector, root, one, true); // redo with skipNative true
924
        }
925
    },
926
 
927
    filter: function(nodes, selector) {
928
        var ret = [],
929
            i, node;
930
 
931
        if (nodes && selector) {
932
            for (i = 0; (node = nodes[i++]);) {
933
                if (Selector.test(node, selector)) {
934
                    ret[ret.length] = node;
935
                }
936
            }
937
        } else {
938
            Y.log('invalid filter input (nodes: ' + nodes +
939
                    ', selector: ' + selector + ')', 'warn', 'Selector');
940
        }
941
 
942
        return ret;
943
    },
944
 
945
    test: function(node, selector, root) {
946
        var ret = false,
947
            groups = selector.split(','),
948
            useFrag = false,
949
            parent,
950
            item,
951
            items,
952
            frag,
953
            i, j, group;
954
 
955
        if (node && node.tagName) { // only test HTMLElements
956
 
957
            // we need a root if off-doc
958
            if (!root && !Y_DOM_inDoc(node)) {
959
                parent = node.parentNode;
960
                if (parent) {
961
                    root = parent;
962
                } else { // only use frag when no parent to query
963
                    frag = node[OWNER_DOCUMENT].createDocumentFragment();
964
                    frag.appendChild(node);
965
                    root = frag;
966
                    useFrag = true;
967
                }
968
            }
969
            root = root || node[OWNER_DOCUMENT];
970
 
971
            if (!node.id) {
972
                node.id = Y_guid();
973
            }
974
            for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
975
                group += '[id="' + node.id + '"]';
976
                items = Selector.query(group, root);
977
 
978
                for (j = 0; item = items[j++];) {
979
                    if (item === node) {
980
                        ret = true;
981
                        break;
982
                    }
983
                }
984
                if (ret) {
985
                    break;
986
                }
987
            }
988
 
989
            if (useFrag) { // cleanup
990
                frag.removeChild(node);
991
            }
992
        }
993
 
994
        return ret;
995
    }
996
 
997
};
998
 
999
YAHOO.util.Selector = Selector;
1000
/**
1001
 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
1002
 * @module dom
1003
 * @submodule selector-css2
1004
 * @for Selector
1005
 */
1006
 
1007
/**
1008
 * Provides helper methods for collecting and filtering DOM elements.
1009
 */
1010
 
1011
var PARENT_NODE = 'parentNode',
1012
    TAG_NAME = 'tagName',
1013
    ATTRIBUTES = 'attributes',
1014
    COMBINATOR = 'combinator',
1015
    PSEUDOS = 'pseudos',
1016
 
1017
    SelectorCSS2 = {
1018
        _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
1019
        SORT_RESULTS: true,
1020
        _children: function(node, tag) {
1021
            var ret = node.children,
1022
                i,
1023
                children = [],
1024
                childNodes,
1025
                child;
1026
 
1027
            if (node.children && tag && node.children.tags) {
1028
                children = node.children.tags(tag);
1029
            } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
1030
                childNodes = ret || node.childNodes;
1031
                ret = [];
1032
                for (i = 0; (child = childNodes[i++]);) {
1033
                    if (child.tagName) {
1034
                        if (!tag || tag === child.tagName) {
1035
                            ret.push(child);
1036
                        }
1037
                    }
1038
                }
1039
            }
1040
 
1041
            return ret || [];
1042
        },
1043
 
1044
        _re: {
1045
            //attr: /(\[.*\])/g,
1046
            attr: /(\[[^\]]*\])/g,
1047
            //esc: /\\[:\[][\w\d\]]*/gi,
1048
            esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
1049
            //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i
1050
            pseudos: /(\([^\)]*\))/g
1051
        },
1052
 
1053
        /**
1054
         * Mapping of shorthand tokens to corresponding attribute selector
1055
         * @property shorthand
1056
         * @type object
1057
         */
1058
        shorthand: {
1059
            //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]',
1060
            '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]',
1061
            //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]',
1062
            //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]'
1063
            '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
1064
        },
1065
 
1066
        /**
1067
         * List of operators and corresponding boolean functions.
1068
         * These functions are passed the attribute and the current node's value of the attribute.
1069
         * @property operators
1070
         * @type object
1071
         */
1072
        operators: {
1073
            '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute
1074
            //'': '.+',
1075
            //'=': '^{val}$', // equality
1076
            '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
1077
            '|=': '^{val}(?:-|$)' // optional hyphen-delimited
1078
        },
1079
 
1080
        pseudos: {
1081
           'first-child': function(node) {
1082
                return Selector._children(node[PARENT_NODE])[0] === node;
1083
            }
1084
        },
1085
 
1086
        _bruteQuery: function(selector, root, firstOnly) {
1087
            var ret = [],
1088
                nodes = [],
1089
                tokens = Selector._tokenize(selector),
1090
                token = tokens[tokens.length - 1],
1091
                rootDoc = Y_getDoc(root),
1092
                child,
1093
                id,
1094
                className,
1095
                tagName;
1096
 
1097
 
1098
            // if we have an initial ID, set to root when in document
1099
            /*
1100
            if (tokens[0] && rootDoc === root &&
1101
                    (id = tokens[0].id) &&
1102
                    rootDoc.getElementById(id)) {
1103
                root = rootDoc.getElementById(id);
1104
            }
1105
            */
1106
 
1107
            if (token) {
1108
                // prefilter nodes
1109
                id = token.id;
1110
                className = token.className;
1111
                tagName = token.tagName || '*';
1112
 
1113
                if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
1114
                    // try ID first, unless no root.all && root not in document
1115
                    // (root.all works off document, but not getElementById)
1116
                    // TODO: move to allById?
1117
                    if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) {
1118
                        nodes = Y_DOM_allById(id, root);
1119
                    // try className
1120
                    } else if (className) {
1121
                        nodes = root.getElementsByClassName(className);
1122
                    } else { // default to tagName
1123
                        nodes = root.getElementsByTagName(tagName);
1124
                    }
1125
 
1126
                } else { // brute getElementsByTagName('*')
1127
                    child = root.firstChild;
1128
                    while (child) {
1129
                        if (child.tagName) { // only collect HTMLElements
1130
                            nodes.push(child);
1131
                        }
1132
                        child = child.nextSilbing || child.firstChild;
1133
                    }
1134
                }
1135
                if (nodes.length) {
1136
                    ret = Selector._filterNodes(nodes, tokens, firstOnly);
1137
                }
1138
            }
1139
 
1140
            return ret;
1141
        },
1142
 
1143
        _filterNodes: function(nodes, tokens, firstOnly) {
1144
            var i = 0,
1145
                j,
1146
                len = tokens.length,
1147
                n = len - 1,
1148
                result = [],
1149
                node = nodes[0],
1150
                tmpNode = node,
1151
                getters = Selector.getters,
1152
                operator,
1153
                combinator,
1154
                token,
1155
                path,
1156
                pass,
1157
                //FUNCTION = 'function',
1158
                value,
1159
                tests,
1160
                test;
1161
 
1162
            //do {
1163
            for (i = 0; (tmpNode = node = nodes[i++]);) {
1164
                n = len - 1;
1165
                path = null;
1166
 
1167
                testLoop:
1168
                while (tmpNode && tmpNode.tagName) {
1169
                    token = tokens[n];
1170
                    tests = token.tests;
1171
                    j = tests.length;
1172
                    if (j && !pass) {
1173
                        while ((test = tests[--j])) {
1174
                            operator = test[1];
1175
                            if (getters[test[0]]) {
1176
                                value = getters[test[0]](tmpNode, test[0]);
1177
                            } else {
1178
                                value = tmpNode[test[0]];
1179
                                // use getAttribute for non-standard attributes
1180
                                if (value === undefined && tmpNode.getAttribute) {
1181
                                    value = tmpNode.getAttribute(test[0]);
1182
                                }
1183
                            }
1184
 
1185
                            if ((operator === '=' && value !== test[2]) ||  // fast path for equality
1186
                                (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
1187
                                operator.test && !operator.test(value)) ||  // regex test
1188
                                (!operator.test && // protect against RegExp as function (webkit)
1189
                                        typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
1190
 
1191
                                // skip non element nodes or non-matching tags
1192
                                if ((tmpNode = tmpNode[path])) {
1193
                                    while (tmpNode &&
1194
                                        (!tmpNode.tagName ||
1195
                                            (token.tagName && token.tagName !== tmpNode.tagName))
1196
                                    ) {
1197
                                        tmpNode = tmpNode[path];
1198
                                    }
1199
                                }
1200
                                continue testLoop;
1201
                            }
1202
                        }
1203
                    }
1204
 
1205
                    n--; // move to next token
1206
                    // now that we've passed the test, move up the tree by combinator
1207
                    if (!pass && (combinator = token.combinator)) {
1208
                        path = combinator.axis;
1209
                        tmpNode = tmpNode[path];
1210
 
1211
                        // skip non element nodes
1212
                        while (tmpNode && !tmpNode.tagName) {
1213
                            tmpNode = tmpNode[path];
1214
                        }
1215
 
1216
                        if (combinator.direct) { // one pass only
1217
                            path = null;
1218
                        }
1219
 
1220
                    } else { // success if we made it this far
1221
                        result.push(node);
1222
                        if (firstOnly) {
1223
                            return result;
1224
                        }
1225
                        break;
1226
                    }
1227
                }
1228
            }// while (tmpNode = node = nodes[++i]);
1229
            node = tmpNode = null;
1230
            return result;
1231
        },
1232
 
1233
        combinators: {
1234
            ' ': {
1235
                axis: 'parentNode'
1236
            },
1237
 
1238
            '>': {
1239
                axis: 'parentNode',
1240
                direct: true
1241
            },
1242
 
1243
 
1244
            '+': {
1245
                axis: 'previousSibling',
1246
                direct: true
1247
            }
1248
        },
1249
 
1250
        _parsers: [
1251
            {
1252
                name: ATTRIBUTES,
1253
                //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
1254
                re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
1255
                fn: function(match, token) {
1256
                    var operator = match[2] || '',
1257
                        operators = Selector.operators,
1258
                        escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
1259
                        test;
1260
 
1261
                    // add prefiltering for ID and CLASS
1262
                    if ((match[1] === 'id' && operator === '=') ||
1263
                            (match[1] === 'className' &&
1264
                            Y_DOCUMENT_ELEMENT.getElementsByClassName &&
1265
                            (operator === '~=' || operator === '='))) {
1266
                        token.prefilter = match[1];
1267
 
1268
 
1269
                        match[3] = escVal;
1270
 
1271
                        // escape all but ID for prefilter, which may run through QSA (via Dom.allById)
1272
                        token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
1273
 
1274
                    }
1275
 
1276
                    // add tests
1277
                    if (operator in operators) {
1278
                        test = operators[operator];
1279
                        if (typeof test === 'string') {
1280
                            match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
1281
                            test = new RegExp(test.replace('{val}', match[3]));
1282
                        }
1283
                        match[2] = test;
1284
                    }
1285
                    if (!token.last || token.prefilter !== match[1]) {
1286
                        return match.slice(1);
1287
                    }
1288
                }
1289
 
1290
            },
1291
            {
1292
                name: TAG_NAME,
1293
                re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
1294
                fn: function(match, token) {
1295
                    var tag = match[1].toUpperCase();
1296
                    token.tagName = tag;
1297
 
1298
                    if (tag !== '*' && (!token.last || token.prefilter)) {
1299
                        return [TAG_NAME, '=', tag];
1300
                    }
1301
                    if (!token.prefilter) {
1302
                        token.prefilter = 'tagName';
1303
                    }
1304
                }
1305
            },
1306
            {
1307
                name: COMBINATOR,
1308
                re: /^\s*([>+~]|\s)\s*/,
1309
                fn: function(match, token) {
1310
                }
1311
            },
1312
            {
1313
                name: PSEUDOS,
1314
                re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
1315
                fn: function(match, token) {
1316
                    var test = Selector[PSEUDOS][match[1]];
1317
                    if (test) { // reorder match array and unescape special chars for tests
1318
                        if (match[2]) {
1319
                            match[2] = match[2].replace(/\\/g, '');
1320
                        }
1321
                        return [match[2], test];
1322
                    } else { // selector token not supported (possibly missing CSS3 module)
1323
                        return false;
1324
                    }
1325
                }
1326
            }
1327
            ],
1328
 
1329
        _getToken: function(token) {
1330
            return {
1331
                tagName: null,
1332
                id: null,
1333
                className: null,
1334
                attributes: {},
1335
                combinator: null,
1336
                tests: []
1337
            };
1338
        },
1339
 
1340
        /**
1341
            Break selector into token units per simple selector.
1342
            Combinator is attached to the previous token.
1343
         */
1344
        _tokenize: function(selector) {
1345
            selector = selector || '';
1346
            selector = Selector._replaceShorthand(Y_Lang.trim(selector));
1347
            var token = Selector._getToken(),     // one token per simple selector (left selector holds combinator)
1348
                query = selector, // original query for debug report
1349
                tokens = [],    // array of tokens
1350
                found = false,  // whether or not any matches were found this pass
1351
                match,         // the regex match
1352
                test,
1353
                i, parser;
1354
 
1355
            /*
1356
                Search for selector patterns, store, and strip them from the selector string
1357
                until no patterns match (invalid selector) or we run out of chars.
1358
 
1359
                Multiple attributes and pseudos are allowed, in any order.
1360
                for example:
1361
                    'form:first-child[type=button]:not(button)[lang|=en]'
1362
            */
1363
 
1364
            outer:
1365
            do {
1366
                found = false; // reset after full pass
1367
 
1368
                for (i = 0; (parser = Selector._parsers[i++]);) {
1369
                    if ( (match = parser.re.exec(selector)) ) { // note assignment
1370
                        if (parser.name !== COMBINATOR ) {
1371
                            token.selector = selector;
1372
                        }
1373
                        selector = selector.replace(match[0], ''); // strip current match from selector
1374
                        if (!selector.length) {
1375
                            token.last = true;
1376
                        }
1377
 
1378
                        if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
1379
                            match[1] = Selector._attrFilters[match[1]];
1380
                        }
1381
 
1382
                        test = parser.fn(match, token);
1383
                        if (test === false) { // selector not supported
1384
                            found = false;
1385
                            break outer;
1386
                        } else if (test) {
1387
                            token.tests.push(test);
1388
                        }
1389
 
1390
                        if (!selector.length || parser.name === COMBINATOR) {
1391
                            tokens.push(token);
1392
                            token = Selector._getToken(token);
1393
                            if (parser.name === COMBINATOR) {
1394
                                token.combinator = Selector.combinators[match[1]];
1395
                            }
1396
                        }
1397
                        found = true;
1398
 
1399
 
1400
                    }
1401
                }
1402
            } while (found && selector.length);
1403
 
1404
            if (!found || selector.length) { // not fully parsed
1405
                Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
1406
                tokens = [];
1407
            }
1408
            return tokens;
1409
        },
1410
 
1411
        _replaceShorthand: function(selector) {
1412
            var shorthand = Selector.shorthand,
1413
                esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
1414
                attrs,
1415
                pseudos,
1416
                re, i, len;
1417
 
1418
            if (esc) {
1419
                selector = selector.replace(Selector._re.esc, '\uE000');
1420
            }
1421
 
1422
            attrs = selector.match(Selector._re.attr);
1423
            pseudos = selector.match(Selector._re.pseudos);
1424
 
1425
            if (attrs) {
1426
                selector = selector.replace(Selector._re.attr, '\uE001');
1427
            }
1428
 
1429
            if (pseudos) {
1430
                selector = selector.replace(Selector._re.pseudos, '\uE002');
1431
            }
1432
 
1433
 
1434
            for (re in shorthand) {
1435
                if (shorthand.hasOwnProperty(re)) {
1436
                    selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
1437
                }
1438
            }
1439
 
1440
            if (attrs) {
1441
                for (i = 0, len = attrs.length; i < len; ++i) {
1442
                    selector = selector.replace(/\uE001/, attrs[i]);
1443
                }
1444
            }
1445
 
1446
            if (pseudos) {
1447
                for (i = 0, len = pseudos.length; i < len; ++i) {
1448
                    selector = selector.replace(/\uE002/, pseudos[i]);
1449
                }
1450
            }
1451
 
1452
            selector = selector.replace(/\[/g, '\uE003');
1453
            selector = selector.replace(/\]/g, '\uE004');
1454
 
1455
            selector = selector.replace(/\(/g, '\uE005');
1456
            selector = selector.replace(/\)/g, '\uE006');
1457
 
1458
            if (esc) {
1459
                for (i = 0, len = esc.length; i < len; ++i) {
1460
                    selector = selector.replace('\uE000', esc[i]);
1461
                }
1462
            }
1463
 
1464
            return selector;
1465
        },
1466
 
1467
        _attrFilters: {
1468
            'class': 'className',
1469
            'for': 'htmlFor'
1470
        },
1471
 
1472
        getters: {
1473
            href: function(node, attr) {
1474
                return Y_DOM.getAttribute(node, attr);
1475
            }
1476
        }
1477
    };
1478
 
1479
Y_mix(Selector, SelectorCSS2, true);
1480
Selector.getters.src = Selector.getters.rel = Selector.getters.href;
1481
 
1482
// IE wants class with native queries
1483
if (Selector.useNative && Y_DOC.querySelector) {
1484
    Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]';
1485
}
1486
 
1487
/**
1488
 * The selector css3 module provides support for css3 selectors.
1489
 * @module dom
1490
 * @submodule selector-css3
1491
 * @for Selector
1492
 */
1493
 
1494
/*
1495
    an+b = get every _a_th node starting at the _b_th
1496
    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
1497
    1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
1498
    an+0 = get every _a_th element, "0" may be omitted
1499
*/
1500
 
1501
Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
1502
 
1503
Selector._getNth = function(node, expr, tag, reverse) {
1504
    Selector._reNth.test(expr);
1505
    var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
1506
        n = RegExp.$2, // "n"
1507
        oddeven = RegExp.$3, // "odd" or "even"
1508
        b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
1509
        result = [],
1510
        siblings = Selector._children(node.parentNode, tag),
1511
        op;
1512
 
1513
    if (oddeven) {
1514
        a = 2; // always every other
1515
        op = '+';
1516
        n = 'n';
1517
        b = (oddeven === 'odd') ? 1 : 0;
1518
    } else if ( isNaN(a) ) {
1519
        a = (n) ? 1 : 0; // start from the first or no repeat
1520
    }
1521
 
1522
    if (a === 0) { // just the first
1523
        if (reverse) {
1524
            b = siblings.length - b + 1;
1525
        }
1526
 
1527
        if (siblings[b - 1] === node) {
1528
            return true;
1529
        } else {
1530
            return false;
1531
        }
1532
 
1533
    } else if (a < 0) {
1534
        reverse = !!reverse;
1535
        a = Math.abs(a);
1536
    }
1537
 
1538
    if (!reverse) {
1539
        for (var i = b - 1, len = siblings.length; i < len; i += a) {
1540
            if ( i >= 0 && siblings[i] === node ) {
1541
                return true;
1542
            }
1543
        }
1544
    } else {
1545
        for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
1546
            if ( i < len && siblings[i] === node ) {
1547
                return true;
1548
            }
1549
        }
1550
    }
1551
    return false;
1552
};
1553
 
1554
Y_mix(Selector.pseudos, {
1555
    'root': function(node) {
1556
        return node === node.ownerDocument.documentElement;
1557
    },
1558
 
1559
    'nth-child': function(node, expr) {
1560
        return Selector._getNth(node, expr);
1561
    },
1562
 
1563
    'nth-last-child': function(node, expr) {
1564
        return Selector._getNth(node, expr, null, true);
1565
    },
1566
 
1567
    'nth-of-type': function(node, expr) {
1568
        return Selector._getNth(node, expr, node.tagName);
1569
    },
1570
 
1571
    'nth-last-of-type': function(node, expr) {
1572
        return Selector._getNth(node, expr, node.tagName, true);
1573
    },
1574
 
1575
    'last-child': function(node) {
1576
        var children = Selector._children(node.parentNode);
1577
        return children[children.length - 1] === node;
1578
    },
1579
 
1580
    'first-of-type': function(node) {
1581
        return Selector._children(node.parentNode, node.tagName)[0] === node;
1582
    },
1583
 
1584
    'last-of-type': function(node) {
1585
        var children = Selector._children(node.parentNode, node.tagName);
1586
        return children[children.length - 1] === node;
1587
    },
1588
 
1589
    'only-child': function(node) {
1590
        var children = Selector._children(node.parentNode);
1591
        return children.length === 1 && children[0] === node;
1592
    },
1593
 
1594
    'only-of-type': function(node) {
1595
        var children = Selector._children(node.parentNode, node.tagName);
1596
        return children.length === 1 && children[0] === node;
1597
    },
1598
 
1599
    'empty': function(node) {
1600
        return node.childNodes.length === 0;
1601
    },
1602
 
1603
    'not': function(node, expr) {
1604
        return !Selector.test(node, expr);
1605
    },
1606
 
1607
    'contains': function(node, expr) {
1608
        var text = node.innerText || node.textContent || '';
1609
        return text.indexOf(expr) > -1;
1610
    },
1611
 
1612
    'checked': function(node) {
1613
        return (node.checked === true || node.selected === true);
1614
    },
1615
 
1616
    enabled: function(node) {
1617
        return (node.disabled !== undefined && !node.disabled);
1618
    },
1619
 
1620
    disabled: function(node) {
1621
        return (node.disabled);
1622
    }
1623
});
1624
 
1625
Y_mix(Selector.operators, {
1626
    '^=': '^{val}', // Match starts with value
1627
    '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value
1628
    '$=': '{val}$', // Match ends with value
1629
    '*=': '{val}' // Match contains value as substring
1630
});
1631
 
1632
Selector.combinators['~'] = {
1633
    axis: 'previousSibling'
1634
};
1635
YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"});
1636
 
1637
 
1638
 
1639
/****************************************************************************/
1640
/****************************************************************************/
1641
/****************************************************************************/
1642
 
1643
var Dom = YAHOO.util.Dom;
1644
 
1645
/**
1646
 * The ColumnSet class defines and manages a DataTable's Columns,
1647
 * including nested hierarchies and access to individual Column instances.
1648
 *
1649
 * @namespace YAHOO.widget
1650
 * @class ColumnSet
1651
 * @uses YAHOO.util.EventProvider
1652
 * @constructor
1653
 * @param aDefinitions {Object[]} Array of object literals that define cells in
1654
 * the THEAD.
1655
 */
1656
YAHOO.widget.ColumnSet = function(aDefinitions) {
1657
    this._sId = Dom.generateId(null, "yui-cs"); // "yui-cs" + YAHOO.widget.ColumnSet._nCount;
1658
 
1659
    // First clone the defs
1660
    aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions);
1661
    this._init(aDefinitions);
1662
 
1663
    YAHOO.widget.ColumnSet._nCount++;
1664
};
1665
 
1666
/////////////////////////////////////////////////////////////////////////////
1667
//
1668
// Private member variables
1669
//
1670
/////////////////////////////////////////////////////////////////////////////
1671
 
1672
/**
1673
 * Internal class variable to index multiple ColumnSet instances.
1674
 *
1675
 * @property ColumnSet._nCount
1676
 * @type Number
1677
 * @private
1678
 * @static
1679
 */
1680
YAHOO.widget.ColumnSet._nCount = 0;
1681
 
1682
YAHOO.widget.ColumnSet.prototype = {
1683
    /**
1684
     * Unique instance name.
1685
     *
1686
     * @property _sId
1687
     * @type String
1688
     * @private
1689
     */
1690
    _sId : null,
1691
 
1692
    /**
1693
     * Array of object literal Column definitions passed to the constructor.
1694
     *
1695
     * @property _aDefinitions
1696
     * @type Object[]
1697
     * @private
1698
     */
1699
    _aDefinitions : null,
1700
 
1701
    /////////////////////////////////////////////////////////////////////////////
1702
    //
1703
    // Public member variables
1704
    //
1705
    /////////////////////////////////////////////////////////////////////////////
1706
 
1707
    /**
1708
     * Top-down tree representation of Column hierarchy.
1709
     *
1710
     * @property tree
1711
     * @type YAHOO.widget.Column[]
1712
     */
1713
    tree : null,
1714
 
1715
    /**
1716
     * Flattened representation of all Columns.
1717
     *
1718
     * @property flat
1719
     * @type YAHOO.widget.Column[]
1720
     * @default []
1721
     */
1722
    flat : null,
1723
 
1724
    /**
1725
     * Array of Columns that map one-to-one to a table column.
1726
     *
1727
     * @property keys
1728
     * @type YAHOO.widget.Column[]
1729
     * @default []
1730
     */
1731
    keys : null,
1732
 
1733
    /**
1734
     * ID index of nested parent hierarchies for HEADERS accessibility attribute.
1735
     *
1736
     * @property headers
1737
     * @type String[]
1738
     * @default []
1739
     */
1740
    headers : null,
1741
 
1742
    /////////////////////////////////////////////////////////////////////////////
1743
    //
1744
    // Private methods
1745
    //
1746
    /////////////////////////////////////////////////////////////////////////////
1747
 
1748
    /**
1749
     * Initializes ColumnSet instance with data from Column definitions.
1750
     *
1751
     * @method _init
1752
     * @param aDefinitions {Object[]} Array of object literals that define cells in
1753
     * the THEAD .
1754
     * @private
1755
     */
1756
 
1757
    _init : function(aDefinitions) {
1758
        // DOM tree representation of all Columns
1759
        var tree = [];
1760
        // Flat representation of all Columns
1761
        var flat = [];
1762
        // Flat representation of only Columns that are meant to display data
1763
        var keys = [];
1764
        // Array of HEADERS attribute values for all keys in the "keys" array
1765
        var headers = [];
1766
 
1767
        // Tracks current node list depth being tracked
1768
        var nodeDepth = -1;
1769
 
1770
        // Internal recursive function to define Column instances
1771
        var parseColumns = function(nodeList, parent) {
1772
            // One level down
1773
            nodeDepth++;
1774
 
1775
            // Create corresponding tree node if not already there for this depth
1776
            if(!tree[nodeDepth]) {
1777
                tree[nodeDepth] = [];
1778
            }
1779
 
1780
 
1781
            // Parse each node at this depth for attributes and any children
1782
            for(var j=0; j<nodeList.length; j++) {
1783
                var currentNode = nodeList[j];
1784
 
1785
                // Instantiate a new Column for each node
1786
                var oColumn = new YAHOO.widget.Column(currentNode);
1787
 
1788
                // Cross-reference Column ID back to the original object literal definition
1789
                currentNode.yuiColumnId = oColumn._sId;
1790
 
1791
                // Add the new Column to the flat list
1792
                flat.push(oColumn);
1793
 
1794
                // Assign its parent as an attribute, if applicable
1795
                if(parent) {
1796
                    oColumn._oParent = parent;
1797
                }
1798
 
1799
                // The Column has descendants
1800
                if(YAHOO.lang.isArray(currentNode.children)) {
1801
                    oColumn.children = currentNode.children;
1802
 
1803
                    // Determine COLSPAN value for this Column
1804
                    var terminalChildNodes = 0;
1805
                    var countTerminalChildNodes = function(ancestor) {
1806
                        var descendants = ancestor.children;
1807
                        // Drill down each branch and count terminal nodes
1808
                        for(var k=0; k<descendants.length; k++) {
1809
                            // Keep drilling down
1810
                            if(YAHOO.lang.isArray(descendants[k].children)) {
1811
                                countTerminalChildNodes(descendants[k]);
1812
                            }
1813
                            // Reached branch terminus
1814
                            else {
1815
                                terminalChildNodes++;
1816
                            }
1817
                        }
1818
                    };
1819
                    countTerminalChildNodes(currentNode);
1820
                    oColumn._nColspan = terminalChildNodes;
1821
 
1822
                    // Cascade certain properties to children if not defined on their own
1823
                    var currentChildren = currentNode.children;
1824
                    for(var k=0; k<currentChildren.length; k++) {
1825
                        var child = currentChildren[k];
1826
                        if(oColumn.className && (child.className === undefined)) {
1827
                            child.className = oColumn.className;
1828
                        }
1829
                        if(oColumn.editor && (child.editor === undefined)) {
1830
                            child.editor = oColumn.editor;
1831
                        }
1832
                        //TODO: Deprecated
1833
                        if(oColumn.editorOptions && (child.editorOptions === undefined)) {
1834
                            child.editorOptions = oColumn.editorOptions;
1835
                        }
1836
                        if(oColumn.formatter && (child.formatter === undefined)) {
1837
                            child.formatter = oColumn.formatter;
1838
                        }
1839
                        if(oColumn.resizeable && (child.resizeable === undefined)) {
1840
                            child.resizeable = oColumn.resizeable;
1841
                        }
1842
                        if(oColumn.sortable && (child.sortable === undefined)) {
1843
                            child.sortable = oColumn.sortable;
1844
                        }
1845
                        if(oColumn.hidden) {
1846
                            child.hidden = true;
1847
                        }
1848
                        if(oColumn.width && (child.width === undefined)) {
1849
                            child.width = oColumn.width;
1850
                        }
1851
                        if(oColumn.minWidth && (child.minWidth === undefined)) {
1852
                            child.minWidth = oColumn.minWidth;
1853
                        }
1854
                        if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) {
1855
                            child.maxAutoWidth = oColumn.maxAutoWidth;
1856
                        }
1857
                        // Backward compatibility
1858
                        if(oColumn.type && (child.type === undefined)) {
1859
                            child.type = oColumn.type;
1860
                        }
1861
                        if(oColumn.type && !oColumn.formatter) {
1862
                            oColumn.formatter = oColumn.type;
1863
                        }
1864
                        if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
1865
                            oColumn.label = oColumn.text;
1866
                        }
1867
                        if(oColumn.parser) {
1868
                        }
1869
                        if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
1870
                                (oColumn.sortOptions.descFunction))) {
1871
                        }
1872
                    }
1873
 
1874
                    // The children themselves must also be parsed for Column instances
1875
                    if(!tree[nodeDepth+1]) {
1876
                        tree[nodeDepth+1] = [];
1877
                    }
1878
                    parseColumns(currentChildren, oColumn);
1879
                }
1880
                // This Column does not have any children
1881
                else {
1882
                    oColumn._nKeyIndex = keys.length;
1883
                    oColumn._nColspan = 1;
1884
                    keys.push(oColumn);
1885
                }
1886
 
1887
                // Add the Column to the top-down tree
1888
                tree[nodeDepth].push(oColumn);
1889
            }
1890
            nodeDepth--;
1891
        };
1892
 
1893
        // Parse out Column instances from the array of object literals
1894
        if(YAHOO.lang.isArray(aDefinitions)) {
1895
            parseColumns(aDefinitions);
1896
 
1897
            // Store the array
1898
            this._aDefinitions = aDefinitions;
1899
        }
1900
        else {
1901
            return null;
1902
        }
1903
 
1904
        var i;
1905
 
1906
        // Determine ROWSPAN value for each Column in the tree
1907
        var parseTreeForRowspan = function(tree) {
1908
            var maxRowDepth = 1;
1909
            var currentRow;
1910
            var currentColumn;
1911
 
1912
            // Calculate the max depth of descendants for this row
1913
            var countMaxRowDepth = function(row, tmpRowDepth) {
1914
                tmpRowDepth = tmpRowDepth || 1;
1915
 
1916
                for(var n=0; n<row.length; n++) {
1917
                    var col = row[n];
1918
                    // Column has children, so keep counting
1919
                    if(YAHOO.lang.isArray(col.children)) {
1920
                        tmpRowDepth++;
1921
                        countMaxRowDepth(col.children, tmpRowDepth);
1922
                        tmpRowDepth--;
1923
                    }
1924
                    // No children, is it the max depth?
1925
                    else {
1926
                        if(tmpRowDepth > maxRowDepth) {
1927
                            maxRowDepth = tmpRowDepth;
1928
                        }
1929
                    }
1930
 
1931
                }
1932
            };
1933
 
1934
            // Count max row depth for each row
1935
            for(var m=0; m<tree.length; m++) {
1936
                currentRow = tree[m];
1937
                countMaxRowDepth(currentRow);
1938
 
1939
                // Assign the right ROWSPAN values to each Column in the row
1940
                for(var p=0; p<currentRow.length; p++) {
1941
                    currentColumn = currentRow[p];
1942
                    if(!YAHOO.lang.isArray(currentColumn.children)) {
1943
                        currentColumn._nRowspan = maxRowDepth;
1944
                    }
1945
                    else {
1946
                        currentColumn._nRowspan = 1;
1947
                    }
1948
                }
1949
 
1950
                // Reset counter for next row
1951
                maxRowDepth = 1;
1952
            }
1953
        };
1954
        parseTreeForRowspan(tree);
1955
 
1956
        // Store tree index values
1957
        for(i=0; i<tree[0].length; i++) {
1958
            tree[0][i]._nTreeIndex = i;
1959
        }
1960
 
1961
        // Store header relationships in an array for HEADERS attribute
1962
        var recurseAncestorsForHeaders = function(i, oColumn) {
1963
            headers[i].push(oColumn.getSanitizedKey());
1964
            if(oColumn._oParent) {
1965
                recurseAncestorsForHeaders(i, oColumn._oParent);
1966
            }
1967
        };
1968
        for(i=0; i<keys.length; i++) {
1969
            headers[i] = [];
1970
            recurseAncestorsForHeaders(i, keys[i]);
1971
            headers[i] = headers[i].reverse();
1972
        }
1973
 
1974
        // Save to the ColumnSet instance
1975
        this.tree = tree;
1976
        this.flat = flat;
1977
        this.keys = keys;
1978
        this.headers = headers;
1979
    },
1980
 
1981
    /////////////////////////////////////////////////////////////////////////////
1982
    //
1983
    // Public methods
1984
    //
1985
    /////////////////////////////////////////////////////////////////////////////
1986
 
1987
    /**
1988
     * Returns unique name of the ColumnSet instance.
1989
     *
1990
     * @method getId
1991
     * @return {String} Unique name of the ColumnSet instance.
1992
     */
1993
 
1994
    getId : function() {
1995
        return this._sId;
1996
    },
1997
 
1998
    /**
1999
     * ColumnSet instance name, for logging.
2000
     *
2001
     * @method toString
2002
     * @return {String} Unique name of the ColumnSet instance.
2003
     */
2004
 
2005
    toString : function() {
2006
        return "ColumnSet instance " + this._sId;
2007
    },
2008
 
2009
    /**
2010
     * Public accessor to the definitions array.
2011
     *
2012
     * @method getDefinitions
2013
     * @return {Object[]} Array of object literal Column definitions.
2014
     */
2015
 
2016
    getDefinitions : function() {
2017
        var aDefinitions = this._aDefinitions;
2018
 
2019
        // Internal recursive function to define Column instances
2020
        var parseColumns = function(nodeList, oSelf) {
2021
            // Parse each node at this depth for attributes and any children
2022
            for(var j=0; j<nodeList.length; j++) {
2023
                var currentNode = nodeList[j];
2024
 
2025
                // Get the Column for each node
2026
                var oColumn = oSelf.getColumnById(currentNode.yuiColumnId);
2027
 
2028
                if(oColumn) {
2029
                    // Update the current values
2030
                    var oDefinition = oColumn.getDefinition();
2031
                    for(var name in oDefinition) {
2032
                        if(YAHOO.lang.hasOwnProperty(oDefinition, name)) {
2033
                            currentNode[name] = oDefinition[name];
2034
                        }
2035
                    }
2036
                }
2037
 
2038
                // The Column has descendants
2039
                if(YAHOO.lang.isArray(currentNode.children)) {
2040
                    // The children themselves must also be parsed for Column instances
2041
                    parseColumns(currentNode.children, oSelf);
2042
                }
2043
            }
2044
        };
2045
 
2046
        parseColumns(aDefinitions, this);
2047
        this._aDefinitions = aDefinitions;
2048
        return aDefinitions;
2049
    },
2050
 
2051
    /**
2052
     * Returns Column instance with given ID.
2053
     *
2054
     * @method getColumnById
2055
     * @param column {String} Column ID.
2056
     * @return {YAHOO.widget.Column} Column instance.
2057
     */
2058
 
2059
    getColumnById : function(column) {
2060
        if(YAHOO.lang.isString(column)) {
2061
            var allColumns = this.flat;
2062
            for(var i=allColumns.length-1; i>-1; i--) {
2063
                if(allColumns[i]._sId === column) {
2064
                    return allColumns[i];
2065
                }
2066
            }
2067
        }
2068
        return null;
2069
    },
2070
 
2071
    /**
2072
     * Returns Column instance with given key or ColumnSet key index.
2073
     *
2074
     * @method getColumn
2075
     * @param column {String | Number} Column key or ColumnSet key index.
2076
     * @return {YAHOO.widget.Column} Column instance.
2077
     */
2078
 
2079
    getColumn : function(column) {
2080
        if(YAHOO.lang.isNumber(column) && this.keys[column]) {
2081
            return this.keys[column];
2082
        }
2083
        else if(YAHOO.lang.isString(column)) {
2084
            var allColumns = this.flat;
2085
            var aColumns = [];
2086
            for(var i=0; i<allColumns.length; i++) {
2087
                if(allColumns[i].key === column) {
2088
                    aColumns.push(allColumns[i]);
2089
                }
2090
            }
2091
            if(aColumns.length === 1) {
2092
                return aColumns[0];
2093
            }
2094
            else if(aColumns.length > 1) {
2095
                return aColumns;
2096
            }
2097
        }
2098
        return null;
2099
    },
2100
 
2101
    /**
2102
     * Public accessor returns array of given Column's desendants (if any), including itself.
2103
     *
2104
     * @method getDescendants
2105
     * @parem {YAHOO.widget.Column} Column instance.
2106
     * @return {Array} Array including the Column itself and all descendants (if any).
2107
     */
2108
    getDescendants : function(oColumn) {
2109
        var oSelf = this;
2110
        var allDescendants = [];
2111
        var i;
2112
 
2113
        // Recursive function to loop thru all children
2114
        var parse = function(oParent) {
2115
            allDescendants.push(oParent);
2116
            // This Column has children
2117
            if(oParent.children) {
2118
                for(i=0; i<oParent.children.length; i++) {
2119
                    parse(oSelf.getColumn(oParent.children[i].key));
2120
                }
2121
            }
2122
        };
2123
        parse(oColumn);
2124
 
2125
        return allDescendants;
2126
    }
2127
};
2128
 
2129
/****************************************************************************/
2130
/****************************************************************************/
2131
/****************************************************************************/
2132
 
2133
/**
2134
 * The Column class defines and manages attributes of DataTable Columns
2135
 *
2136
 * @namespace YAHOO.widget
2137
 * @class Column
2138
 * @constructor
2139
 * @param oConfigs {Object} Object literal of definitions.
2140
 */
2141
YAHOO.widget.Column = function(oConfigs) {
2142
    this._sId = Dom.generateId(null, "yui-col"); // "yui-col" + YAHOO.widget.Column._nCount;
2143
 
2144
    // Object literal defines Column attributes
2145
    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
2146
        for(var sConfig in oConfigs) {
2147
            if(sConfig) {
2148
                this[sConfig] = oConfigs[sConfig];
2149
            }
2150
        }
2151
    }
2152
 
2153
    // Assign a key if not found
2154
    if(!YAHOO.lang.isValue(this.key)) {
2155
        this.key = Dom.generateId(null, "yui-dt-col"); //"yui-dt-col" + YAHOO.widget.Column._nCount;
2156
    }
2157
 
2158
    // Assign a field if not found, defaults to key
2159
    if(!YAHOO.lang.isValue(this.field)) {
2160
        this.field = this.key;
2161
    }
2162
 
2163
    // Increment counter
2164
    YAHOO.widget.Column._nCount++;
2165
 
2166
    // Backward compatibility
2167
    if(this.width && !YAHOO.lang.isNumber(this.width)) {
2168
        this.width = null;
2169
    }
2170
    if(this.editor && YAHOO.lang.isString(this.editor)) {
2171
        this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions);
2172
    }
2173
};
2174
 
2175
/////////////////////////////////////////////////////////////////////////////
2176
//
2177
// Private member variables
2178
//
2179
/////////////////////////////////////////////////////////////////////////////
2180
 
2181
YAHOO.lang.augmentObject(YAHOO.widget.Column, {
2182
    /**
2183
     * Internal class variable to index multiple Column instances.
2184
     *
2185
     * @property Column._nCount
2186
     * @type Number
2187
     * @private
2188
     * @static
2189
     */
2190
    _nCount : 0,
2191
 
2192
    formatCheckbox : function(elCell, oRecord, oColumn, oData) {
2193
        YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
2194
    },
2195
 
2196
    formatCurrency : function(elCell, oRecord, oColumn, oData) {
2197
        YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
2198
    },
2199
 
2200
    formatDate : function(elCell, oRecord, oColumn, oData) {
2201
        YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
2202
    },
2203
 
2204
    formatEmail : function(elCell, oRecord, oColumn, oData) {
2205
        YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
2206
    },
2207
 
2208
    formatLink : function(elCell, oRecord, oColumn, oData) {
2209
        YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
2210
    },
2211
 
2212
    formatNumber : function(elCell, oRecord, oColumn, oData) {
2213
        YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
2214
    },
2215
 
2216
    formatSelect : function(elCell, oRecord, oColumn, oData) {
2217
        YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
2218
    }
2219
});
2220
 
2221
YAHOO.widget.Column.prototype = {
2222
    /**
2223
     * Unique String identifier assigned at instantiation.
2224
     *
2225
     * @property _sId
2226
     * @type String
2227
     * @private
2228
     */
2229
    _sId : null,
2230
 
2231
    /**
2232
     * Reference to Column's current position index within its ColumnSet's keys
2233
     * array, if applicable. This property only applies to non-nested and bottom-
2234
     * level child Columns.
2235
     *
2236
     * @property _nKeyIndex
2237
     * @type Number
2238
     * @private
2239
     */
2240
    _nKeyIndex : null,
2241
 
2242
    /**
2243
     * Reference to Column's current position index within its ColumnSet's tree
2244
     * array, if applicable. This property only applies to non-nested and top-
2245
     * level parent Columns.
2246
     *
2247
     * @property _nTreeIndex
2248
     * @type Number
2249
     * @private
2250
     */
2251
    _nTreeIndex : null,
2252
 
2253
    /**
2254
     * Number of table cells the Column spans.
2255
     *
2256
     * @property _nColspan
2257
     * @type Number
2258
     * @private
2259
     */
2260
    _nColspan : 1,
2261
 
2262
    /**
2263
     * Number of table rows the Column spans.
2264
     *
2265
     * @property _nRowspan
2266
     * @type Number
2267
     * @private
2268
     */
2269
    _nRowspan : 1,
2270
 
2271
    /**
2272
     * Column's parent Column instance, or null.
2273
     *
2274
     * @property _oParent
2275
     * @type YAHOO.widget.Column
2276
     * @private
2277
     */
2278
    _oParent : null,
2279
 
2280
    /**
2281
     * The DOM reference to the associated TH element.
2282
     *
2283
     * @property _elTh
2284
     * @type HTMLElement
2285
     * @private
2286
     */
2287
    _elTh : null,
2288
 
2289
    /**
2290
     * The DOM reference to the associated TH element's liner DIV element.
2291
     *
2292
     * @property _elThLiner
2293
     * @type HTMLElement
2294
     * @private
2295
     */
2296
    _elThLiner : null,
2297
 
2298
    /**
2299
     * The DOM reference to the associated TH element's label SPAN element.
2300
     *
2301
     * @property _elThLabel
2302
     * @type HTMLElement
2303
     * @private
2304
     */
2305
    _elThLabel : null,
2306
 
2307
    /**
2308
     * The DOM reference to the associated resizerelement (if any).
2309
     *
2310
     * @property _elResizer
2311
     * @type HTMLElement
2312
     * @private
2313
     */
2314
    _elResizer : null,
2315
 
2316
    /**
2317
     * Internal width tracker.
2318
     *
2319
     * @property _nWidth
2320
     * @type Number
2321
     * @private
2322
     */
2323
    _nWidth : null,
2324
 
2325
    /**
2326
     * For unreg() purposes, a reference to the Column's DragDrop instance.
2327
     *
2328
     * @property _dd
2329
     * @type YAHOO.util.DragDrop
2330
     * @private
2331
     */
2332
    _dd : null,
2333
 
2334
    /**
2335
     * For unreg() purposes, a reference to the Column resizer's DragDrop instance.
2336
     *
2337
     * @property _ddResizer
2338
     * @type YAHOO.util.DragDrop
2339
     * @private
2340
     */
2341
    _ddResizer : null,
2342
 
2343
    /////////////////////////////////////////////////////////////////////////////
2344
    //
2345
    // Public member variables
2346
    //
2347
    /////////////////////////////////////////////////////////////////////////////
2348
 
2349
    /**
2350
     * Unique name, required. If "label" property is not provided, the "key"
2351
     * value will be treated as markup and inserted into the DOM as innerHTML.
2352
     *
2353
     * @property key
2354
     * @type String|HTML
2355
     */
2356
    key : null,
2357
 
2358
    /**
2359
     * Associated database field, or null.
2360
     *
2361
     * @property field
2362
     * @type String
2363
     */
2364
    field : null,
2365
 
2366
    /**
2367
     * Value displayed as Column header in the TH element. String value is
2368
     * treated as markup and inserted into the DOM as innerHTML.
2369
     *
2370
     * @property label
2371
     * @type HTML
2372
     */
2373
    label : null,
2374
 
2375
    /**
2376
     * Column head cell ABBR for accessibility.
2377
     *
2378
     * @property abbr
2379
     * @type String
2380
     */
2381
    abbr : null,
2382
 
2383
    /**
2384
     * Array of object literals that define children (nested headers) of a Column.
2385
     *
2386
     * @property children
2387
     * @type Object[]
2388
     */
2389
    children : null,
2390
 
2391
    /**
2392
     * Column width (in pixels).
2393
     *
2394
     * @property width
2395
     * @type Number
2396
     */
2397
    width : null,
2398
 
2399
    /**
2400
     * Minimum Column width (in pixels).
2401
     *
2402
     * @property minWidth
2403
     * @type Number
2404
     * @default null
2405
     */
2406
    minWidth : null,
2407
 
2408
    /**
2409
     * When a width is not defined for a Column, maxAutoWidth defines an upper
2410
     * limit that the Column should be auto-sized to. If resizeable is enabled,
2411
     * users may still resize to a greater width. Most useful for Columns intended
2412
     * to hold long unbroken, unwrapped Strings, such as URLs, to prevent very
2413
     * wide Columns from disrupting visual readability by inducing truncation.
2414
     *
2415
     * @property maxAutoWidth
2416
     * @type Number
2417
     * @default null
2418
     */
2419
    maxAutoWidth : null,
2420
 
2421
    /**
2422
     * True if Column is in hidden state.
2423
     *
2424
     * @property hidden
2425
     * @type Boolean
2426
     * @default false
2427
     */
2428
    hidden : false,
2429
 
2430
    /**
2431
     * True if Column is in selected state.
2432
     *
2433
     * @property selected
2434
     * @type Boolean
2435
     * @default false
2436
     */
2437
    selected : false,
2438
 
2439
    /**
2440
     * Custom CSS class or array of classes to be applied to every cell in the Column.
2441
     *
2442
     * @property className
2443
     * @type String || String[]
2444
     */
2445
    className : null,
2446
 
2447
    /**
2448
     * Cell formatter function, or a shortcut pointer to a function in the
2449
     * DataTable.Formatter object. The function, called from the DataTable's
2450
     * formatCell method, renders markup into the cell liner
2451
     * element and accepts the following arguments:
2452
     * <dl>
2453
     *    <dt>elLiner</dt>
2454
     *    <dd>The element to write innerHTML to.</dd>
2455
     *    <dt>oRecord</dt>
2456
     *    <dd>The associated Record for the row.</dd>
2457
     *    <dt>oColumn</dt>
2458
     *    <dd>The Column instance for the cell.</dd>
2459
     *    <dt>oData</dt>
2460
     *    <dd>The data value for the cell.</dd>
2461
     * </dl>
2462
     *
2463
     * @property formatter
2464
     * @type String || HTMLFunction
2465
     */
2466
    formatter : null,
2467
 
2468
    /**
2469
     * Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
2470
     *
2471
     * @property currencyOptions
2472
     * @type Object
2473
     * @default null
2474
     */
2475
    currencyOptions : null,
2476
 
2477
    /**
2478
     * Config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
2479
     *
2480
     * @property dateOptions
2481
     * @type Object
2482
     * @default null
2483
     */
2484
    dateOptions : null,
2485
 
2486
    /**
2487
     * Array of dropdown values for formatter:"dropdown" cases. Can either be a
2488
     * simple array (e.g., ["Alabama","Alaska","Arizona","Arkansas"]) or a an
2489
     * array of objects (e.g., [{label:"Alabama", value:"AL"},
2490
     * {label:"Alaska", value:"AK"}, {label:"Arizona", value:"AZ"},
2491
     * {label:"Arkansas", value:"AR"}]). String values are treated as markup and
2492
     * inserted into the DOM as innerHTML.
2493
     *
2494
     * @property dropdownOptions
2495
     * @type HTML[] | Object[]
2496
     */
2497
    dropdownOptions : null,
2498
 
2499
    /**
2500
     * A CellEditor instance, otherwise Column is not editable.
2501
     *
2502
     * @property editor
2503
     * @type YAHOO.widget.CellEditor
2504
     */
2505
    editor : null,
2506
 
2507
    /**
2508
     * True if Column is resizeable, false otherwise. The Drag & Drop Utility is
2509
     * required to enable this feature. Only bottom-level and non-nested Columns are
2510
     * resizeble.
2511
     *
2512
     * @property resizeable
2513
     * @type Boolean
2514
     * @default false
2515
     */
2516
    resizeable : false,
2517
 
2518
    /**
2519
     * True if Column is sortable, false otherwise.
2520
     *
2521
     * @property sortable
2522
     * @type Boolean
2523
     * @default false
2524
     */
2525
    sortable : false,
2526
 
2527
    /**
2528
     * @property sortOptions.defaultOrder
2529
     * @deprecated Use sortOptions.defaultDir.
2530
     */
2531
    /**
2532
     * Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC.
2533
     *
2534
     * @property sortOptions.defaultDir
2535
     * @type String
2536
     * @default null
2537
     */
2538
    /**
2539
     * Custom field to sort on.
2540
     *
2541
     * @property sortOptions.field
2542
     * @type String
2543
     * @default null
2544
     */
2545
    /**
2546
     * Custom sort handler. Signature: sortFunction(a, b, desc, field) where field is the sortOptions.field value
2547
     *
2548
     * @property sortOptions.sortFunction
2549
     * @type Function
2550
     * @default null
2551
     */
2552
    sortOptions : null,
2553
 
2554
 
2555
 
2556
 
2557
 
2558
 
2559
 
2560
 
2561
 
2562
 
2563
 
2564
 
2565
 
2566
 
2567
 
2568
    /////////////////////////////////////////////////////////////////////////////
2569
    //
2570
    // Public methods
2571
    //
2572
    /////////////////////////////////////////////////////////////////////////////
2573
 
2574
    /**
2575
     * Returns unique ID string.
2576
     *
2577
     * @method getId
2578
     * @return {String} Unique ID string.
2579
     */
2580
    getId : function() {
2581
        return this._sId;
2582
    },
2583
 
2584
    /**
2585
     * Column instance name, for logging.
2586
     *
2587
     * @method toString
2588
     * @return {String} Column's unique name.
2589
     */
2590
    toString : function() {
2591
        return "Column instance " + this._sId;
2592
    },
2593
 
2594
    /**
2595
     * Returns object literal definition.
2596
     *
2597
     * @method getDefinition
2598
     * @return {Object} Object literal definition.
2599
     */
2600
    getDefinition : function() {
2601
        var oDefinition = {};
2602
 
2603
        // Update the definition
2604
        oDefinition.abbr = this.abbr;
2605
        oDefinition.className = this.className;
2606
        oDefinition.editor = this.editor;
2607
        oDefinition.editorOptions = this.editorOptions; //TODO: deprecated
2608
        oDefinition.field = this.field;
2609
        oDefinition.formatter = this.formatter;
2610
        oDefinition.hidden = this.hidden;
2611
        oDefinition.key = this.key;
2612
        oDefinition.label = this.label;
2613
        oDefinition.minWidth = this.minWidth;
2614
        oDefinition.maxAutoWidth = this.maxAutoWidth;
2615
        oDefinition.resizeable = this.resizeable;
2616
        oDefinition.selected = this.selected;
2617
        oDefinition.sortable = this.sortable;
2618
        oDefinition.sortOptions = this.sortOptions;
2619
        oDefinition.width = this.width;
2620
 
2621
        // Bug 2529147
2622
        oDefinition._calculatedWidth = this._calculatedWidth;
2623
 
2624
        return oDefinition;
2625
    },
2626
 
2627
    /**
2628
     * Returns unique Column key.
2629
     *
2630
     * @method getKey
2631
     * @return {String} Column key.
2632
     */
2633
    getKey : function() {
2634
        return this.key;
2635
    },
2636
 
2637
    /**
2638
     * Returns field.
2639
     *
2640
     * @method getField
2641
     * @return {String} Column field.
2642
     */
2643
    getField : function() {
2644
        return this.field;
2645
    },
2646
 
2647
    /**
2648
     * Returns Column key which has been sanitized for DOM (class and ID) usage
2649
     * starts with letter, contains only letters, numbers, hyphen, or period.
2650
     *
2651
     * @method getSanitizedKey
2652
     * @return {String} Sanitized Column key.
2653
     */
2654
    getSanitizedKey : function() {
2655
        return this.getKey().replace(/[^\w\-]/g,"");
2656
    },
2657
 
2658
    /**
2659
     * Public accessor returns Column's current position index within its
2660
     * ColumnSet's keys array, if applicable. Only non-nested and bottom-level
2661
     * child Columns will return a value.
2662
     *
2663
     * @method getKeyIndex
2664
     * @return {Number} Position index, or null.
2665
     */
2666
    getKeyIndex : function() {
2667
        return this._nKeyIndex;
2668
    },
2669
 
2670
    /**
2671
     * Public accessor returns Column's current position index within its
2672
     * ColumnSet's tree array, if applicable. Only non-nested and top-level parent
2673
     * Columns will return a value;
2674
     *
2675
     * @method getTreeIndex
2676
     * @return {Number} Position index, or null.
2677
     */
2678
    getTreeIndex : function() {
2679
        return this._nTreeIndex;
2680
    },
2681
 
2682
    /**
2683
     * Public accessor returns Column's parent instance if any, or null otherwise.
2684
     *
2685
     * @method getParent
2686
     * @return {YAHOO.widget.Column} Column's parent instance.
2687
     */
2688
    getParent : function() {
2689
        return this._oParent;
2690
    },
2691
 
2692
    /**
2693
     * Public accessor returns Column's calculated COLSPAN value.
2694
     *
2695
     * @method getColspan
2696
     * @return {Number} Column's COLSPAN value.
2697
     */
2698
    getColspan : function() {
2699
        return this._nColspan;
2700
    },
2701
    // Backward compatibility
2702
    getColSpan : function() {
2703
        return this.getColspan();
2704
    },
2705
 
2706
    /**
2707
     * Public accessor returns Column's calculated ROWSPAN value.
2708
     *
2709
     * @method getRowspan
2710
     * @return {Number} Column's ROWSPAN value.
2711
     */
2712
    getRowspan : function() {
2713
        return this._nRowspan;
2714
    },
2715
 
2716
    /**
2717
     * Returns DOM reference to the key TH element.
2718
     *
2719
     * @method getThEl
2720
     * @return {HTMLElement} TH element.
2721
     */
2722
    getThEl : function() {
2723
        return this._elTh;
2724
    },
2725
 
2726
    /**
2727
     * Returns DOM reference to the TH's liner DIV element. Introduced since
2728
     * resizeable Columns may have an extra resizer liner, making the DIV liner
2729
     * not reliably the TH element's first child.
2730
     *
2731
     * @method getThLInerEl
2732
     * @return {HTMLElement} TH element.
2733
     */
2734
    getThLinerEl : function() {
2735
        return this._elThLiner;
2736
    },
2737
 
2738
    /**
2739
     * Returns DOM reference to the resizer element, or null.
2740
     *
2741
     * @method getResizerEl
2742
     * @return {HTMLElement} DIV element.
2743
     */
2744
    getResizerEl : function() {
2745
        return this._elResizer;
2746
    },
2747
 
2748
    // Backward compatibility
2749
    /**
2750
     * @method getColEl
2751
     * @deprecated Use getThEl
2752
     */
2753
    getColEl : function() {
2754
        return this.getThEl();
2755
    },
2756
    getIndex : function() {
2757
        return this.getKeyIndex();
2758
    },
2759
    format : function() {
2760
    }
2761
};
2762
 
2763
/****************************************************************************/
2764
/****************************************************************************/
2765
/****************************************************************************/
2766
 
2767
/**
2768
 * Sort static utility to support Column sorting.
2769
 *
2770
 * @namespace YAHOO.util
2771
 * @class Sort
2772
 * @static
2773
 */
2774
YAHOO.util.Sort = {
2775
    /////////////////////////////////////////////////////////////////////////////
2776
    //
2777
    // Public methods
2778
    //
2779
    /////////////////////////////////////////////////////////////////////////////
2780
 
2781
    /**
2782
     * Comparator function for simple case-insensitive string sorting.
2783
     *
2784
     * @method compare
2785
     * @param a {Object} First sort argument.
2786
     * @param b {Object} Second sort argument.
2787
     * @param desc {Boolean} True if sort direction is descending, false if
2788
     * sort direction is ascending.
2789
     * @return {Boolean} Return -1 when a < b. Return 0 when a = b.
2790
     * Return 1 when a > b.
2791
     */
2792
    compare: function(a, b, desc) {
2793
        if((a === null) || (typeof a == "undefined")) {
2794
            if((b === null) || (typeof b == "undefined")) {
2795
                return 0;
2796
            }
2797
            else {
2798
                return 1;
2799
            }
2800
        }
2801
        else if((b === null) || (typeof b == "undefined")) {
2802
            return -1;
2803
        }
2804
 
2805
        if(a.constructor == String) {
2806
            a = a.toLowerCase();
2807
        }
2808
        if(b.constructor == String) {
2809
            b = b.toLowerCase();
2810
        }
2811
        if(a < b) {
2812
            return (desc) ? 1 : -1;
2813
        }
2814
        else if (a > b) {
2815
            return (desc) ? -1 : 1;
2816
        }
2817
        else {
2818
            return 0;
2819
        }
2820
    }
2821
};
2822
 
2823
/****************************************************************************/
2824
/****************************************************************************/
2825
/****************************************************************************/
2826
 
2827
/**
2828
 * ColumnDD subclasses DragDrop to support rearrangeable Columns.
2829
 *
2830
 * @namespace YAHOO.util
2831
 * @class ColumnDD
2832
 * @extends YAHOO.util.DDProxy
2833
 * @constructor
2834
 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
2835
 * @param oColumn {YAHOO.widget.Column} Column instance.
2836
 * @param elTh {HTMLElement} TH element reference.
2837
 * @param elTarget {HTMLElement} Drag target element.
2838
 */
2839
YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) {
2840
    if(oDataTable && oColumn && elTh && elTarget) {
2841
        this.datatable = oDataTable;
2842
        this.table = oDataTable.getTableEl();
2843
        this.column = oColumn;
2844
        this.headCell = elTh;
2845
        this.pointer = elTarget;
2846
        this.newIndex = null;
2847
        this.init(elTh);
2848
        this.initFrame(); // Needed for DDProxy
2849
        this.invalidHandleTypes = {};
2850
 
2851
        // Set top/bottom padding to account for children of nested columns
2852
        this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0);
2853
 
2854
        YAHOO.util.Event.on(window, 'resize', function() {
2855
            this.initConstraints();
2856
        }, this, true);
2857
    }
2858
    else {
2859
    }
2860
};
2861
 
2862
if(YAHOO.util.DDProxy) {
2863
    YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, {
2864
        initConstraints: function() {
2865
            //Get the top, right, bottom and left positions
2866
            var region = YAHOO.util.Dom.getRegion(this.table),
2867
                //Get the element we are working on
2868
                el = this.getEl(),
2869
                //Get the xy position of it
2870
                xy = YAHOO.util.Dom.getXY(el),
2871
                //Get the width and height
2872
                width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10),
2873
                height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10),
2874
                //Set left to x minus left
2875
                left = ((xy[0] - region.left) + 15), //Buffer of 15px
2876
                //Set right to right minus x minus width
2877
                right = ((region.right - xy[0] - width) + 15);
2878
 
2879
            //Set the constraints based on the above calculations
2880
            this.setXConstraint(left, right);
2881
            this.setYConstraint(10, 10);
2882
        },
2883
        _resizeProxy: function() {
2884
            YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this, arguments);
2885
            var dragEl = this.getDragEl(),
2886
                el = this.getEl();
2887
 
2888
            YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px');
2889
            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block');
2890
            var xy = YAHOO.util.Dom.getXY(el);
2891
            YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
2892
 
2893
            YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px");
2894
            YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
2895
            YAHOO.util.Dom.setXY(this.dragEl, xy);
2896
        },
2897
        onMouseDown: function() {
2898
                this.initConstraints();
2899
                this.resetConstraints();
2900
        },
2901
        clickValidator: function(e) {
2902
            if(!this.column.hidden) {
2903
                var target = YAHOO.util.Event.getTarget(e);
2904
                return ( this.isValidHandleChild(target) &&
2905
                            (this.id == this.handleElId ||
2906
                                this.DDM.handleWasClicked(target, this.id)) );
2907
            }
2908
        },
2909
        onDragOver: function(ev, id) {
2910
            // Validate target as a Column
2911
            var target = this.datatable.getColumn(id);
2912
            if(target) {
2913
                // Validate target as a top-level parent
2914
                var targetIndex = target.getTreeIndex();
2915
                while((targetIndex === null) && target.getParent()) {
2916
                    target = target.getParent();
2917
                    targetIndex = target.getTreeIndex();
2918
                }
2919
                if(targetIndex !== null) {
2920
                    // Are we placing to left or right of target?
2921
                    var elTarget = target.getThEl();
2922
                    var newIndex = targetIndex;
2923
                    var mouseX = YAHOO.util.Event.getPageX(ev),
2924
                        targetX = YAHOO.util.Dom.getX(elTarget),
2925
                        midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2),
2926
                        currentIndex =  this.column.getTreeIndex();
2927
 
2928
                    if (mouseX < midX) {
2929
                       YAHOO.util.Dom.setX(this.pointer, targetX);
2930
                    } else {
2931
                        var targetWidth = parseInt(elTarget.offsetWidth, 10);
2932
                        YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth));
2933
                        newIndex++;
2934
                    }
2935
                    if (targetIndex > currentIndex) {
2936
                        newIndex--;
2937
                    }
2938
                    if(newIndex < 0) {
2939
                        newIndex = 0;
2940
                    }
2941
                    else if(newIndex > this.datatable.getColumnSet().tree[0].length) {
2942
                        newIndex = this.datatable.getColumnSet().tree[0].length;
2943
                    }
2944
                    this.newIndex = newIndex;
2945
                }
2946
            }
2947
        },
2948
        onDragDrop: function() {
2949
            this.datatable.reorderColumn(this.column, this.newIndex);
2950
        },
2951
        endDrag: function() {
2952
            this.newIndex = null;
2953
            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none');
2954
        }
2955
    });
2956
}
2957
 
2958
/****************************************************************************/
2959
/****************************************************************************/
2960
/****************************************************************************/
2961
 
2962
/**
2963
 * ColumnResizer subclasses DragDrop to support resizeable Columns.
2964
 *
2965
 * @namespace YAHOO.util
2966
 * @class ColumnResizer
2967
 * @extends YAHOO.util.DDProxy
2968
 * @constructor
2969
 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
2970
 * @param oColumn {YAHOO.widget.Column} Column instance.
2971
 * @param elTh {HTMLElement} TH element reference.
2972
 * @param sHandleElId {String} DOM ID of the handle element that causes the resize.
2973
 * @param elProxy {HTMLElement} Resizer proxy element.
2974
 */
2975
YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) {
2976
    if(oDataTable && oColumn && elTh && sHandleId) {
2977
        this.datatable = oDataTable;
2978
        this.column = oColumn;
2979
        this.headCell = elTh;
2980
        this.headCellLiner = oColumn.getThLinerEl();
2981
        this.resizerLiner = elTh.firstChild;
2982
        this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id});
2983
        this.initFrame(); // Needed for proxy
2984
        this.resetResizerEl(); // Needed when rowspan > 0
2985
 
2986
        // Set right padding for bug 1858462
2987
        this.setPadding(0, 1, 0, 0);
2988
    }
2989
    else {
2990
    }
2991
};
2992
 
2993
if(YAHOO.util.DD) {
2994
    YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, {
2995
        /////////////////////////////////////////////////////////////////////////////
2996
        //
2997
        // Public methods
2998
        //
2999
        /////////////////////////////////////////////////////////////////////////////
3000
        /**
3001
         * Resets resizer element.
3002
         *
3003
         * @method resetResizerEl
3004
         */
3005
        resetResizerEl : function() {
3006
            var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style;
3007
            resizerStyle.left = "auto";
3008
            resizerStyle.right = 0;
3009
            resizerStyle.top = "auto";
3010
            resizerStyle.bottom = 0;
3011
            resizerStyle.height = this.headCell.offsetHeight+"px";
3012
        },
3013
 
3014
        /////////////////////////////////////////////////////////////////////////////
3015
        //
3016
        // Public DOM event handlers
3017
        //
3018
        /////////////////////////////////////////////////////////////////////////////
3019
 
3020
        /**
3021
         * Handles mouseup events on the Column resizer.
3022
         *
3023
         * @method onMouseUp
3024
         * @param e {string} The mouseup event
3025
         */
3026
        onMouseUp : function(e) {
3027
            // Reset height of all resizer els in case TH's have changed height
3028
            var allKeys = this.datatable.getColumnSet().keys,
3029
                col;
3030
            for(var i=0, len=allKeys.length; i<len; i++) {
3031
                col = allKeys[i];
3032
                if(col._ddResizer) {
3033
                    col._ddResizer.resetResizerEl();
3034
                }
3035
            }
3036
            this.resetResizerEl();
3037
 
3038
            var el = this.headCellLiner;
3039
            var newWidth = el.offsetWidth -
3040
                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) -
3041
                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0);
3042
 
3043
            this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth});
3044
        },
3045
 
3046
        /**
3047
         * Handles mousedown events on the Column resizer.
3048
         *
3049
         * @method onMouseDown
3050
         * @param e {string} The mousedown event
3051
         */
3052
        onMouseDown : function(e) {
3053
            this.startWidth = this.headCellLiner.offsetWidth;
3054
            this.startX = YAHOO.util.Event.getXY(e)[0];
3055
            this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) +
3056
                    (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
3057
        },
3058
 
3059
        /**
3060
         * Custom clickValidator to ensure Column is not in hidden state.
3061
         *
3062
         * @method clickValidator
3063
         * @param {Event} e
3064
         * @private
3065
         */
3066
        clickValidator : function(e) {
3067
            if(!this.column.hidden) {
3068
                var target = YAHOO.util.Event.getTarget(e);
3069
                return ( this.isValidHandleChild(target) &&
3070
                            (this.id == this.handleElId ||
3071
                                this.DDM.handleWasClicked(target, this.id)) );
3072
            }
3073
        },
3074
 
3075
        /**
3076
         * Handles start drag on the Column resizer.
3077
         *
3078
         * @method startDrag
3079
         * @param e {string} The drag event
3080
         */
3081
        startDrag : function() {
3082
            // Shrinks height of all resizer els to not hold open TH els
3083
            var allKeys = this.datatable.getColumnSet().keys,
3084
                thisKey = this.column.getKeyIndex(),
3085
                col;
3086
            for(var i=0, len=allKeys.length; i<len; i++) {
3087
                col = allKeys[i];
3088
                if(col._ddResizer) {
3089
                    YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em";
3090
                }
3091
            }
3092
        },
3093
 
3094
        /**
3095
         * Handles drag events on the Column resizer.
3096
         *
3097
         * @method onDrag
3098
         * @param e {string} The drag event
3099
         */
3100
        onDrag : function(e) {
3101
            var newX = YAHOO.util.Event.getXY(e)[0];
3102
            if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) {
3103
                var offsetX = newX - this.startX;
3104
                var newWidth = this.startWidth + offsetX - this.nLinerPadding;
3105
                if(newWidth > 0) {
3106
                    this.datatable.setColumnWidth(this.column, newWidth);
3107
                }
3108
            }
3109
        }
3110
    });
3111
}
3112
 
3113
/////////////////////////////////////////////////////////////////////////////
3114
//
3115
// Deprecated
3116
//
3117
/////////////////////////////////////////////////////////////////////////////
3118
 
3119
/**
3120
 * @property editorOptions
3121
 * @deprecated Pass configs directly to CellEditor constructor.
3122
 */
3123
 
3124
 
3125
(function () {
3126
 
3127
var lang   = YAHOO.lang,
3128
    util   = YAHOO.util,
3129
    widget = YAHOO.widget,
3130
 
3131
    Dom    = util.Dom,
3132
    Ev     = util.Event,
3133
    DT     = widget.DataTable;
3134
 
3135
/****************************************************************************/
3136
/****************************************************************************/
3137
/****************************************************************************/
3138
 
3139
/**
3140
 * A RecordSet defines and manages a set of Records.
3141
 *
3142
 * @namespace YAHOO.widget
3143
 * @class RecordSet
3144
 * @param data {Object || Object[]} An object literal or an array of data.
3145
 * @constructor
3146
 */
3147
YAHOO.widget.RecordSet = function(data) {
3148
    this._init(data);
3149
};
3150
 
3151
var RS = widget.RecordSet;
3152
 
3153
/**
3154
 * Internal class variable to name multiple Recordset instances.
3155
 *
3156
 * @property RecordSet._nCount
3157
 * @type Number
3158
 * @private
3159
 * @static
3160
 */
3161
RS._nCount = 0;
3162
 
3163
RS.prototype = {
3164
 
3165
    /////////////////////////////////////////////////////////////////////////////
3166
    //
3167
    // Private member variables
3168
    //
3169
    /////////////////////////////////////////////////////////////////////////////
3170
    /**
3171
     * Unique String identifier assigned at instantiation.
3172
     *
3173
     * @property _sId
3174
     * @type String
3175
     * @private
3176
     */
3177
    _sId : null,
3178
 
3179
    /**
3180
     * Internal counter of how many Records are in the RecordSet.
3181
     *
3182
     * @property _length
3183
     * @type Number
3184
     * @private
3185
     * @deprecated No longer used
3186
     */
3187
    //_length : null,
3188
 
3189
    /////////////////////////////////////////////////////////////////////////////
3190
    //
3191
    // Private methods
3192
    //
3193
    /////////////////////////////////////////////////////////////////////////////
3194
 
3195
    /**
3196
     * Initializer.
3197
     *
3198
     * @method _init
3199
     * @param data {Object || Object[]} An object literal or an array of data.
3200
     * @private
3201
     */
3202
    _init : function(data) {
3203
        // Internal variables
3204
        this._sId = Dom.generateId(null, "yui-rs");// "yui-rs" + widget.RecordSet._nCount;
3205
        widget.RecordSet._nCount++;
3206
        this._records = [];
3207
        //this._length = 0;
3208
 
3209
        this._initEvents();
3210
 
3211
        if(data) {
3212
            if(lang.isArray(data)) {
3213
                this.addRecords(data);
3214
            }
3215
            else if(lang.isObject(data)) {
3216
                this.addRecord(data);
3217
            }
3218
        }
3219
 
3220
    },
3221
 
3222
    /**
3223
     * Initializes custom events.
3224
     *
3225
     * @method _initEvents
3226
     * @private
3227
     */
3228
    _initEvents : function() {
3229
        this.createEvent("recordAddEvent");
3230
        this.createEvent("recordsAddEvent");
3231
        this.createEvent("recordSetEvent");
3232
        this.createEvent("recordsSetEvent");
3233
        this.createEvent("recordUpdateEvent");
3234
        this.createEvent("recordDeleteEvent");
3235
        this.createEvent("recordsDeleteEvent");
3236
        this.createEvent("resetEvent");
3237
        this.createEvent("recordValueUpdateEvent");
3238
    },
3239
 
3240
    /**
3241
     * Adds one Record to the RecordSet at the given index. If index is null,
3242
     * then adds the Record to the end of the RecordSet.
3243
     *
3244
     * @method _addRecord
3245
     * @param oData {Object} An object literal of data.
3246
     * @param index {Number} (optional) Position index.
3247
     * @return {YAHOO.widget.Record} A Record instance.
3248
     * @private
3249
     */
3250
    _addRecord : function(oData, index) {
3251
        var oRecord = new YAHOO.widget.Record(oData);
3252
 
3253
        if(YAHOO.lang.isNumber(index) && (index > -1)) {
3254
            this._records.splice(index,0,oRecord);
3255
        }
3256
        else {
3257
            //index = this.getLength();
3258
            //this._records[index] = oRecord;
3259
            this._records[this._records.length] = oRecord;
3260
        }
3261
        //this._length++;
3262
        return oRecord;
3263
    },
3264
 
3265
    /**
3266
     * Sets/replaces one Record to the RecordSet at the given index.  Existing
3267
     * Records with higher indexes are not shifted.  If no index specified, the
3268
     * Record is added to the end of the RecordSet.
3269
     *
3270
     * @method _setRecord
3271
     * @param oData {Object} An object literal of data.
3272
     * @param index {Number} (optional) Position index.
3273
     * @return {YAHOO.widget.Record} A Record instance.
3274
     * @private
3275
     */
3276
    _setRecord : function(oData, index) {
3277
        if (!lang.isNumber(index) || index < 0) {
3278
            index = this._records.length;
3279
        }
3280
        return (this._records[index] = new widget.Record(oData));
3281
        /*
3282
        if(lang.isNumber(index) && (index > -1)) {
3283
            this._records[index] = oRecord;
3284
            if((index+1) > this.getLength()) {
3285
                this._length = index+1;
3286
            }
3287
        }
3288
        else {
3289
            this._records[this.getLength()] = oRecord;
3290
            this._length++;
3291
        }
3292
        return oRecord;
3293
        */
3294
    },
3295
 
3296
    /**
3297
     * Deletes Records from the RecordSet at the given index. If range is null,
3298
     * then only one Record is deleted.
3299
     *
3300
     * @method _deleteRecord
3301
     * @param index {Number} Position index.
3302
     * @param range {Number} (optional) How many Records to delete
3303
     * @private
3304
     */
3305
    _deleteRecord : function(index, range) {
3306
        if(!lang.isNumber(range) || (range < 0)) {
3307
            range = 1;
3308
        }
3309
        this._records.splice(index, range);
3310
        //this._length = this._length - range;
3311
    },
3312
 
3313
    /////////////////////////////////////////////////////////////////////////////
3314
    //
3315
    // Public methods
3316
    //
3317
    /////////////////////////////////////////////////////////////////////////////
3318
 
3319
    /**
3320
     * Returns unique name of the RecordSet instance.
3321
     *
3322
     * @method getId
3323
     * @return {String} Unique name of the RecordSet instance.
3324
     */
3325
    getId : function() {
3326
        return this._sId;
3327
    },
3328
 
3329
    /**
3330
     * Public accessor to the unique name of the RecordSet instance.
3331
     *
3332
     * @method toString
3333
     * @return {String} Unique name of the RecordSet instance.
3334
     */
3335
    toString : function() {
3336
        return "RecordSet instance " + this._sId;
3337
    },
3338
 
3339
    /**
3340
     * Returns the number of Records held in the RecordSet.
3341
     *
3342
     * @method getLength
3343
     * @return {Number} Number of records in the RecordSet.
3344
     */
3345
    getLength : function() {
3346
            //return this._length;
3347
            return this._records.length;
3348
    },
3349
 
3350
    /**
3351
     * Returns Record by ID or RecordSet position index.
3352
     *
3353
     * @method getRecord
3354
     * @param record {YAHOO.widget.Record | Number | String} Record instance,
3355
     * RecordSet position index, or Record ID.
3356
     * @return {YAHOO.widget.Record} Record object.
3357
     */
3358
    getRecord : function(record) {
3359
        var i;
3360
        if(record instanceof widget.Record) {
3361
            for(i=0; i<this._records.length; i++) {
3362
                if(this._records[i] && (this._records[i]._sId === record._sId)) {
3363
                    return record;
3364
                }
3365
            }
3366
        }
3367
        else if(lang.isNumber(record)) {
3368
            if((record > -1) && (record < this.getLength())) {
3369
                return this._records[record];
3370
            }
3371
        }
3372
        else if(lang.isString(record)) {
3373
            for(i=0; i<this._records.length; i++) {
3374
                if(this._records[i] && (this._records[i]._sId === record)) {
3375
                    return this._records[i];
3376
                }
3377
            }
3378
        }
3379
        // Not a valid Record for this RecordSet
3380
        return null;
3381
 
3382
    },
3383
 
3384
    /**
3385
     * Returns an array of Records from the RecordSet.
3386
     *
3387
     * @method getRecords
3388
     * @param index {Number} (optional) Recordset position index of which Record to
3389
     * start at.
3390
     * @param range {Number} (optional) Number of Records to get.
3391
     * @return {YAHOO.widget.Record[]} Array of Records starting at given index and
3392
     * length equal to given range. If index is not given, all Records are returned.
3393
     */
3394
    getRecords : function(index, range) {
3395
        if(!lang.isNumber(index)) {
3396
            return this._records;
3397
        }
3398
        if(!lang.isNumber(range)) {
3399
            return this._records.slice(index);
3400
        }
3401
        return this._records.slice(index, index+range);
3402
    },
3403
 
3404
    /**
3405
     * Returns a boolean indicating whether Records exist in the RecordSet at the
3406
     * specified index range.  Returns true if and only if a Record exists at each
3407
     * index in the range.
3408
     * @method hasRecords
3409
     * @param index
3410
     * @param range
3411
     * @return {Boolean} true if all indices are populated in the RecordSet
3412
     */
3413
    hasRecords : function (index, range) {
3414
        var recs = this.getRecords(index,range);
3415
        for (var i = 0; i < range; ++i) {
3416
            if (typeof recs[i] === 'undefined') {
3417
                return false;
3418
            }
3419
        }
3420
        return true;
3421
    },
3422
 
3423
    /**
3424
     * Returns current position index for the given Record.
3425
     *
3426
     * @method getRecordIndex
3427
     * @param oRecord {YAHOO.widget.Record} Record instance.
3428
     * @return {Number} Record's RecordSet position index.
3429
     */
3430
 
3431
    getRecordIndex : function(oRecord) {
3432
        if(oRecord) {
3433
            for(var i=this._records.length-1; i>-1; i--) {
3434
                if(this._records[i] && oRecord.getId() === this._records[i].getId()) {
3435
                    return i;
3436
                }
3437
            }
3438
        }
3439
        return null;
3440
 
3441
    },
3442
 
3443
    /**
3444
     * Adds one Record to the RecordSet at the given index. If index is null,
3445
     * then adds the Record to the end of the RecordSet.
3446
     *
3447
     * @method addRecord
3448
     * @param oData {Object} An object literal of data.
3449
     * @param index {Number} (optional) Position index.
3450
     * @return {YAHOO.widget.Record} A Record instance.
3451
     */
3452
    addRecord : function(oData, index) {
3453
        if(lang.isObject(oData)) {
3454
            var oRecord = this._addRecord(oData, index);
3455
            this.fireEvent("recordAddEvent",{record:oRecord,data:oData});
3456
            return oRecord;
3457
        }
3458
        else {
3459
            return null;
3460
        }
3461
    },
3462
 
3463
    /**
3464
     * Adds multiple Records at once to the RecordSet at the given index with the
3465
     * given object literal data. If index is null, then the new Records are
3466
     * added to the end of the RecordSet.
3467
     *
3468
     * @method addRecords
3469
     * @param aData {Object[]} An object literal data or an array of data object literals.
3470
     * @param index {Number} (optional) Position index.
3471
     * @return {YAHOO.widget.Record[]} An array of Record instances.
3472
     */
3473
    addRecords : function(aData, index) {
3474
        if(lang.isArray(aData)) {
3475
            var newRecords = [],
3476
                idx,i,len;
3477
 
3478
            index = lang.isNumber(index) ? index : this._records.length;
3479
            idx = index;
3480
 
3481
            // Can't go backwards bc we need to preserve order
3482
            for(i=0,len=aData.length; i<len; ++i) {
3483
                if(lang.isObject(aData[i])) {
3484
                    var record = this._addRecord(aData[i], idx++);
3485
                    newRecords.push(record);
3486
                }
3487
           }
3488
            this.fireEvent("recordsAddEvent",{records:newRecords,data:aData});
3489
           return newRecords;
3490
        }
3491
        else if(lang.isObject(aData)) {
3492
            var oRecord = this._addRecord(aData);
3493
            this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
3494
            return oRecord;
3495
        }
3496
        else {
3497
            return null;
3498
        }
3499
    },
3500
 
3501
    /**
3502
     * Sets or replaces one Record to the RecordSet at the given index. Unlike
3503
     * addRecord, an existing Record at that index is not shifted to preserve it.
3504
     * If no index is specified, it adds the Record to the end of the RecordSet.
3505
     *
3506
     * @method setRecord
3507
     * @param oData {Object} An object literal of data.
3508
     * @param index {Number} (optional) Position index.
3509
     * @return {YAHOO.widget.Record} A Record instance.
3510
     */
3511
    setRecord : function(oData, index) {
3512
        if(lang.isObject(oData)) {
3513
            var oRecord = this._setRecord(oData, index);
3514
            this.fireEvent("recordSetEvent",{record:oRecord,data:oData});
3515
            return oRecord;
3516
        }
3517
        else {
3518
            return null;
3519
        }
3520
    },
3521
 
3522
    /**
3523
     * Sets or replaces multiple Records at once to the RecordSet with the given
3524
     * data, starting at the given index. If index is not specified, then the new
3525
     * Records are added to the end of the RecordSet.
3526
     *
3527
     * @method setRecords
3528
     * @param aData {Object[]} An array of object literal data.
3529
     * @param index {Number} (optional) Position index.
3530
     * @return {YAHOO.widget.Record[]} An array of Record instances.
3531
     */
3532
    setRecords : function(aData, index) {
3533
        var Rec   = widget.Record,
3534
            a     = lang.isArray(aData) ? aData : [aData],
3535
            added = [],
3536
            i = 0, l = a.length, j = 0;
3537
 
3538
        index = parseInt(index,10)|0;
3539
 
3540
        for(; i < l; ++i) {
3541
            if (typeof a[i] === 'object' && a[i]) {
3542
                added[j++] = this._records[index + i] = new Rec(a[i]);
3543
            }
3544
        }
3545
 
3546
        this.fireEvent("recordsSetEvent",{records:added,data:aData});
3547
        // Backward compatibility for bug 1918245
3548
        this.fireEvent("recordsSet",{records:added,data:aData});
3549
 
3550
        if (a.length && !added.length) {
3551
        }
3552
 
3553
        return added;
3554
    },
3555
 
3556
    /**
3557
     * Updates given Record with given data.
3558
     *
3559
     * @method updateRecord
3560
     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
3561
     * a RecordSet position index, or a Record ID.
3562
     * @param oData {Object} Object literal of new data.
3563
     * @return {YAHOO.widget.Record} Updated Record, or null.
3564
     */
3565
    updateRecord : function(record, oData) {
3566
        var oRecord = this.getRecord(record);
3567
        if(oRecord && lang.isObject(oData)) {
3568
            // Copy data from the Record for the event that gets fired later
3569
            var oldData = {};
3570
            for(var key in oRecord._oData) {
3571
                if(lang.hasOwnProperty(oRecord._oData, key)) {
3572
                    oldData[key] = oRecord._oData[key];
3573
                }
3574
            }
3575
            oRecord._oData = oData;
3576
            this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData});
3577
            return oRecord;
3578
        }
3579
        else {
3580
            return null;
3581
        }
3582
    },
3583
 
3584
    /**
3585
     * @method updateKey
3586
     * @deprecated Use updateRecordValue
3587
     */
3588
    updateKey : function(record, sKey, oData) {
3589
        this.updateRecordValue(record, sKey, oData);
3590
    },
3591
    /**
3592
     * Sets given Record at given key to given data.
3593
     *
3594
     * @method updateRecordValue
3595
     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
3596
     * a RecordSet position index, or a Record ID.
3597
     * @param sKey {String} Key name.
3598
     * @param oData {Object} New data.
3599
     */
3600
    updateRecordValue : function(record, sKey, oData) {
3601
        var oRecord = this.getRecord(record);
3602
        if(oRecord) {
3603
            var oldData = null;
3604
            var keyValue = oRecord._oData[sKey];
3605
            // Copy data from the Record for the event that gets fired later
3606
            if(keyValue && lang.isObject(keyValue)) {
3607
                oldData = {};
3608
                for(var key in keyValue)  {
3609
                    if(lang.hasOwnProperty(keyValue, key)) {
3610
                        oldData[key] = keyValue[key];
3611
                    }
3612
                }
3613
            }
3614
            // Copy by value
3615
            else {
3616
                oldData = keyValue;
3617
            }
3618
 
3619
            oRecord._oData[sKey] = oData;
3620
            this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
3621
            this.fireEvent("recordValueUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
3622
        }
3623
        else {
3624
        }
3625
    },
3626
 
3627
    /**
3628
     * Replaces all Records in RecordSet with new object literal data.
3629
     *
3630
     * @method replaceRecords
3631
     * @param data {Object || Object[]} An object literal of data or an array of
3632
     * data object literals.
3633
     * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or
3634
     * an array of Records.
3635
     */
3636
    replaceRecords : function(data) {
3637
        this.reset();
3638
        return this.addRecords(data);
3639
    },
3640
 
3641
    /**
3642
     * Sorts all Records by given function. Records keep their unique IDs but will
3643
     * have new RecordSet position indexes.
3644
     *
3645
     * @method sortRecords
3646
     * @param fnSort {Function} Reference to a sort function.
3647
     * @param desc {Boolean} True if sort direction is descending, false if sort
3648
     * direction is ascending.
3649
     * @param field {String} The field to sort by, from sortOptions.field
3650
     * @return {YAHOO.widget.Record[]} Sorted array of Records.
3651
     */
3652
    sortRecords : function(fnSort, desc, field) {
3653
        return this._records.sort(function(a, b) {return fnSort(a, b, desc, field);});
3654
    },
3655
 
3656
    /**
3657
     * Reverses all Records, so ["one", "two", "three"] becomes ["three", "two", "one"].
3658
     *
3659
     * @method reverseRecords
3660
     * @return {YAHOO.widget.Record[]} Reverse-sorted array of Records.
3661
     */
3662
    reverseRecords : function() {
3663
        return this._records.reverse();
3664
    },
3665
 
3666
    /**
3667
     * Removes the Record at the given position index from the RecordSet. If a range
3668
     * is also provided, removes that many Records, starting from the index. Length
3669
     * of RecordSet is correspondingly shortened.
3670
     *
3671
     * @method deleteRecord
3672
     * @param index {Number} Record's RecordSet position index.
3673
     * @return {Object} A copy of the data held by the deleted Record.
3674
     */
3675
    deleteRecord : function(index) {
3676
        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
3677
            var oData = this.getRecord(index).getData();
3678
 
3679
            this._deleteRecord(index);
3680
            this.fireEvent("recordDeleteEvent",{data:oData,index:index});
3681
            return oData;
3682
        }
3683
        else {
3684
            return null;
3685
        }
3686
    },
3687
 
3688
    /**
3689
     * Removes the Record at the given position index from the RecordSet. If a range
3690
     * is also provided, removes that many Records, starting from the index. Length
3691
     * of RecordSet is correspondingly shortened.
3692
     *
3693
     * @method deleteRecords
3694
     * @param index {Number} Record's RecordSet position index.
3695
     * @param range {Number} (optional) How many Records to delete.
3696
     * @return {Object[]} An array of copies of the data held by the deleted Records.
3697
     */
3698
    deleteRecords : function(index, range) {
3699
        if(!lang.isNumber(range)) {
3700
            range = 1;
3701
        }
3702
        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
3703
            var recordsToDelete = this.getRecords(index, range);
3704
            var deletedData = [], // this mistakenly held Records, not data
3705
                deletedObjects = []; // this passes data only
3706
 
3707
            for(var i=0; i<recordsToDelete.length; i++) {
3708
                deletedData[deletedData.length] = recordsToDelete[i]; // backward compatibility
3709
                deletedObjects[deletedObjects.length] = recordsToDelete[i].getData();
3710
            }
3711
            this._deleteRecord(index, range);
3712
 
3713
            this.fireEvent("recordsDeleteEvent",{data:deletedData,deletedData:deletedObjects,index:index});
3714
 
3715
            return deletedData;
3716
        }
3717
        else {
3718
            return null;
3719
        }
3720
    },
3721
 
3722
    /**
3723
     * Deletes all Records from the RecordSet.
3724
     *
3725
     * @method reset
3726
     */
3727
    reset : function() {
3728
        this._records = [];
3729
        //this._length = 0;
3730
        this.fireEvent("resetEvent");
3731
    }
3732
};
3733
 
3734
/////////////////////////////////////////////////////////////////////////////
3735
//
3736
// Custom Events
3737
//
3738
/////////////////////////////////////////////////////////////////////////////
3739
 
3740
// RecordSet uses EventProvider
3741
lang.augmentProto(RS, util.EventProvider);
3742
 
3743
/**
3744
 * Fired when a new Record is added to the RecordSet.
3745
 *
3746
 * @event recordAddEvent
3747
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3748
 * @param oArgs.data {Object} Data added.
3749
 */
3750
 
3751
/**
3752
 * Fired when multiple Records are added to the RecordSet at once.
3753
 *
3754
 * @event recordsAddEvent
3755
 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
3756
 * @param oArgs.data {Object[]} Data added.
3757
 */
3758
 
3759
/**
3760
 * Fired when a Record is set in the RecordSet.
3761
 *
3762
 * @event recordSetEvent
3763
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3764
 * @param oArgs.data {Object} Data added.
3765
 */
3766
 
3767
/**
3768
 * Fired when multiple Records are set in the RecordSet at once.
3769
 *
3770
 * @event recordsSetEvent
3771
 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
3772
 * @param oArgs.data {Object[]} Data added.
3773
 */
3774
 
3775
/**
3776
 * Fired when a Record is updated with new data.
3777
 *
3778
 * @event recordUpdateEvent
3779
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3780
 * @param oArgs.newData {Object} New data.
3781
 * @param oArgs.oldData {Object} Old data.
3782
 */
3783
 
3784
/**
3785
 * Fired when a Record is deleted from the RecordSet.
3786
 *
3787
 * @event recordDeleteEvent
3788
 * @param oArgs.data {Object} The data held by the deleted Record,
3789
 * or an array of data object literals if multiple Records were deleted at once.
3790
 * @param oArgs.index {Object} Index of the deleted Record.
3791
 */
3792
 
3793
/**
3794
 * Fired when multiple Records are deleted from the RecordSet at once.
3795
 *
3796
 * @event recordsDeleteEvent
3797
 * @param oArgs.data {Object[]} An array of deleted Records.
3798
 * @param oArgs.deletedData {Object[]} An array of deleted data.
3799
 * @param oArgs.index {Object} Index of the first deleted Record.
3800
 */
3801
 
3802
/**
3803
 * Fired when all Records are deleted from the RecordSet at once.
3804
 *
3805
 * @event resetEvent
3806
 */
3807
 
3808
/**
3809
 * @event keyUpdateEvent
3810
 * @deprecated Use recordValueUpdateEvent
3811
 */
3812
 
3813
/**
3814
 * Fired when a Record value is updated with new data.
3815
 *
3816
 * @event recordValueUpdateEvent
3817
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
3818
 * @param oArgs.key {String} The updated key.
3819
 * @param oArgs.newData {Object} New data.
3820
 * @param oArgs.oldData {Object} Old data.
3821
 *
3822
 */
3823
 
3824
 
3825
/****************************************************************************/
3826
/****************************************************************************/
3827
/****************************************************************************/
3828
 
3829
/**
3830
 * The Record class defines a DataTable record.
3831
 *
3832
 * @namespace YAHOO.widget
3833
 * @class Record
3834
 * @constructor
3835
 * @param oConfigs {Object} (optional) Object literal of key/value pairs.
3836
 */
3837
YAHOO.widget.Record = function(oLiteral) {
3838
    this._nCount = widget.Record._nCount;
3839
    this._sId = Dom.generateId(null, "yui-rec");//"yui-rec" + this._nCount;
3840
    widget.Record._nCount++;
3841
    this._oData = {};
3842
    if(lang.isObject(oLiteral)) {
3843
        for(var sKey in oLiteral) {
3844
            if(lang.hasOwnProperty(oLiteral, sKey)) {
3845
                this._oData[sKey] = oLiteral[sKey];
3846
            }
3847
        }
3848
    }
3849
};
3850
 
3851
/////////////////////////////////////////////////////////////////////////////
3852
//
3853
// Private member variables
3854
//
3855
/////////////////////////////////////////////////////////////////////////////
3856
 
3857
/**
3858
 * Internal class variable to give unique IDs to Record instances.
3859
 *
3860
 * @property Record._nCount
3861
 * @type Number
3862
 * @private
3863
 */
3864
YAHOO.widget.Record._nCount = 0;
3865
 
3866
YAHOO.widget.Record.prototype = {
3867
    /**
3868
     * Immutable unique count assigned at instantiation. Remains constant while a
3869
     * Record's position index can change from sorting.
3870
     *
3871
     * @property _nCount
3872
     * @type Number
3873
     * @private
3874
     */
3875
    _nCount : null,
3876
 
3877
    /**
3878
     * Immutable unique ID assigned at instantiation. Remains constant while a
3879
     * Record's position index can change from sorting.
3880
     *
3881
     * @property _sId
3882
     * @type String
3883
     * @private
3884
     */
3885
    _sId : null,
3886
 
3887
    /**
3888
     * Holds data for the Record in an object literal.
3889
     *
3890
     * @property _oData
3891
     * @type Object
3892
     * @private
3893
     */
3894
    _oData : null,
3895
 
3896
    /////////////////////////////////////////////////////////////////////////////
3897
    //
3898
    // Public member variables
3899
    //
3900
    /////////////////////////////////////////////////////////////////////////////
3901
 
3902
    /////////////////////////////////////////////////////////////////////////////
3903
    //
3904
    // Public methods
3905
    //
3906
    /////////////////////////////////////////////////////////////////////////////
3907
 
3908
    /**
3909
     * Returns unique count assigned at instantiation.
3910
     *
3911
     * @method getCount
3912
     * @return Number
3913
     */
3914
    getCount : function() {
3915
        return this._nCount;
3916
    },
3917
 
3918
    /**
3919
     * Returns unique ID assigned at instantiation.
3920
     *
3921
     * @method getId
3922
     * @return String
3923
     */
3924
    getId : function() {
3925
        return this._sId;
3926
    },
3927
 
3928
    /**
3929
     * Returns data for the Record for a field if given, or the entire object
3930
     * literal otherwise.
3931
     *
3932
     * @method getData
3933
     * @param sField {String} (Optional) The field from which to retrieve data value.
3934
     * @return Object
3935
     */
3936
    getData : function(sField) {
3937
        if(lang.isString(sField)) {
3938
            return this._oData[sField];
3939
        }
3940
        else {
3941
            return this._oData;
3942
        }
3943
    },
3944
 
3945
    /**
3946
     * Sets given data at the given key. Use the RecordSet method updateRecordValue to trigger
3947
     * events.
3948
     *
3949
     * @method setData
3950
     * @param sKey {String} The key of the new value.
3951
     * @param oData {MIXED} The new value.
3952
     */
3953
    setData : function(sKey, oData) {
3954
        this._oData[sKey] = oData;
3955
    }
3956
};
3957
 
3958
})();
3959
 
3960
(function () {
3961
 
3962
var lang   = YAHOO.lang,
3963
    util   = YAHOO.util,
3964
    widget = YAHOO.widget,
3965
    ua     = YAHOO.env.ua,
3966
 
3967
    Dom    = util.Dom,
3968
    Ev     = util.Event,
3969
    DS     = util.DataSourceBase;
3970
 
3971
/**
3972
 * The DataTable widget provides a progressively enhanced DHTML control for
3973
 * displaying tabular data across A-grade browsers.
3974
 *
3975
 * @module datatable
3976
 * @requires yahoo, dom, event, element, datasource
3977
 * @optional dragdrop, dragdrop
3978
 * @title DataTable Widget
3979
 */
3980
 
3981
/****************************************************************************/
3982
/****************************************************************************/
3983
/****************************************************************************/
3984
 
3985
/**
3986
 * DataTable class for the YUI DataTable widget.
3987
 *
3988
 * @namespace YAHOO.widget
3989
 * @class DataTable
3990
 * @extends YAHOO.util.Element
3991
 * @constructor
3992
 * @param elContainer {HTMLElement} Container element for the TABLE.
3993
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
3994
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
3995
 * @param oConfigs {object} (optional) Object literal of configuration values.
3996
 */
3997
YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
3998
    var DT = widget.DataTable;
3999
 
4000
    ////////////////////////////////////////////////////////////////////////////
4001
    // Backward compatibility for SDT, but prevent infinite loops
4002
 
4003
    if(oConfigs && oConfigs.scrollable) {
4004
        return new YAHOO.widget.ScrollingDataTable(elContainer,aColumnDefs,oDataSource,oConfigs);
4005
    }
4006
 
4007
    ////////////////////////////////////////////////////////////////////////////
4008
    // Initialization
4009
 
4010
    // Internal vars
4011
    this._nIndex = DT._nCount;
4012
    this._sId = Dom.generateId(null, "yui-dt");// "yui-dt"+this._nIndex;
4013
    this._oChainRender = new YAHOO.util.Chain();
4014
    this._oChainRender.subscribe("end",this._onRenderChainEnd, this, true);
4015
 
4016
    // Initialize configs
4017
    this._initConfigs(oConfigs);
4018
 
4019
    // Initialize DataSource
4020
    this._initDataSource(oDataSource);
4021
    if(!this._oDataSource) {
4022
        return;
4023
    }
4024
 
4025
    // Initialize ColumnSet
4026
    this._initColumnSet(aColumnDefs);
4027
    if(!this._oColumnSet) {
4028
        return;
4029
    }
4030
 
4031
    // Initialize RecordSet
4032
    this._initRecordSet();
4033
    if(!this._oRecordSet) {
4034
    }
4035
 
4036
    // Initialize Attributes
4037
    DT.superclass.constructor.call(this, elContainer, this.configs);
4038
 
4039
    // Initialize DOM elements
4040
    var okDom = this._initDomElements(elContainer);
4041
    if(!okDom) {
4042
        return;
4043
    }
4044
 
4045
    // Show message as soon as config is available
4046
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
4047
 
4048
    ////////////////////////////////////////////////////////////////////////////
4049
    // Once per instance
4050
    this._initEvents();
4051
 
4052
    DT._nCount++;
4053
    DT._nCurrentCount++;
4054
 
4055
    ////////////////////////////////////////////////////////////////////////////
4056
    // Data integration
4057
 
4058
    // Send a simple initial request
4059
    var oCallback = {
4060
        success : this.onDataReturnSetRows,
4061
        failure : this.onDataReturnSetRows,
4062
        scope   : this,
4063
        argument: this.getState()
4064
    };
4065
 
4066
    var initialLoad = this.get("initialLoad");
4067
    if(initialLoad === true) {
4068
        this._oDataSource.sendRequest(this.get("initialRequest"), oCallback);
4069
    }
4070
    // Do not send an initial request at all
4071
    else if(initialLoad === false) {
4072
        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
4073
    }
4074
    // Send an initial request with a custom payload
4075
    else {
4076
        var oCustom = initialLoad || {};
4077
        oCallback.argument = oCustom.argument || {};
4078
        this._oDataSource.sendRequest(oCustom.request, oCallback);
4079
    }
4080
};
4081
 
4082
var DT = widget.DataTable;
4083
 
4084
/////////////////////////////////////////////////////////////////////////////
4085
//
4086
// Public constants
4087
//
4088
/////////////////////////////////////////////////////////////////////////////
4089
 
4090
lang.augmentObject(DT, {
4091
 
4092
    /**
4093
     * Class name assigned to outer DataTable container.
4094
     *
4095
     * @property DataTable.CLASS_DATATABLE
4096
     * @type String
4097
     * @static
4098
     * @final
4099
     * @default "yui-dt"
4100
     */
4101
    CLASS_DATATABLE : "yui-dt",
4102
 
4103
    /**
4104
     * Class name assigned to liner DIV elements.
4105
     *
4106
     * @property DataTable.CLASS_LINER
4107
     * @type String
4108
     * @static
4109
     * @final
4110
     * @default "yui-dt-liner"
4111
     */
4112
    CLASS_LINER : "yui-dt-liner",
4113
 
4114
    /**
4115
     * Class name assigned to display label elements.
4116
     *
4117
     * @property DataTable.CLASS_LABEL
4118
     * @type String
4119
     * @static
4120
     * @final
4121
     * @default "yui-dt-label"
4122
     */
4123
    CLASS_LABEL : "yui-dt-label",
4124
 
4125
    /**
4126
     * Class name assigned to messaging elements.
4127
     *
4128
     * @property DataTable.CLASS_MESSAGE
4129
     * @type String
4130
     * @static
4131
     * @final
4132
     * @default "yui-dt-message"
4133
     */
4134
    CLASS_MESSAGE : "yui-dt-message",
4135
 
4136
    /**
4137
     * Class name assigned to mask element when DataTable is disabled.
4138
     *
4139
     * @property DataTable.CLASS_MASK
4140
     * @type String
4141
     * @static
4142
     * @final
4143
     * @default "yui-dt-mask"
4144
     */
4145
    CLASS_MASK : "yui-dt-mask",
4146
 
4147
    /**
4148
     * Class name assigned to data elements.
4149
     *
4150
     * @property DataTable.CLASS_DATA
4151
     * @type String
4152
     * @static
4153
     * @final
4154
     * @default "yui-dt-data"
4155
     */
4156
    CLASS_DATA : "yui-dt-data",
4157
 
4158
    /**
4159
     * Class name assigned to Column drag target.
4160
     *
4161
     * @property DataTable.CLASS_COLTARGET
4162
     * @type String
4163
     * @static
4164
     * @final
4165
     * @default "yui-dt-coltarget"
4166
     */
4167
    CLASS_COLTARGET : "yui-dt-coltarget",
4168
 
4169
    /**
4170
     * Class name assigned to resizer handle elements.
4171
     *
4172
     * @property DataTable.CLASS_RESIZER
4173
     * @type String
4174
     * @static
4175
     * @final
4176
     * @default "yui-dt-resizer"
4177
     */
4178
    CLASS_RESIZER : "yui-dt-resizer",
4179
 
4180
    /**
4181
     * Class name assigned to resizer liner elements.
4182
     *
4183
     * @property DataTable.CLASS_RESIZERLINER
4184
     * @type String
4185
     * @static
4186
     * @final
4187
     * @default "yui-dt-resizerliner"
4188
     */
4189
    CLASS_RESIZERLINER : "yui-dt-resizerliner",
4190
 
4191
    /**
4192
     * Class name assigned to resizer proxy elements.
4193
     *
4194
     * @property DataTable.CLASS_RESIZERPROXY
4195
     * @type String
4196
     * @static
4197
     * @final
4198
     * @default "yui-dt-resizerproxy"
4199
     */
4200
    CLASS_RESIZERPROXY : "yui-dt-resizerproxy",
4201
 
4202
    /**
4203
     * Class name assigned to CellEditor container elements.
4204
     *
4205
     * @property DataTable.CLASS_EDITOR
4206
     * @type String
4207
     * @static
4208
     * @final
4209
     * @default "yui-dt-editor"
4210
     */
4211
    CLASS_EDITOR : "yui-dt-editor",
4212
 
4213
    /**
4214
     * Class name assigned to CellEditor container shim.
4215
     *
4216
     * @property DataTable.CLASS_EDITOR_SHIM
4217
     * @type String
4218
     * @static
4219
     * @final
4220
     * @default "yui-dt-editor-shim"
4221
     */
4222
    CLASS_EDITOR_SHIM : "yui-dt-editor-shim",
4223
 
4224
    /**
4225
     * Class name assigned to paginator container elements.
4226
     *
4227
     * @property DataTable.CLASS_PAGINATOR
4228
     * @type String
4229
     * @static
4230
     * @final
4231
     * @default "yui-dt-paginator"
4232
     */
4233
    CLASS_PAGINATOR : "yui-dt-paginator",
4234
 
4235
    /**
4236
     * Class name assigned to page number indicators.
4237
     *
4238
     * @property DataTable.CLASS_PAGE
4239
     * @type String
4240
     * @static
4241
     * @final
4242
     * @default "yui-dt-page"
4243
     */
4244
    CLASS_PAGE : "yui-dt-page",
4245
 
4246
    /**
4247
     * Class name assigned to default indicators.
4248
     *
4249
     * @property DataTable.CLASS_DEFAULT
4250
     * @type String
4251
     * @static
4252
     * @final
4253
     * @default "yui-dt-default"
4254
     */
4255
    CLASS_DEFAULT : "yui-dt-default",
4256
 
4257
    /**
4258
     * Class name assigned to previous indicators.
4259
     *
4260
     * @property DataTable.CLASS_PREVIOUS
4261
     * @type String
4262
     * @static
4263
     * @final
4264
     * @default "yui-dt-previous"
4265
     */
4266
    CLASS_PREVIOUS : "yui-dt-previous",
4267
 
4268
    /**
4269
     * Class name assigned next indicators.
4270
     *
4271
     * @property DataTable.CLASS_NEXT
4272
     * @type String
4273
     * @static
4274
     * @final
4275
     * @default "yui-dt-next"
4276
     */
4277
    CLASS_NEXT : "yui-dt-next",
4278
 
4279
    /**
4280
     * Class name assigned to first elements.
4281
     *
4282
     * @property DataTable.CLASS_FIRST
4283
     * @type String
4284
     * @static
4285
     * @final
4286
     * @default "yui-dt-first"
4287
     */
4288
    CLASS_FIRST : "yui-dt-first",
4289
 
4290
    /**
4291
     * Class name assigned to last elements.
4292
     *
4293
     * @property DataTable.CLASS_LAST
4294
     * @type String
4295
     * @static
4296
     * @final
4297
     * @default "yui-dt-last"
4298
     */
4299
    CLASS_LAST : "yui-dt-last",
4300
 
4301
    /**
4302
     * Class name assigned to Record elements.
4303
     *
4304
     * @property DataTable.CLASS_REC
4305
     * @type String
4306
     * @static
4307
     * @final
4308
     * @default "yui-dt-rec"
4309
     */
4310
    CLASS_REC : "yui-dt-rec",
4311
 
4312
    /**
4313
     * Class name assigned to even elements.
4314
     *
4315
     * @property DataTable.CLASS_EVEN
4316
     * @type String
4317
     * @static
4318
     * @final
4319
     * @default "yui-dt-even"
4320
     */
4321
    CLASS_EVEN : "yui-dt-even",
4322
 
4323
    /**
4324
     * Class name assigned to odd elements.
4325
     *
4326
     * @property DataTable.CLASS_ODD
4327
     * @type String
4328
     * @static
4329
     * @final
4330
     * @default "yui-dt-odd"
4331
     */
4332
    CLASS_ODD : "yui-dt-odd",
4333
 
4334
    /**
4335
     * Class name assigned to selected elements.
4336
     *
4337
     * @property DataTable.CLASS_SELECTED
4338
     * @type String
4339
     * @static
4340
     * @final
4341
     * @default "yui-dt-selected"
4342
     */
4343
    CLASS_SELECTED : "yui-dt-selected",
4344
 
4345
    /**
4346
     * Class name assigned to highlighted elements.
4347
     *
4348
     * @property DataTable.CLASS_HIGHLIGHTED
4349
     * @type String
4350
     * @static
4351
     * @final
4352
     * @default "yui-dt-highlighted"
4353
     */
4354
    CLASS_HIGHLIGHTED : "yui-dt-highlighted",
4355
 
4356
    /**
4357
     * Class name assigned to hidden elements.
4358
     *
4359
     * @property DataTable.CLASS_HIDDEN
4360
     * @type String
4361
     * @static
4362
     * @final
4363
     * @default "yui-dt-hidden"
4364
     */
4365
    CLASS_HIDDEN : "yui-dt-hidden",
4366
 
4367
    /**
4368
     * Class name assigned to disabled elements.
4369
     *
4370
     * @property DataTable.CLASS_DISABLED
4371
     * @type String
4372
     * @static
4373
     * @final
4374
     * @default "yui-dt-disabled"
4375
     */
4376
    CLASS_DISABLED : "yui-dt-disabled",
4377
 
4378
    /**
4379
     * Class name assigned to empty indicators.
4380
     *
4381
     * @property DataTable.CLASS_EMPTY
4382
     * @type String
4383
     * @static
4384
     * @final
4385
     * @default "yui-dt-empty"
4386
     */
4387
    CLASS_EMPTY : "yui-dt-empty",
4388
 
4389
    /**
4390
     * Class name assigned to loading indicatorx.
4391
     *
4392
     * @property DataTable.CLASS_LOADING
4393
     * @type String
4394
     * @static
4395
     * @final
4396
     * @default "yui-dt-loading"
4397
     */
4398
    CLASS_LOADING : "yui-dt-loading",
4399
 
4400
    /**
4401
     * Class name assigned to error indicators.
4402
     *
4403
     * @property DataTable.CLASS_ERROR
4404
     * @type String
4405
     * @static
4406
     * @final
4407
     * @default "yui-dt-error"
4408
     */
4409
    CLASS_ERROR : "yui-dt-error",
4410
 
4411
    /**
4412
     * Class name assigned to editable elements.
4413
     *
4414
     * @property DataTable.CLASS_EDITABLE
4415
     * @type String
4416
     * @static
4417
     * @final
4418
     * @default "yui-dt-editable"
4419
     */
4420
    CLASS_EDITABLE : "yui-dt-editable",
4421
 
4422
    /**
4423
     * Class name assigned to draggable elements.
4424
     *
4425
     * @property DataTable.CLASS_DRAGGABLE
4426
     * @type String
4427
     * @static
4428
     * @final
4429
     * @default "yui-dt-draggable"
4430
     */
4431
    CLASS_DRAGGABLE : "yui-dt-draggable",
4432
 
4433
    /**
4434
     * Class name assigned to resizeable elements.
4435
     *
4436
     * @property DataTable.CLASS_RESIZEABLE
4437
     * @type String
4438
     * @static
4439
     * @final
4440
     * @default "yui-dt-resizeable"
4441
     */
4442
    CLASS_RESIZEABLE : "yui-dt-resizeable",
4443
 
4444
    /**
4445
     * Class name assigned to scrollable elements.
4446
     *
4447
     * @property DataTable.CLASS_SCROLLABLE
4448
     * @type String
4449
     * @static
4450
     * @final
4451
     * @default "yui-dt-scrollable"
4452
     */
4453
    CLASS_SCROLLABLE : "yui-dt-scrollable",
4454
 
4455
    /**
4456
     * Class name assigned to sortable elements.
4457
     *
4458
     * @property DataTable.CLASS_SORTABLE
4459
     * @type String
4460
     * @static
4461
     * @final
4462
     * @default "yui-dt-sortable"
4463
     */
4464
    CLASS_SORTABLE : "yui-dt-sortable",
4465
 
4466
    /**
4467
     * Class name assigned to ascending elements.
4468
     *
4469
     * @property DataTable.CLASS_ASC
4470
     * @type String
4471
     * @static
4472
     * @final
4473
     * @default "yui-dt-asc"
4474
     */
4475
    CLASS_ASC : "yui-dt-asc",
4476
 
4477
    /**
4478
     * Class name assigned to descending elements.
4479
     *
4480
     * @property DataTable.CLASS_DESC
4481
     * @type String
4482
     * @static
4483
     * @final
4484
     * @default "yui-dt-desc"
4485
     */
4486
    CLASS_DESC : "yui-dt-desc",
4487
 
4488
    /**
4489
     * Class name assigned to BUTTON elements and/or container elements.
4490
     *
4491
     * @property DataTable.CLASS_BUTTON
4492
     * @type String
4493
     * @static
4494
     * @final
4495
     * @default "yui-dt-button"
4496
     */
4497
    CLASS_BUTTON : "yui-dt-button",
4498
 
4499
    /**
4500
     * Class name assigned to INPUT TYPE=CHECKBOX elements and/or container elements.
4501
     *
4502
     * @property DataTable.CLASS_CHECKBOX
4503
     * @type String
4504
     * @static
4505
     * @final
4506
     * @default "yui-dt-checkbox"
4507
     */
4508
    CLASS_CHECKBOX : "yui-dt-checkbox",
4509
 
4510
    /**
4511
     * Class name assigned to SELECT elements and/or container elements.
4512
     *
4513
     * @property DataTable.CLASS_DROPDOWN
4514
     * @type String
4515
     * @static
4516
     * @final
4517
     * @default "yui-dt-dropdown"
4518
     */
4519
    CLASS_DROPDOWN : "yui-dt-dropdown",
4520
 
4521
    /**
4522
     * Class name assigned to INPUT TYPE=RADIO elements and/or container elements.
4523
     *
4524
     * @property DataTable.CLASS_RADIO
4525
     * @type String
4526
     * @static
4527
     * @final
4528
     * @default "yui-dt-radio"
4529
     */
4530
    CLASS_RADIO : "yui-dt-radio",
4531
 
4532
    /////////////////////////////////////////////////////////////////////////
4533
    //
4534
    // Private static properties
4535
    //
4536
    /////////////////////////////////////////////////////////////////////////
4537
 
4538
    /**
4539
     * Internal class variable for indexing multiple DataTable instances.
4540
     *
4541
     * @property DataTable._nCount
4542
     * @type Number
4543
     * @private
4544
     * @static
4545
     */
4546
    _nCount : 0,
4547
 
4548
    /**
4549
     * Internal class variable tracking current number of DataTable instances,
4550
     * so that certain class values can be reset when all instances are destroyed.
4551
     *
4552
     * @property DataTable._nCurrentCount
4553
     * @type Number
4554
     * @private
4555
     * @static
4556
     */
4557
    _nCurrentCount : 0,
4558
 
4559
    /**
4560
     * Reference to the STYLE node that is dynamically created and updated
4561
     * in order to manage Column widths.
4562
     *
4563
     * @property DataTable._elDynStyleNode
4564
     * @type HTMLElement
4565
     * @private
4566
     * @static
4567
     */
4568
    _elDynStyleNode : null,
4569
 
4570
    /**
4571
     * Set to true if _elDynStyleNode cannot be populated due to browser incompatibility.
4572
     *
4573
     * @property DataTable._bDynStylesFallback
4574
     * @type boolean
4575
     * @private
4576
     * @static
4577
     */
4578
    _bDynStylesFallback : (ua.ie) ? true : false,
4579
 
4580
    /**
4581
     * Object literal hash of Columns and their dynamically create style rules.
4582
     *
4583
     * @property DataTable._oDynStyles
4584
     * @type Object
4585
     * @private
4586
     * @static
4587
     */
4588
    _oDynStyles : {},
4589
 
4590
    /////////////////////////////////////////////////////////////////////////
4591
    //
4592
    // Private static methods
4593
    //
4594
    /////////////////////////////////////////////////////////////////////////
4595
 
4596
    /**
4597
     * Clones object literal or array of object literals.
4598
     *
4599
     * @method DataTable._cloneObject
4600
     * @param o {Object} Object.
4601
     * @private
4602
     * @static
4603
     */
4604
    _cloneObject: function(o) {
4605
        if(!lang.isValue(o)) {
4606
            return o;
4607
        }
4608
 
4609
        var copy = {};
4610
 
4611
        if(o instanceof YAHOO.widget.BaseCellEditor) {
4612
            copy = o;
4613
        }
4614
        else if(Object.prototype.toString.apply(o) === "[object RegExp]") {
4615
            copy = o;
4616
        }
4617
        else if(lang.isFunction(o)) {
4618
            copy = o;
4619
        }
4620
        else if(lang.isArray(o)) {
4621
            var array = [];
4622
            for(var i=0,len=o.length;i<len;i++) {
4623
                array[i] = DT._cloneObject(o[i]);
4624
            }
4625
            copy = array;
4626
        }
4627
        else if(lang.isObject(o)) {
4628
            for (var x in o){
4629
                if(lang.hasOwnProperty(o, x)) {
4630
                    if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
4631
                        copy[x] = DT._cloneObject(o[x]);
4632
                    }
4633
                    else {
4634
                        copy[x] = o[x];
4635
                    }
4636
                }
4637
            }
4638
        }
4639
        else {
4640
            copy = o;
4641
        }
4642
 
4643
        return copy;
4644
    },
4645
 
4646
    /**
4647
     * Formats a BUTTON element.
4648
     *
4649
     * @method DataTable.formatButton
4650
     * @param el {HTMLElement} The element to format with markup.
4651
     * @param oRecord {YAHOO.widget.Record} Record instance.
4652
     * @param oColumn {YAHOO.widget.Column} Column instance.
4653
     * @param oData {HTML} Data value for the cell. By default, the value
4654
     * is what gets written to the BUTTON. String values are treated as markup
4655
     * and inserted into the DOM with innerHTML.
4656
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4657
     * @static
4658
     */
4659
    formatButton : function(el, oRecord, oColumn, oData, oDataTable) {
4660
        var sValue = lang.isValue(oData) ? oData : "Click";
4661
        //TODO: support YAHOO.widget.Button
4662
        //if(YAHOO.widget.Button) {
4663
 
4664
        //}
4665
        //else {
4666
            el.innerHTML = "<button type=\"button\" class=\""+
4667
                    DT.CLASS_BUTTON + "\">" + sValue + "</button>";
4668
        //}
4669
    },
4670
 
4671
    /**
4672
     * Formats a CHECKBOX element.
4673
     *
4674
     * @method DataTable.formatCheckbox
4675
     * @param el {HTMLElement} The element to format with markup.
4676
     * @param oRecord {YAHOO.widget.Record} Record instance.
4677
     * @param oColumn {YAHOO.widget.Column} Column instance.
4678
     * @param oData {Object | Boolean | HTML} Data value for the cell. Can be a simple
4679
     * Boolean to indicate whether checkbox is checked or not. Can be object literal
4680
     * {checked:bBoolean, label:sLabel}. String values are treated as markup
4681
     * and inserted into the DOM with innerHTML.
4682
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4683
     * @static
4684
     */
4685
    formatCheckbox : function(el, oRecord, oColumn, oData, oDataTable) {
4686
        var bChecked = oData;
4687
        bChecked = (bChecked) ? " checked=\"checked\"" : "";
4688
        el.innerHTML = "<input type=\"checkbox\"" + bChecked +
4689
                " class=\"" + DT.CLASS_CHECKBOX + "\" />";
4690
    },
4691
 
4692
    /**
4693
     * Formats currency. Default unit is USD.
4694
     *
4695
     * @method DataTable.formatCurrency
4696
     * @param el {HTMLElement} The element to format with markup.
4697
     * @param oRecord {YAHOO.widget.Record} Record instance.
4698
     * @param oColumn {YAHOO.widget.Column} Column instance.
4699
     * @param oData {Number} Data value for the cell.
4700
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4701
     * @static
4702
     */
4703
    formatCurrency : function(el, oRecord, oColumn, oData, oDataTable) {
4704
        var oDT = oDataTable || this;
4705
        el.innerHTML = util.Number.format(oData, oColumn.currencyOptions || oDT.get("currencyOptions"));
4706
    },
4707
 
4708
    /**
4709
     * Formats JavaScript Dates.
4710
     *
4711
     * @method DataTable.formatDate
4712
     * @param el {HTMLElement} The element to format with markup.
4713
     * @param oRecord {YAHOO.widget.Record} Record instance.
4714
     * @param oColumn {YAHOO.widget.Column} Column instance.
4715
     * @param oData {Object} Data value for the cell, or null. String values are
4716
     * treated as markup and inserted into the DOM with innerHTML.
4717
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4718
     * @static
4719
     */
4720
    formatDate : function(el, oRecord, oColumn, oData, oDataTable) {
4721
        var oDT = oDataTable || this,
4722
            oConfig = oColumn.dateOptions || oDT.get("dateOptions");
4723
        el.innerHTML = util.Date.format(oData, oConfig, oConfig.locale);
4724
    },
4725
 
4726
    /**
4727
     * Formats SELECT elements.
4728
     *
4729
     * @method DataTable.formatDropdown
4730
     * @param el {HTMLElement} The element to format with markup.
4731
     * @param oRecord {YAHOO.widget.Record} Record instance.
4732
     * @param oColumn {YAHOO.widget.Column} Column instance.
4733
     * @param oData {Object} Data value for the cell, or null. String values may
4734
     * be treated as markup and inserted into the DOM with innerHTML as element
4735
     * label.
4736
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4737
     * @static
4738
     */
4739
    formatDropdown : function(el, oRecord, oColumn, oData, oDataTable) {
4740
        var oDT = oDataTable || this,
4741
            selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field),
4742
            options = (lang.isArray(oColumn.dropdownOptions)) ?
4743
                oColumn.dropdownOptions : null,
4744
 
4745
            selectEl,
4746
            collection = el.getElementsByTagName("select");
4747
 
4748
        // Create the form element only once, so we can attach the onChange listener
4749
        if(collection.length === 0) {
4750
            // Create SELECT element
4751
            selectEl = document.createElement("select");
4752
            selectEl.className = DT.CLASS_DROPDOWN;
4753
            selectEl = el.appendChild(selectEl);
4754
 
4755
            // Add event listener
4756
            Ev.addListener(selectEl,"change",oDT._onDropdownChange,oDT);
4757
        }
4758
 
4759
        selectEl = collection[0];
4760
 
4761
        // Update the form element
4762
        if(selectEl) {
4763
            // Clear out previous options
4764
            selectEl.innerHTML = "";
4765
 
4766
            // We have options to populate
4767
            if(options) {
4768
                // Create OPTION elements
4769
                for(var i=0; i<options.length; i++) {
4770
                    var option = options[i];
4771
                    var optionEl = document.createElement("option");
4772
                    optionEl.value = (lang.isValue(option.value)) ?
4773
                            option.value : option;
4774
                    // Bug 2334323: Support legacy text, support label for consistency with DropdownCellEditor
4775
                    optionEl.innerHTML = (lang.isValue(option.text)) ?
4776
                            option.text : (lang.isValue(option.label)) ? option.label : option;
4777
                    optionEl = selectEl.appendChild(optionEl);
4778
                    if (optionEl.value == selectedValue) {
4779
                        optionEl.selected = true;
4780
                    }
4781
                }
4782
            }
4783
            // Selected value is our only option
4784
            else {
4785
                selectEl.innerHTML = "<option selected value=\"" + selectedValue + "\">" + selectedValue + "</option>";
4786
            }
4787
        }
4788
        else {
4789
            el.innerHTML = lang.isValue(oData) ? oData : "";
4790
        }
4791
    },
4792
 
4793
    /**
4794
     * Formats emails.
4795
     *
4796
     * @method DataTable.formatEmail
4797
     * @param el {HTMLElement} The element to format with markup.
4798
     * @param oRecord {YAHOO.widget.Record} Record instance.
4799
     * @param oColumn {YAHOO.widget.Column} Column instance.
4800
     * @param oData {String} Data value for the cell, or null. Values are
4801
     * HTML-escaped.
4802
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4803
     * @static
4804
     */
4805
    formatEmail : function(el, oRecord, oColumn, oData, oDataTable) {
4806
        if(lang.isString(oData)) {
4807
            oData = lang.escapeHTML(oData);
4808
            el.innerHTML = "<a href=\"mailto:" + oData + "\">" + oData + "</a>";
4809
        }
4810
        else {
4811
            el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : "";
4812
        }
4813
    },
4814
 
4815
    /**
4816
     * Formats links.
4817
     *
4818
     * @method DataTable.formatLink
4819
     * @param el {HTMLElement} The element to format with markup.
4820
     * @param oRecord {YAHOO.widget.Record} Record instance.
4821
     * @param oColumn {YAHOO.widget.Column} Column instance.
4822
     * @param oData {String} Data value for the cell, or null. Values are
4823
     * HTML-escaped
4824
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4825
     * @static
4826
     */
4827
    formatLink : function(el, oRecord, oColumn, oData, oDataTable) {
4828
        if(lang.isString(oData)) {
4829
            oData = lang.escapeHTML(oData);
4830
            el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>";
4831
        }
4832
        else {
4833
            el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : "";
4834
        }
4835
    },
4836
 
4837
    /**
4838
     * Formats numbers.
4839
     *
4840
     * @method DataTable.formatNumber
4841
     * @param el {HTMLElement} The element to format with markup.
4842
     * @param oRecord {YAHOO.widget.Record} Record instance.
4843
     * @param oColumn {YAHOO.widget.Column} Column instance.
4844
     * @param oData {Object} Data value for the cell, or null.
4845
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4846
     * @static
4847
     */
4848
    formatNumber : function(el, oRecord, oColumn, oData, oDataTable) {
4849
        var oDT = oDataTable || this;
4850
        el.innerHTML = util.Number.format(oData, oColumn.numberOptions || oDT.get("numberOptions"));
4851
    },
4852
 
4853
    /**
4854
     * Formats INPUT TYPE=RADIO elements.
4855
     *
4856
     * @method DataTable.formatRadio
4857
     * @param el {HTMLElement} The element to format with markup.
4858
     * @param oRecord {YAHOO.widget.Record} Record instance.
4859
     * @param oColumn {YAHOO.widget.Column} Column instance.
4860
     * @param oData {Object} (Optional) Data value for the cell.
4861
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4862
     * @static
4863
     */
4864
    formatRadio : function(el, oRecord, oColumn, oData, oDataTable) {
4865
        var oDT = oDataTable || this,
4866
            bChecked = oData;
4867
        bChecked = (bChecked) ? " checked=\"checked\"" : "";
4868
        el.innerHTML = "<input type=\"radio\"" + bChecked +
4869
                " name=\""+oDT.getId()+"-col-" + oColumn.getSanitizedKey() + "\"" +
4870
                " class=\"" + DT.CLASS_RADIO+ "\" />";
4871
    },
4872
 
4873
    /**
4874
     * Formats text strings.
4875
     *
4876
     * @method DataTable.formatText
4877
     * @param el {HTMLElement} The element to format with markup.
4878
     * @param oRecord {YAHOO.widget.Record} Record instance.
4879
     * @param oColumn {YAHOO.widget.Column} Column instance.
4880
     * @param oData {String} (Optional) Data value for the cell. Values are
4881
     * HTML-escaped.
4882
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4883
     * @static
4884
     */
4885
    formatText : function(el, oRecord, oColumn, oData, oDataTable) {
4886
        var value = (lang.isValue(oData)) ? oData : "";
4887
        el.innerHTML = lang.escapeHTML(value.toString());
4888
    },
4889
 
4890
    /**
4891
     * Formats TEXTAREA elements.
4892
     *
4893
     * @method DataTable.formatTextarea
4894
     * @param el {HTMLElement} The element to format with markup.
4895
     * @param oRecord {YAHOO.widget.Record} Record instance.
4896
     * @param oColumn {YAHOO.widget.Column} Column instance.
4897
     * @param oData {Object} (Optional) Data value for the cell. Values are
4898
     * HTML-escaped.
4899
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4900
     * @static
4901
     */
4902
    formatTextarea : function(el, oRecord, oColumn, oData, oDataTable) {
4903
        var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "",
4904
            markup = "<textarea>" + value + "</textarea>";
4905
        el.innerHTML = markup;
4906
    },
4907
 
4908
    /**
4909
     * Formats INPUT TYPE=TEXT elements.
4910
     *
4911
     * @method DataTable.formatTextbox
4912
     * @param el {HTMLElement} The element to format with markup.
4913
     * @param oRecord {YAHOO.widget.Record} Record instance.
4914
     * @param oColumn {YAHOO.widget.Column} Column instance.
4915
     * @param oData {Object} (Optional) Data value for the cell. Values are
4916
     * HTML-escaped.
4917
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4918
     * @static
4919
     */
4920
    formatTextbox : function(el, oRecord, oColumn, oData, oDataTable) {
4921
        var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "",
4922
            markup = "<input type=\"text\" value=\"" + value + "\" />";
4923
        el.innerHTML = markup;
4924
    },
4925
 
4926
    /**
4927
     * Default cell formatter
4928
     *
4929
     * @method DataTable.formatDefault
4930
     * @param el {HTMLElement} The element to format with markup.
4931
     * @param oRecord {YAHOO.widget.Record} Record instance.
4932
     * @param oColumn {YAHOO.widget.Column} Column instance.
4933
     * @param oData {HTML} (Optional) Data value for the cell. String values are
4934
     * treated as markup and inserted into the DOM with innerHTML.
4935
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
4936
     * @static
4937
     */
4938
    formatDefault : function(el, oRecord, oColumn, oData, oDataTable) {
4939
        el.innerHTML = (lang.isValue(oData) && oData !== "") ? oData.toString() : "&#160;";
4940
    },
4941
 
4942
    /**
4943
     * Validates data value to type Number, doing type conversion as
4944
     * necessary. A valid Number value is return, else null is returned
4945
     * if input value does not validate.
4946
     *
4947
     *
4948
     * @method DataTable.validateNumber
4949
     * @param oData {Object} Data to validate.
4950
     * @static
4951
    */
4952
    validateNumber : function(oData) {
4953
        //Convert to number
4954
        var number = oData * 1;
4955
 
4956
        // Validate
4957
        if(lang.isNumber(number)) {
4958
            return number;
4959
        }
4960
        else {
4961
            return undefined;
4962
        }
4963
    }
4964
});
4965
 
4966
// Done in separate step so referenced functions are defined.
4967
/**
4968
 * Registry of cell formatting functions, enables shortcut pointers in Column
4969
 * definition formatter value (i.e., {key:"myColumn", formatter:"date"}).
4970
 * @property DataTable.Formatter
4971
 * @type Object
4972
 * @static
4973
 */
4974
DT.Formatter = {
4975
    button   : DT.formatButton,
4976
    checkbox : DT.formatCheckbox,
4977
    currency : DT.formatCurrency,
4978
    "date"   : DT.formatDate,
4979
    dropdown : DT.formatDropdown,
4980
    email    : DT.formatEmail,
4981
    link     : DT.formatLink,
4982
    "number" : DT.formatNumber,
4983
    radio    : DT.formatRadio,
4984
    text     : DT.formatText,
4985
    textarea : DT.formatTextarea,
4986
    textbox  : DT.formatTextbox,
4987
 
4988
    defaultFormatter : DT.formatDefault
4989
};
4990
 
4991
lang.extend(DT, util.Element, {
4992
 
4993
/////////////////////////////////////////////////////////////////////////////
4994
//
4995
// Superclass methods
4996
//
4997
/////////////////////////////////////////////////////////////////////////////
4998
 
4999
/**
5000
 * Implementation of Element's abstract method. Sets up config values.
5001
 *
5002
 * @method initAttributes
5003
 * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
5004
 * @private
5005
 */
5006
 
5007
initAttributes : function(oConfigs) {
5008
    oConfigs = oConfigs || {};
5009
    DT.superclass.initAttributes.call(this, oConfigs);
5010
 
5011
    /**
5012
    * @attribute summary
5013
    * @description String value for the SUMMARY attribute.
5014
    * @type String
5015
    * @default ""
5016
    */
5017
    this.setAttributeConfig("summary", {
5018
        value: "",
5019
        validator: lang.isString,
5020
        method: function(sSummary) {
5021
            if(this._elTable) {
5022
                this._elTable.summary = sSummary;
5023
            }
5024
        }
5025
    });
5026
 
5027
    /**
5028
    * @attribute selectionMode
5029
    * @description Specifies row or cell selection mode. Accepts the following strings:
5030
    *    <dl>
5031
    *      <dt>"standard"</dt>
5032
    *      <dd>Standard row selection with support for modifier keys to enable
5033
    *      multiple selections.</dd>
5034
    *
5035
    *      <dt>"single"</dt>
5036
    *      <dd>Row selection with modifier keys disabled to not allow
5037
    *      multiple selections.</dd>
5038
    *
5039
    *      <dt>"singlecell"</dt>
5040
    *      <dd>Cell selection with modifier keys disabled to not allow
5041
    *      multiple selections.</dd>
5042
    *
5043
    *      <dt>"cellblock"</dt>
5044
    *      <dd>Cell selection with support for modifier keys to enable multiple
5045
    *      selections in a block-fashion, like a spreadsheet.</dd>
5046
    *
5047
    *      <dt>"cellrange"</dt>
5048
    *      <dd>Cell selection with support for modifier keys to enable multiple
5049
    *      selections in a range-fashion, like a calendar.</dd>
5050
    *    </dl>
5051
    *
5052
    * @default "standard"
5053
    * @type String
5054
    */
5055
    this.setAttributeConfig("selectionMode", {
5056
        value: "standard",
5057
        validator: lang.isString
5058
    });
5059
 
5060
    /**
5061
    * @attribute sortedBy
5062
    * @description Object literal provides metadata for initial sort values if
5063
    * data will arrive pre-sorted:
5064
    * <dl>
5065
    *     <dt>sortedBy.key</dt>
5066
    *     <dd>{String} Key of sorted Column</dd>
5067
    *     <dt>sortedBy.dir</dt>
5068
    *     <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
5069
    * </dl>
5070
    * @type Object | null
5071
    */
5072
    this.setAttributeConfig("sortedBy", {
5073
        value: null,
5074
        // TODO: accepted array for nested sorts
5075
        validator: function(oNewSortedBy) {
5076
            if(oNewSortedBy) {
5077
                return (lang.isObject(oNewSortedBy) && oNewSortedBy.key);
5078
            }
5079
            else {
5080
                return (oNewSortedBy === null);
5081
            }
5082
        },
5083
        method: function(oNewSortedBy) {
5084
            // Stash the previous value
5085
            var oOldSortedBy = this.get("sortedBy");
5086
 
5087
            // Workaround for bug 1827195
5088
            this._configs.sortedBy.value = oNewSortedBy;
5089
 
5090
            // Remove ASC/DESC from TH
5091
            var oOldColumn,
5092
                nOldColumnKeyIndex,
5093
                oNewColumn,
5094
                nNewColumnKeyIndex;
5095
 
5096
            if(this._elThead) {
5097
                if(oOldSortedBy && oOldSortedBy.key && oOldSortedBy.dir) {
5098
                    oOldColumn = this._oColumnSet.getColumn(oOldSortedBy.key);
5099
                    nOldColumnKeyIndex = oOldColumn.getKeyIndex();
5100
 
5101
                    // Remove previous UI from THEAD
5102
                    var elOldTh = oOldColumn.getThEl();
5103
                    Dom.removeClass(elOldTh, oOldSortedBy.dir);
5104
                    this.formatTheadCell(oOldColumn.getThLinerEl().firstChild, oOldColumn, oNewSortedBy);
5105
                }
5106
                if(oNewSortedBy) {
5107
                    oNewColumn = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key);
5108
                    nNewColumnKeyIndex = oNewColumn.getKeyIndex();
5109
 
5110
                    // Update THEAD with new UI
5111
                    var elNewTh = oNewColumn.getThEl();
5112
                    // Backward compatibility
5113
                    if(oNewSortedBy.dir && ((oNewSortedBy.dir == "asc") ||  (oNewSortedBy.dir == "desc"))) {
5114
                        var newClass = (oNewSortedBy.dir == "desc") ?
5115
                                DT.CLASS_DESC :
5116
                                DT.CLASS_ASC;
5117
                        Dom.addClass(elNewTh, newClass);
5118
                    }
5119
                    else {
5120
                         var sortClass = oNewSortedBy.dir || DT.CLASS_ASC;
5121
                         Dom.addClass(elNewTh, sortClass);
5122
                    }
5123
                    this.formatTheadCell(oNewColumn.getThLinerEl().firstChild, oNewColumn, oNewSortedBy);
5124
                }
5125
            }
5126
 
5127
            if(this._elTbody) {
5128
                // Update TBODY UI
5129
                this._elTbody.style.display = "none";
5130
                var allRows = this._elTbody.rows,
5131
                    allCells;
5132
                for(var i=allRows.length-1; i>-1; i--) {
5133
                    allCells = allRows[i].childNodes;
5134
                    if(allCells[nOldColumnKeyIndex]) {
5135
                        Dom.removeClass(allCells[nOldColumnKeyIndex], oOldSortedBy.dir);
5136
                    }
5137
                    if(allCells[nNewColumnKeyIndex]) {
5138
                        Dom.addClass(allCells[nNewColumnKeyIndex], oNewSortedBy.dir);
5139
                    }
5140
                }
5141
                this._elTbody.style.display = "";
5142
            }
5143
 
5144
            this._clearTrTemplateEl();
5145
        }
5146
    });
5147
 
5148
    /**
5149
    * @attribute paginator
5150
    * @description An instance of YAHOO.widget.Paginator.
5151
    * @default null
5152
    * @type {Object|YAHOO.widget.Paginator}
5153
    */
5154
    this.setAttributeConfig("paginator", {
5155
        value : null,
5156
        validator : function (val) {
5157
            return val === null || val instanceof widget.Paginator;
5158
        },
5159
        method : function () { this._updatePaginator.apply(this,arguments); }
5160
    });
5161
 
5162
    /**
5163
    * @attribute caption
5164
    * @description Value for the CAPTION element. String values are treated as
5165
    * markup and inserted into the DOM with innerHTML. NB: Not supported in
5166
    * ScrollingDataTable.
5167
    * @type HTML
5168
    */
5169
    this.setAttributeConfig("caption", {
5170
        value: null,
5171
        validator: lang.isString,
5172
        method: function(sCaption) {
5173
            this._initCaptionEl(sCaption);
5174
        }
5175
    });
5176
 
5177
    /**
5178
    * @attribute draggableColumns
5179
    * @description True if Columns are draggable to reorder, false otherwise.
5180
    * The Drag & Drop Utility is required to enable this feature. Only top-level
5181
    * and non-nested Columns are draggable. Write once.
5182
    * @default false
5183
    * @type Boolean
5184
    */
5185
    this.setAttributeConfig("draggableColumns", {
5186
        value: false,
5187
        validator: lang.isBoolean,
5188
        method: function(oParam) {
5189
            if(this._elThead) {
5190
                if(oParam) {
5191
                    this._initDraggableColumns();
5192
                }
5193
                else {
5194
                    this._destroyDraggableColumns();
5195
                }
5196
            }
5197
        }
5198
    });
5199
 
5200
    /**
5201
    * @attribute renderLoopSize
5202
    * @description A value greater than 0 enables DOM rendering of rows to be
5203
    * executed from a non-blocking timeout queue and sets how many rows to be
5204
    * rendered per timeout. Recommended for very large data sets.
5205
    * @type Number
5206
    * @default 0
5207
    */
5208
     this.setAttributeConfig("renderLoopSize", {
5209
         value: 0,
5210
         validator: lang.isNumber
5211
     });
5212
 
5213
    /**
5214
    * @attribute sortFunction
5215
    * @description Default Column sort function, receives the following args:
5216
    *    <dl>
5217
    *      <dt>a {Object}</dt>
5218
    *      <dd>First sort argument.</dd>
5219
    *      <dt>b {Object}</dt>
5220
    *      <dd>Second sort argument.</dd>
5221
 
5222
    *      <dt>desc {Boolean}</dt>
5223
    *      <dd>True if sort direction is descending, false if
5224
    * sort direction is ascending.</dd>
5225
    *      <dt>field {String}</dt>
5226
    *      <dd>The field to sort by, from sortOptions.field</dd>
5227
    *   </dl>
5228
    * @type function
5229
    */
5230
    this.setAttributeConfig("sortFunction", {
5231
        value: function(a, b, desc, field) {
5232
            var compare = YAHOO.util.Sort.compare,
5233
                sorted = compare(a.getData(field),b.getData(field), desc);
5234
            if(sorted === 0) {
5235
                return compare(a.getCount(),b.getCount(), desc); // Bug 1932978
5236
            }
5237
            else {
5238
                return sorted;
5239
            }
5240
        }
5241
    });
5242
 
5243
    /**
5244
    * @attribute formatRow
5245
    * @description A function that accepts a TR element and its associated Record
5246
    * for custom formatting. The function must return TRUE in order to automatically
5247
    * continue formatting of child TD elements, else TD elements will not be
5248
    * automatically formatted.
5249
    * @type function
5250
    * @default null
5251
    */
5252
    this.setAttributeConfig("formatRow", {
5253
        value: null,
5254
        validator: lang.isFunction
5255
    });
5256
 
5257
    /**
5258
    * @attribute generateRequest
5259
    * @description A function that converts an object literal of desired DataTable
5260
    * states into a request value which is then passed to the DataSource's
5261
    * sendRequest method in order to retrieve data for those states. This
5262
    * function is passed an object literal of state data and a reference to the
5263
    * DataTable instance:
5264
    *
5265
    * <dl>
5266
    *   <dt>pagination<dt>
5267
    *   <dd>
5268
    *         <dt>offsetRecord</dt>
5269
    *         <dd>{Number} Index of the first Record of the desired page</dd>
5270
    *         <dt>rowsPerPage</dt>
5271
    *         <dd>{Number} Number of rows per page</dd>
5272
    *   </dd>
5273
    *   <dt>sortedBy</dt>
5274
    *   <dd>
5275
    *         <dt>key</dt>
5276
    *         <dd>{String} Key of sorted Column</dd>
5277
    *         <dt>dir</dt>
5278
    *         <dd>{String} Sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
5279
    *   </dd>
5280
    *   <dt>self</dt>
5281
    *   <dd>The DataTable instance</dd>
5282
    * </dl>
5283
    *
5284
    * and by default returns a String of syntax:
5285
    * "sort={sortColumn}&dir={sortDir}&startIndex={pageStartIndex}&results={rowsPerPage}"
5286
    * @type function
5287
    * @default HTMLFunction
5288
    */
5289
    this.setAttributeConfig("generateRequest", {
5290
        value: function(oState, oSelf) {
5291
            // Set defaults
5292
            oState = oState || {pagination:null, sortedBy:null};
5293
            var sort = encodeURIComponent((oState.sortedBy) ? oState.sortedBy.key : oSelf.getColumnSet().keys[0].getKey());
5294
            var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
5295
            var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
5296
            var results = (oState.pagination) ? oState.pagination.rowsPerPage : null;
5297
 
5298
            // Build the request
5299
            return  "sort=" + sort +
5300
                    "&dir=" + dir +
5301
                    "&startIndex=" + startIndex +
5302
                    ((results !== null) ? "&results=" + results : "");
5303
        },
5304
        validator: lang.isFunction
5305
    });
5306
 
5307
    /**
5308
    * @attribute initialRequest
5309
    * @description Defines the initial request that gets sent to the DataSource
5310
    * during initialization. Value is ignored if initialLoad is set to any value
5311
    * other than true.
5312
    * @type MIXED
5313
    * @default null
5314
    */
5315
    this.setAttributeConfig("initialRequest", {
5316
        value: null
5317
    });
5318
 
5319
    /**
5320
    * @attribute initialLoad
5321
    * @description Determines whether or not to load data at instantiation. By
5322
    * default, will trigger a sendRequest() to the DataSource and pass in the
5323
    * request defined by initialRequest. If set to false, data will not load
5324
    * at instantiation. Alternatively, implementers who wish to work with a
5325
    * custom payload may pass in an object literal with the following values:
5326
    *
5327
    *    <dl>
5328
    *      <dt>request (MIXED)</dt>
5329
    *      <dd>Request value.</dd>
5330
    *
5331
    *      <dt>argument (MIXED)</dt>
5332
    *      <dd>Custom data that will be passed through to the callback function.</dd>
5333
    *    </dl>
5334
    *
5335
    *
5336
    * @type Boolean | Object
5337
    * @default true
5338
    */
5339
    this.setAttributeConfig("initialLoad", {
5340
        value: true
5341
    });
5342
 
5343
    /**
5344
    * @attribute dynamicData
5345
    * @description If true, sorting and pagination are relegated to the DataSource
5346
    * for handling, using the request returned by the "generateRequest" function.
5347
    * Each new DataSource response blows away all previous Records. False by default, so
5348
    * sorting and pagination will be handled directly on the client side, without
5349
    * causing any new requests for data from the DataSource.
5350
    * @type Boolean
5351
    * @default false
5352
    */
5353
    this.setAttributeConfig("dynamicData", {
5354
        value: false,
5355
        validator: lang.isBoolean
5356
    });
5357
 
5358
    /**
5359
     * @attribute MSG_EMPTY
5360
     * @description Message to display if DataTable has no data. String
5361
     * values are treated as markup and inserted into the DOM with innerHTML.
5362
     * @type HTML
5363
     * @default "No records found."
5364
     */
5365
     this.setAttributeConfig("MSG_EMPTY", {
5366
         value: "No records found.",
5367
         validator: lang.isString
5368
     });
5369
 
5370
    /**
5371
     * @attribute MSG_LOADING
5372
     * @description Message to display while DataTable is loading data. String
5373
     * values are treated as markup and inserted into the DOM with innerHTML.
5374
     * @type HTML
5375
     * @default "Loading..."
5376
     */
5377
     this.setAttributeConfig("MSG_LOADING", {
5378
         value: "Loading...",
5379
         validator: lang.isString
5380
     });
5381
 
5382
    /**
5383
     * @attribute MSG_ERROR
5384
     * @description Message to display while DataTable has data error. String
5385
     * values are treated as markup and inserted into the DOM with innerHTML.
5386
     * @type HTML
5387
     * @default "Data error."
5388
     */
5389
     this.setAttributeConfig("MSG_ERROR", {
5390
         value: "Data error.",
5391
         validator: lang.isString
5392
     });
5393
 
5394
    /**
5395
     * @attribute MSG_SORTASC
5396
     * @description Message to display in tooltip to sort Column in ascending
5397
     * order. String values are treated as markup and inserted into the DOM as
5398
     * innerHTML.
5399
     * @type HTML
5400
     * @default "Click to sort ascending"
5401
     */
5402
     this.setAttributeConfig("MSG_SORTASC", {
5403
         value: "Click to sort ascending",
5404
         validator: lang.isString,
5405
         method: function(sParam) {
5406
            if(this._elThead) {
5407
                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
5408
                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_ASC) {
5409
                        allKeys[i]._elThLabel.firstChild.title = sParam;
5410
                    }
5411
                }
5412
            }
5413
         }
5414
     });
5415
 
5416
    /**
5417
     * @attribute MSG_SORTDESC
5418
     * @description Message to display in tooltip to sort Column in descending
5419
     * order. String values are treated as markup and inserted into the DOM as
5420
     * innerHTML.
5421
     * @type HTML
5422
     * @default "Click to sort descending"
5423
     */
5424
     this.setAttributeConfig("MSG_SORTDESC", {
5425
         value: "Click to sort descending",
5426
         validator: lang.isString,
5427
         method: function(sParam) {
5428
            if(this._elThead) {
5429
                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
5430
                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_DESC) {
5431
                        allKeys[i]._elThLabel.firstChild.title = sParam;
5432
                    }
5433
                }
5434
            }
5435
         }
5436
     });
5437
 
5438
    /**
5439
     * @attribute currencySymbol
5440
     * @deprecated Use currencyOptions.
5441
     */
5442
    this.setAttributeConfig("currencySymbol", {
5443
        value: "$",
5444
        validator: lang.isString
5445
    });
5446
 
5447
    /**
5448
     * Default config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
5449
     * @attribute currencyOptions
5450
     * @type Object
5451
     * @default {prefix: $, decimalPlaces:2, decimalSeparator:".", thousandsSeparator:","}
5452
     */
5453
    this.setAttributeConfig("currencyOptions", {
5454
        value: {
5455
            prefix: this.get("currencySymbol"), // TODO: deprecate currencySymbol
5456
            decimalPlaces:2,
5457
            decimalSeparator:".",
5458
            thousandsSeparator:","
5459
        }
5460
    });
5461
 
5462
    /**
5463
     * Default config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
5464
     * @attribute dateOptions
5465
     * @type Object
5466
     * @default {format:"%m/%d/%Y", locale:"en"}
5467
     */
5468
    this.setAttributeConfig("dateOptions", {
5469
        value: {format:"%m/%d/%Y", locale:"en"}
5470
    });
5471
 
5472
    /**
5473
     * Default config passed to YAHOO.util.Number.format() by the 'number' Column formatter.
5474
     * @attribute numberOptions
5475
     * @type Object
5476
     * @default {decimalPlaces:0, thousandsSeparator:","}
5477
     */
5478
    this.setAttributeConfig("numberOptions", {
5479
        value: {
5480
            decimalPlaces:0,
5481
            thousandsSeparator:","
5482
        }
5483
    });
5484
 
5485
},
5486
 
5487
/////////////////////////////////////////////////////////////////////////////
5488
//
5489
// Private member variables
5490
//
5491
/////////////////////////////////////////////////////////////////////////////
5492
 
5493
/**
5494
 * True if instance is initialized, so as to fire the initEvent after render.
5495
 *
5496
 * @property _bInit
5497
 * @type Boolean
5498
 * @default true
5499
 * @private
5500
 */
5501
_bInit : true,
5502
 
5503
/**
5504
 * Index assigned to instance.
5505
 *
5506
 * @property _nIndex
5507
 * @type Number
5508
 * @private
5509
 */
5510
_nIndex : null,
5511
 
5512
/**
5513
 * Counter for IDs assigned to TR elements.
5514
 *
5515
 * @property _nTrCount
5516
 * @type Number
5517
 * @private
5518
 */
5519
_nTrCount : 0,
5520
 
5521
/**
5522
 * Counter for IDs assigned to TD elements.
5523
 *
5524
 * @property _nTdCount
5525
 * @type Number
5526
 * @private
5527
 */
5528
_nTdCount : 0,
5529
 
5530
/**
5531
 * Unique id assigned to instance "yui-dtN", useful prefix for generating unique
5532
 * DOM ID strings and log messages.
5533
 *
5534
 * @property _sId
5535
 * @type String
5536
 * @private
5537
 */
5538
_sId : null,
5539
 
5540
/**
5541
 * Render chain.
5542
 *
5543
 * @property _oChainRender
5544
 * @type YAHOO.util.Chain
5545
 * @private
5546
 */
5547
_oChainRender : null,
5548
 
5549
/**
5550
 * DOM reference to the container element for the DataTable instance into which
5551
 * all other elements get created.
5552
 *
5553
 * @property _elContainer
5554
 * @type HTMLElement
5555
 * @private
5556
 */
5557
_elContainer : null,
5558
 
5559
/**
5560
 * DOM reference to the mask element for the DataTable instance which disables it.
5561
 *
5562
 * @property _elMask
5563
 * @type HTMLElement
5564
 * @private
5565
 */
5566
_elMask : null,
5567
 
5568
/**
5569
 * DOM reference to the TABLE element for the DataTable instance.
5570
 *
5571
 * @property _elTable
5572
 * @type HTMLElement
5573
 * @private
5574
 */
5575
_elTable : null,
5576
 
5577
/**
5578
 * DOM reference to the CAPTION element for the DataTable instance.
5579
 *
5580
 * @property _elCaption
5581
 * @type HTMLElement
5582
 * @private
5583
 */
5584
_elCaption : null,
5585
 
5586
/**
5587
 * DOM reference to the COLGROUP element for the DataTable instance.
5588
 *
5589
 * @property _elColgroup
5590
 * @type HTMLElement
5591
 * @private
5592
 */
5593
_elColgroup : null,
5594
 
5595
/**
5596
 * DOM reference to the THEAD element for the DataTable instance.
5597
 *
5598
 * @property _elThead
5599
 * @type HTMLElement
5600
 * @private
5601
 */
5602
_elThead : null,
5603
 
5604
/**
5605
 * DOM reference to the primary TBODY element for the DataTable instance.
5606
 *
5607
 * @property _elTbody
5608
 * @type HTMLElement
5609
 * @private
5610
 */
5611
_elTbody : null,
5612
 
5613
/**
5614
 * DOM reference to the secondary TBODY element used to display DataTable messages.
5615
 *
5616
 * @property _elMsgTbody
5617
 * @type HTMLElement
5618
 * @private
5619
 */
5620
_elMsgTbody : null,
5621
 
5622
/**
5623
 * DOM reference to the secondary TBODY element's single TR element used to display DataTable messages.
5624
 *
5625
 * @property _elMsgTr
5626
 * @type HTMLElement
5627
 * @private
5628
 */
5629
_elMsgTr : null,
5630
 
5631
/**
5632
 * DOM reference to the secondary TBODY element's single TD element used to display DataTable messages.
5633
 *
5634
 * @property _elMsgTd
5635
 * @type HTMLElement
5636
 * @private
5637
 */
5638
_elMsgTd : null,
5639
 
5640
/**
5641
 * Element reference to shared Column drag target.
5642
 *
5643
 * @property _elColumnDragTarget
5644
 * @type HTMLElement
5645
 * @private
5646
 */
5647
_elColumnDragTarget : null,
5648
 
5649
/**
5650
 * Element reference to shared Column resizer proxy.
5651
 *
5652
 * @property _elColumnResizerProxy
5653
 * @type HTMLElement
5654
 * @private
5655
 */
5656
_elColumnResizerProxy : null,
5657
 
5658
/**
5659
 * DataSource instance for the DataTable instance.
5660
 *
5661
 * @property _oDataSource
5662
 * @type YAHOO.util.DataSource
5663
 * @private
5664
 */
5665
_oDataSource : null,
5666
 
5667
/**
5668
 * ColumnSet instance for the DataTable instance.
5669
 *
5670
 * @property _oColumnSet
5671
 * @type YAHOO.widget.ColumnSet
5672
 * @private
5673
 */
5674
_oColumnSet : null,
5675
 
5676
/**
5677
 * RecordSet instance for the DataTable instance.
5678
 *
5679
 * @property _oRecordSet
5680
 * @type YAHOO.widget.RecordSet
5681
 * @private
5682
 */
5683
_oRecordSet : null,
5684
 
5685
/**
5686
 * The active CellEditor instance for the DataTable instance.
5687
 *
5688
 * @property _oCellEditor
5689
 * @type YAHOO.widget.CellEditor
5690
 * @private
5691
 */
5692
_oCellEditor : null,
5693
 
5694
/**
5695
 * ID string of first TR element of the current DataTable page.
5696
 *
5697
 * @property _sFirstTrId
5698
 * @type String
5699
 * @private
5700
 */
5701
_sFirstTrId : null,
5702
 
5703
/**
5704
 * ID string of the last TR element of the current DataTable page.
5705
 *
5706
 * @property _sLastTrId
5707
 * @type String
5708
 * @private
5709
 */
5710
_sLastTrId : null,
5711
 
5712
/**
5713
 * Template row to create all new rows from.
5714
 * @property _elTrTemplate
5715
 * @type {HTMLElement}
5716
 * @private
5717
 */
5718
_elTrTemplate : null,
5719
 
5720
/**
5721
 * Sparse array of custom functions to set column widths for browsers that don't
5722
 * support dynamic CSS rules.  Functions are added at the index representing
5723
 * the number of rows they update.
5724
 *
5725
 * @property _aDynFunctions
5726
 * @type Array
5727
 * @private
5728
 */
5729
_aDynFunctions : [],
5730
 
5731
/**
5732
 * Disabled state.
5733
 *
5734
 * @property _disabled
5735
 * @type Boolean
5736
 * @private
5737
 */
5738
_disabled : false,
5739
 
5740
 
5741
 
5742
 
5743
 
5744
 
5745
 
5746
 
5747
 
5748
 
5749
 
5750
 
5751
 
5752
 
5753
 
5754
 
5755
 
5756
 
5757
 
5758
 
5759
 
5760
 
5761
 
5762
 
5763
 
5764
 
5765
 
5766
 
5767
/////////////////////////////////////////////////////////////////////////////
5768
//
5769
// Private methods
5770
//
5771
/////////////////////////////////////////////////////////////////////////////
5772
 
5773
/**
5774
 * Clears browser text selection. Useful to call on rowSelectEvent or
5775
 * cellSelectEvent to prevent clicks or dblclicks from selecting text in the
5776
 * browser.
5777
 *
5778
 * @method clearTextSelection
5779
 */
5780
clearTextSelection : function() {
5781
    var sel;
5782
    if(window.getSelection) {
5783
        sel = window.getSelection();
5784
    }
5785
    else if(document.getSelection) {
5786
        sel = document.getSelection();
5787
    }
5788
    else if(document.selection) {
5789
        sel = document.selection;
5790
    }
5791
    if(sel) {
5792
        if(sel.empty) {
5793
            sel.empty();
5794
        }
5795
        else if (sel.removeAllRanges) {
5796
            sel.removeAllRanges();
5797
        }
5798
        else if(sel.collapse) {
5799
            sel.collapse();
5800
        }
5801
    }
5802
},
5803
 
5804
/**
5805
 * Sets focus on the given element.
5806
 *
5807
 * @method _focusEl
5808
 * @param el {HTMLElement} Element.
5809
 * @private
5810
 */
5811
_focusEl : function(el) {
5812
    el = el || this._elTbody;
5813
    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
5814
    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
5815
    // strange unexpected things as the user clicks on buttons and other controls.
5816
    setTimeout(function() {
5817
        try {
5818
            el.focus();
5819
        }
5820
        catch(e) {
5821
        }
5822
    },0);
5823
},
5824
 
5825
/**
5826
 * Forces Gecko repaint.
5827
 *
5828
 * @method _repaintGecko
5829
 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
5830
 * @private
5831
 */
5832
_repaintGecko : (ua.gecko) ?
5833
    function(el) {
5834
        el = el || this._elContainer;
5835
        var parent = el.parentNode;
5836
        var nextSibling = el.nextSibling;
5837
        parent.insertBefore(parent.removeChild(el), nextSibling);
5838
    } : function() {},
5839
 
5840
/**
5841
 * Forces Opera repaint.
5842
 *
5843
 * @method _repaintOpera
5844
 * @private
5845
 */
5846
_repaintOpera : (ua.opera) ?
5847
    function() {
5848
        if(ua.opera) {
5849
            document.documentElement.className += " ";
5850
            document.documentElement.className = YAHOO.lang.trim(document.documentElement.className);
5851
        }
5852
    } : function() {} ,
5853
 
5854
/**
5855
 * Forces Webkit repaint.
5856
 *
5857
 * @method _repaintWebkit
5858
 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
5859
 * @private
5860
 */
5861
_repaintWebkit : (ua.webkit) ?
5862
    function(el) {
5863
        el = el || this._elContainer;
5864
        var parent = el.parentNode;
5865
        var nextSibling = el.nextSibling;
5866
        parent.insertBefore(parent.removeChild(el), nextSibling);
5867
    } : function() {},
5868
 
5869
 
5870
 
5871
 
5872
 
5873
 
5874
 
5875
 
5876
 
5877
 
5878
 
5879
 
5880
 
5881
 
5882
 
5883
 
5884
 
5885
 
5886
 
5887
 
5888
 
5889
 
5890
// INIT FUNCTIONS
5891
 
5892
/**
5893
 * Initializes object literal of config values.
5894
 *
5895
 * @method _initConfigs
5896
 * @param oConfig {Object} Object literal of config values.
5897
 * @private
5898
 */
5899
_initConfigs : function(oConfigs) {
5900
    if(!oConfigs || !lang.isObject(oConfigs)) {
5901
        oConfigs = {};
5902
    }
5903
    this.configs = oConfigs;
5904
},
5905
 
5906
/**
5907
 * Initializes ColumnSet.
5908
 *
5909
 * @method _initColumnSet
5910
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
5911
 * @private
5912
 */
5913
_initColumnSet : function(aColumnDefs) {
5914
    var oColumn, i, len;
5915
 
5916
    if(this._oColumnSet) {
5917
        // First clear _oDynStyles for existing ColumnSet and
5918
        // uregister CellEditor Custom Events
5919
        for(i=0, len=this._oColumnSet.keys.length; i<len; i++) {
5920
            oColumn = this._oColumnSet.keys[i];
5921
            DT._oDynStyles["."+this.getId()+"-col-"+oColumn.getSanitizedKey()+" ."+DT.CLASS_LINER] = undefined;
5922
            if(oColumn.editor && oColumn.editor.unsubscribeAll) { // Backward compatibility
5923
                oColumn.editor.unsubscribeAll();
5924
            }
5925
        }
5926
 
5927
        this._oColumnSet = null;
5928
        this._clearTrTemplateEl();
5929
    }
5930
 
5931
    if(lang.isArray(aColumnDefs)) {
5932
        this._oColumnSet =  new YAHOO.widget.ColumnSet(aColumnDefs);
5933
    }
5934
    // Backward compatibility
5935
    else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) {
5936
        this._oColumnSet =  aColumnDefs;
5937
    }
5938
 
5939
    // Register CellEditor Custom Events
5940
    var allKeys = this._oColumnSet.keys;
5941
    for(i=0, len=allKeys.length; i<len; i++) {
5942
        oColumn = allKeys[i];
5943
        if(oColumn.editor && oColumn.editor.subscribe) { // Backward incompatibility
5944
            oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
5945
            oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
5946
            oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
5947
            oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
5948
            oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
5949
            oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
5950
            oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
5951
            oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
5952
        }
5953
    }
5954
},
5955
 
5956
/**
5957
 * Initializes DataSource.
5958
 *
5959
 * @method _initDataSource
5960
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
5961
 * @private
5962
 */
5963
_initDataSource : function(oDataSource) {
5964
    this._oDataSource = null;
5965
    if(oDataSource && (lang.isFunction(oDataSource.sendRequest))) {
5966
        this._oDataSource = oDataSource;
5967
    }
5968
    // Backward compatibility
5969
    else {
5970
        var tmpTable = null;
5971
        var tmpContainer = this._elContainer;
5972
        var i=0;
5973
        //TODO: this will break if re-initing DS at runtime for SDT
5974
        // Peek in container child nodes to see if TABLE already exists
5975
        if(tmpContainer.hasChildNodes()) {
5976
            var tmpChildren = tmpContainer.childNodes;
5977
            for(i=0; i<tmpChildren.length; i++) {
5978
                if(tmpChildren[i].nodeName && tmpChildren[i].nodeName.toLowerCase() == "table") {
5979
                    tmpTable = tmpChildren[i];
5980
                    break;
5981
                }
5982
            }
5983
            if(tmpTable) {
5984
                var tmpFieldsArray = [];
5985
                for(; i<this._oColumnSet.keys.length; i++) {
5986
                    tmpFieldsArray.push({key:this._oColumnSet.keys[i].key});
5987
                }
5988
 
5989
                this._oDataSource = new DS(tmpTable);
5990
                this._oDataSource.responseType = DS.TYPE_HTMLTABLE;
5991
                this._oDataSource.responseSchema = {fields: tmpFieldsArray};
5992
            }
5993
        }
5994
    }
5995
},
5996
 
5997
/**
5998
 * Initializes RecordSet.
5999
 *
6000
 * @method _initRecordSet
6001
 * @private
6002
 */
6003
_initRecordSet : function() {
6004
    if(this._oRecordSet) {
6005
        this._oRecordSet.reset();
6006
    }
6007
    else {
6008
        this._oRecordSet = new YAHOO.widget.RecordSet();
6009
    }
6010
},
6011
 
6012
/**
6013
 * Initializes DOM elements.
6014
 *
6015
 * @method _initDomElements
6016
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
6017
 * return {Boolean} False in case of error, otherwise true
6018
 * @private
6019
 */
6020
_initDomElements : function(elContainer) {
6021
    // Outer container
6022
    this._initContainerEl(elContainer);
6023
    // TABLE
6024
    this._initTableEl(this._elContainer);
6025
    // COLGROUP
6026
    this._initColgroupEl(this._elTable);
6027
    // THEAD
6028
    this._initTheadEl(this._elTable);
6029
 
6030
    // Message TBODY
6031
    this._initMsgTbodyEl(this._elTable);
6032
 
6033
    // Primary TBODY
6034
    this._initTbodyEl(this._elTable);
6035
 
6036
    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody) {
6037
        return false;
6038
    }
6039
    else {
6040
        return true;
6041
    }
6042
},
6043
 
6044
/**
6045
 * Destroy's the DataTable outer container element, if available.
6046
 *
6047
 * @method _destroyContainerEl
6048
 * @param elContainer {HTMLElement} Reference to the container element.
6049
 * @private
6050
 */
6051
_destroyContainerEl : function(elContainer) {
6052
        var columns = this._oColumnSet.keys,
6053
        elements, i;
6054
 
6055
        Dom.removeClass(elContainer, DT.CLASS_DATATABLE);
6056
 
6057
    // Bug 2528783
6058
    Ev.purgeElement( elContainer );
6059
    Ev.purgeElement( this._elThead, true ); // recursive to get resize handles
6060
    Ev.purgeElement( this._elTbody );
6061
    Ev.purgeElement( this._elMsgTbody );
6062
 
6063
    // because change doesn't bubble, each select (via formatDropdown) gets
6064
    // its own subscription
6065
    elements = elContainer.getElementsByTagName( 'select' );
6066
 
6067
    if ( elements.length ) {
6068
        Ev.detachListener( elements, 'change' );
6069
    }
6070
 
6071
    for ( i = columns.length - 1; i >= 0; --i ) {
6072
        if ( columns[i].editor ) {
6073
            Ev.purgeElement( columns[i].editor._elContainer );
6074
        }
6075
    }
6076
 
6077
    elContainer.innerHTML = "";
6078
 
6079
    this._elContainer = null;
6080
    this._elColgroup = null;
6081
    this._elThead = null;
6082
    this._elTbody = null;
6083
},
6084
 
6085
/**
6086
 * Initializes the DataTable outer container element, including a mask.
6087
 *
6088
 * @method _initContainerEl
6089
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
6090
 * @private
6091
 */
6092
_initContainerEl : function(elContainer) {
6093
    // Validate container
6094
    elContainer = Dom.get(elContainer);
6095
 
6096
    if(elContainer && elContainer.nodeName && (elContainer.nodeName.toLowerCase() == "div")) {
6097
        // Destroy previous
6098
        this._destroyContainerEl(elContainer);
6099
 
6100
        Dom.addClass(elContainer, DT.CLASS_DATATABLE);
6101
        Ev.addListener(elContainer, "focus", this._onTableFocus, this);
6102
        Ev.addListener(elContainer, "dblclick", this._onTableDblclick, this);
6103
        this._elContainer = elContainer;
6104
 
6105
        var elMask = document.createElement("div");
6106
        elMask.className = DT.CLASS_MASK;
6107
        elMask.style.display = "none";
6108
        this._elMask = elContainer.appendChild(elMask);
6109
    }
6110
},
6111
 
6112
/**
6113
 * Destroy's the DataTable TABLE element, if available.
6114
 *
6115
 * @method _destroyTableEl
6116
 * @private
6117
 */
6118
_destroyTableEl : function() {
6119
    var elTable = this._elTable;
6120
    if(elTable) {
6121
        Ev.purgeElement(elTable, true);
6122
        elTable.parentNode.removeChild(elTable);
6123
        this._elCaption = null;
6124
        this._elColgroup = null;
6125
        this._elThead = null;
6126
        this._elTbody = null;
6127
    }
6128
},
6129
 
6130
/**
6131
 * Creates HTML markup CAPTION element.
6132
 *
6133
 * @method _initCaptionEl
6134
 * @param sCaption {HTML} Caption value. String values are treated as markup and
6135
 * inserted into the DOM with innerHTML.
6136
 * @private
6137
 */
6138
_initCaptionEl : function(sCaption) {
6139
    if(this._elTable && sCaption) {
6140
        // Create CAPTION element
6141
        if(!this._elCaption) {
6142
            this._elCaption = this._elTable.createCaption();
6143
        }
6144
        // Set CAPTION value
6145
        this._elCaption.innerHTML = sCaption;
6146
    }
6147
    else if(this._elCaption) {
6148
        this._elCaption.parentNode.removeChild(this._elCaption);
6149
    }
6150
},
6151
 
6152
/**
6153
 * Creates HTML markup for TABLE, COLGROUP, THEAD and TBODY elements in outer
6154
 * container element.
6155
 *
6156
 * @method _initTableEl
6157
 * @param elContainer {HTMLElement} Container element into which to create TABLE.
6158
 * @private
6159
 */
6160
_initTableEl : function(elContainer) {
6161
    if(elContainer) {
6162
        // Destroy previous
6163
        this._destroyTableEl();
6164
 
6165
        // Create TABLE
6166
        this._elTable = elContainer.appendChild(document.createElement("table"));
6167
 
6168
        // Set SUMMARY attribute
6169
        this._elTable.summary = this.get("summary");
6170
 
6171
        // Create CAPTION element
6172
        if(this.get("caption")) {
6173
            this._initCaptionEl(this.get("caption"));
6174
        }
6175
 
6176
        // Set up mouseover/mouseout events via mouseenter/mouseleave delegation
6177
        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this);
6178
        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this);
6179
        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-data>tr>td", this);
6180
        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-data>tr>td", this);
6181
        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-message>tr>td", this);
6182
        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-message>tr>td", this);
6183
    }
6184
},
6185
 
6186
/**
6187
 * Destroy's the DataTable COLGROUP element, if available.
6188
 *
6189
 * @method _destroyColgroupEl
6190
 * @private
6191
 */
6192
_destroyColgroupEl : function() {
6193
    var elColgroup = this._elColgroup;
6194
    if(elColgroup) {
6195
        var elTable = elColgroup.parentNode;
6196
        Ev.purgeElement(elColgroup, true);
6197
        elTable.removeChild(elColgroup);
6198
        this._elColgroup = null;
6199
    }
6200
},
6201
 
6202
/**
6203
 * Initializes COLGROUP and COL elements for managing minWidth.
6204
 *
6205
 * @method _initColgroupEl
6206
 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
6207
 * @private
6208
 */
6209
_initColgroupEl : function(elTable) {
6210
    if(elTable) {
6211
        // Destroy previous
6212
        this._destroyColgroupEl();
6213
 
6214
        // Add COLs to DOCUMENT FRAGMENT
6215
        var allCols = this._aColIds || [],
6216
            allKeys = this._oColumnSet.keys,
6217
            i = 0, len = allCols.length,
6218
            elCol, oColumn,
6219
            elFragment = document.createDocumentFragment(),
6220
            elColTemplate = document.createElement("col");
6221
 
6222
        for(i=0,len=allKeys.length; i<len; i++) {
6223
            oColumn = allKeys[i];
6224
            elCol = elFragment.appendChild(elColTemplate.cloneNode(false));
6225
        }
6226
 
6227
        // Create COLGROUP
6228
        var elColgroup = elTable.insertBefore(document.createElement("colgroup"), elTable.firstChild);
6229
        elColgroup.appendChild(elFragment);
6230
        this._elColgroup = elColgroup;
6231
    }
6232
},
6233
 
6234
/**
6235
 * Adds a COL element to COLGROUP at given index.
6236
 *
6237
 * @method _insertColgroupColEl
6238
 * @param index {Number} Index of new COL element.
6239
 * @private
6240
 */
6241
_insertColgroupColEl : function(index) {
6242
    if(lang.isNumber(index)&& this._elColgroup) {
6243
        var nextSibling = this._elColgroup.childNodes[index] || null;
6244
        this._elColgroup.insertBefore(document.createElement("col"), nextSibling);
6245
    }
6246
},
6247
 
6248
/**
6249
 * Removes a COL element to COLGROUP at given index.
6250
 *
6251
 * @method _removeColgroupColEl
6252
 * @param index {Number} Index of removed COL element.
6253
 * @private
6254
 */
6255
_removeColgroupColEl : function(index) {
6256
    if(lang.isNumber(index) && this._elColgroup && this._elColgroup.childNodes[index]) {
6257
        this._elColgroup.removeChild(this._elColgroup.childNodes[index]);
6258
    }
6259
},
6260
 
6261
/**
6262
 * Reorders a COL element from old index(es) to new index.
6263
 *
6264
 * @method _reorderColgroupColEl
6265
 * @param aKeyIndexes {Number[]} Array of indexes of removed COL element.
6266
 * @param newIndex {Number} New index.
6267
 * @private
6268
 */
6269
_reorderColgroupColEl : function(aKeyIndexes, newIndex) {
6270
    if(lang.isArray(aKeyIndexes) && lang.isNumber(newIndex) && this._elColgroup && (this._elColgroup.childNodes.length > aKeyIndexes[aKeyIndexes.length-1])) {
6271
        var i,
6272
            tmpCols = [];
6273
        // Remove COL
6274
        for(i=aKeyIndexes.length-1; i>-1; i--) {
6275
            tmpCols.push(this._elColgroup.removeChild(this._elColgroup.childNodes[aKeyIndexes[i]]));
6276
        }
6277
        // Insert COL
6278
        var nextSibling = this._elColgroup.childNodes[newIndex] || null;
6279
        for(i=tmpCols.length-1; i>-1; i--) {
6280
            this._elColgroup.insertBefore(tmpCols[i], nextSibling);
6281
        }
6282
    }
6283
},
6284
 
6285
/**
6286
 * Destroy's the DataTable THEAD element, if available.
6287
 *
6288
 * @method _destroyTheadEl
6289
 * @private
6290
 */
6291
_destroyTheadEl : function() {
6292
    var elThead = this._elThead;
6293
    if(elThead) {
6294
        var elTable = elThead.parentNode;
6295
        Ev.purgeElement(elThead, true);
6296
        this._destroyColumnHelpers();
6297
        elTable.removeChild(elThead);
6298
        this._elThead = null;
6299
    }
6300
},
6301
 
6302
/**
6303
 * Initializes THEAD element.
6304
 *
6305
 * @method _initTheadEl
6306
 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
6307
 * @param {HTMLElement} Initialized THEAD element.
6308
 * @private
6309
 */
6310
_initTheadEl : function(elTable) {
6311
    elTable = elTable || this._elTable;
6312
 
6313
    if(elTable) {
6314
        // Destroy previous
6315
        this._destroyTheadEl();
6316
 
6317
        //TODO: append to DOM later for performance
6318
        var elThead = (this._elColgroup) ?
6319
            elTable.insertBefore(document.createElement("thead"), this._elColgroup.nextSibling) :
6320
            elTable.appendChild(document.createElement("thead"));
6321
 
6322
        // Set up DOM events for THEAD
6323
        Ev.addListener(elThead, "focus", this._onTheadFocus, this);
6324
        Ev.addListener(elThead, "keydown", this._onTheadKeydown, this);
6325
        Ev.addListener(elThead, "mousedown", this._onTableMousedown, this);
6326
        Ev.addListener(elThead, "mouseup", this._onTableMouseup, this);
6327
        Ev.addListener(elThead, "click", this._onTheadClick, this);
6328
 
6329
        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
6330
        // delegation at the TABLE level
6331
 
6332
        // Since we can't listen for click and dblclick on the same element...
6333
        // Attach separately to THEAD and TBODY
6334
        ///Ev.addListener(elThead, "dblclick", this._onTableDblclick, this);
6335
 
6336
       var oColumnSet = this._oColumnSet,
6337
            oColumn, i,j, l;
6338
 
6339
        // Add TRs to the THEAD
6340
        var colTree = oColumnSet.tree;
6341
        var elTh;
6342
        for(i=0; i<colTree.length; i++) {
6343
            var elTheadTr = elThead.appendChild(document.createElement("tr"));
6344
 
6345
            // ...and create TH cells
6346
            for(j=0; j<colTree[i].length; j++) {
6347
                oColumn = colTree[i][j];
6348
                elTh = elTheadTr.appendChild(document.createElement("th"));
6349
                this._initThEl(elTh,oColumn);
6350
            }
6351
 
6352
                // Set FIRST/LAST on THEAD rows
6353
                if(i === 0) {
6354
                    Dom.addClass(elTheadTr, DT.CLASS_FIRST);
6355
                }
6356
                if(i === (colTree.length-1)) {
6357
                    Dom.addClass(elTheadTr, DT.CLASS_LAST);
6358
                }
6359
 
6360
        }
6361
 
6362
        // Set FIRST/LAST on edge TH elements using the values in ColumnSet headers array
6363
        var aFirstHeaders = oColumnSet.headers[0] || [];
6364
        for(i=0; i<aFirstHeaders.length; i++) {
6365
            Dom.addClass(Dom.get(this.getId() +"-th-"+aFirstHeaders[i]), DT.CLASS_FIRST);
6366
        }
6367
        var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1] || [];
6368
        for(i=0; i<aLastHeaders.length; i++) {
6369
            Dom.addClass(Dom.get(this.getId() +"-th-"+aLastHeaders[i]), DT.CLASS_LAST);
6370
        }
6371
 
6372
 
6373
        ///TODO: try _repaintGecko(this._elContainer) instead
6374
        // Bug 1806891
6375
        if(ua.webkit && ua.webkit < 420) {
6376
            var oSelf = this;
6377
            setTimeout(function() {
6378
                elThead.style.display = "";
6379
            },0);
6380
            elThead.style.display = 'none';
6381
        }
6382
 
6383
        this._elThead = elThead;
6384
 
6385
        // Column helpers needs _elThead to exist
6386
        this._initColumnHelpers();
6387
    }
6388
},
6389
 
6390
/**
6391
 * Populates TH element as defined by Column.
6392
 *
6393
 * @method _initThEl
6394
 * @param elTh {HTMLElement} TH element reference.
6395
 * @param oColumn {YAHOO.widget.Column} Column object.
6396
 * @private
6397
 */
6398
_initThEl : function(elTh, oColumn) {
6399
    elTh.id = this.getId() + "-th-" + oColumn.getSanitizedKey(); // Needed for accessibility, getColumn by TH, and ColumnDD
6400
    elTh.innerHTML = "";
6401
    elTh.rowSpan = oColumn.getRowspan();
6402
    elTh.colSpan = oColumn.getColspan();
6403
    oColumn._elTh = elTh;
6404
 
6405
    var elThLiner = elTh.appendChild(document.createElement("div"));
6406
    elThLiner.id = elTh.id + "-liner"; // Needed for resizer
6407
    elThLiner.className = DT.CLASS_LINER;
6408
    oColumn._elThLiner = elThLiner;
6409
 
6410
    var elThLabel = elThLiner.appendChild(document.createElement("span"));
6411
    elThLabel.className = DT.CLASS_LABEL;
6412
 
6413
    // Assign abbr attribute
6414
    if(oColumn.abbr) {
6415
        elTh.abbr = oColumn.abbr;
6416
    }
6417
    // Clear minWidth on hidden Columns
6418
    if(oColumn.hidden) {
6419
        this._clearMinWidth(oColumn);
6420
    }
6421
 
6422
    elTh.className = this._getColumnClassNames(oColumn);
6423
 
6424
    // Set Column width...
6425
    if(oColumn.width) {
6426
        // Validate minWidth
6427
        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
6428
                oColumn.minWidth : oColumn.width;
6429
        // ...for fallback cases
6430
        if(DT._bDynStylesFallback) {
6431
            elTh.firstChild.style.overflow = 'hidden';
6432
            elTh.firstChild.style.width = nWidth + 'px';
6433
        }
6434
        // ...for non fallback cases
6435
        else {
6436
            this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden');
6437
        }
6438
    }
6439
 
6440
    this.formatTheadCell(elThLabel, oColumn, this.get("sortedBy"));
6441
    oColumn._elThLabel = elThLabel;
6442
},
6443
 
6444
/**
6445
 * Outputs markup into the given TH based on given Column.
6446
 *
6447
 * @method formatTheadCell
6448
 * @param elCellLabel {HTMLElement} The label SPAN element within the TH liner,
6449
 * not the liner DIV element.
6450
 * @param oColumn {YAHOO.widget.Column} Column instance.
6451
 * @param oSortedBy {Object} Sort state object literal.
6452
*/
6453
formatTheadCell : function(elCellLabel, oColumn, oSortedBy) {
6454
    var sKey = oColumn.getKey();
6455
    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
6456
 
6457
    // Add accessibility link for sortable Columns
6458
    if(oColumn.sortable) {
6459
        // Calculate the direction
6460
        var sSortClass = this.getColumnSortDir(oColumn, oSortedBy);
6461
        var bDesc = (sSortClass === DT.CLASS_DESC);
6462
 
6463
        // This is the sorted Column
6464
        if(oSortedBy && (oColumn.key === oSortedBy.key)) {
6465
            bDesc = !(oSortedBy.dir === DT.CLASS_DESC);
6466
        }
6467
 
6468
        // Generate a unique HREF for visited status
6469
        var sHref = this.getId() + "-href-" + oColumn.getSanitizedKey();
6470
 
6471
        // Generate a dynamic TITLE for sort status
6472
        var sTitle = (bDesc) ? this.get("MSG_SORTDESC") : this.get("MSG_SORTASC");
6473
 
6474
        // Format the element
6475
        elCellLabel.innerHTML = "<a href=\"" + sHref + "\" title=\"" + sTitle + "\" class=\"" + DT.CLASS_SORTABLE + "\">" + sLabel + "</a>";
6476
    }
6477
    // Just display the label for non-sortable Columns
6478
    else {
6479
        elCellLabel.innerHTML = sLabel;
6480
    }
6481
},
6482
 
6483
/**
6484
 * Disables DD from top-level Column TH elements.
6485
 *
6486
 * @method _destroyDraggableColumns
6487
 * @private
6488
 */
6489
_destroyDraggableColumns : function() {
6490
    var oColumn, elTh;
6491
    for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
6492
        oColumn = this._oColumnSet.tree[0][i];
6493
        if(oColumn._dd) {
6494
            oColumn._dd = oColumn._dd.unreg();
6495
            Dom.removeClass(oColumn.getThEl(), DT.CLASS_DRAGGABLE);
6496
        }
6497
    }
6498
 
6499
    // Destroy column drag proxy
6500
    this._destroyColumnDragTargetEl();
6501
},
6502
 
6503
/**
6504
 * Initializes top-level Column TH elements into DD instances.
6505
 *
6506
 * @method _initDraggableColumns
6507
 * @private
6508
 */
6509
_initDraggableColumns : function() {
6510
    this._destroyDraggableColumns();
6511
    if(util.DD) {
6512
        var oColumn, elTh, elDragTarget;
6513
        for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
6514
            oColumn = this._oColumnSet.tree[0][i];
6515
            elTh = oColumn.getThEl();
6516
            Dom.addClass(elTh, DT.CLASS_DRAGGABLE);
6517
            elDragTarget = this._initColumnDragTargetEl();
6518
            oColumn._dd = new YAHOO.widget.ColumnDD(this, oColumn, elTh, elDragTarget);
6519
        }
6520
    }
6521
    else {
6522
    }
6523
},
6524
 
6525
/**
6526
 * Destroys shared Column drag target.
6527
 *
6528
 * @method _destroyColumnDragTargetEl
6529
 * @private
6530
 */
6531
_destroyColumnDragTargetEl : function() {
6532
    if(this._elColumnDragTarget) {
6533
        var el = this._elColumnDragTarget;
6534
        YAHOO.util.Event.purgeElement(el);
6535
        el.parentNode.removeChild(el);
6536
        this._elColumnDragTarget = null;
6537
    }
6538
},
6539
 
6540
/**
6541
 * Creates HTML markup for shared Column drag target.
6542
 *
6543
 * @method _initColumnDragTargetEl
6544
 * @return {HTMLElement} Reference to Column drag target.
6545
 * @private
6546
 */
6547
_initColumnDragTargetEl : function() {
6548
    if(!this._elColumnDragTarget) {
6549
        // Attach Column drag target element as first child of body
6550
        var elColumnDragTarget = document.createElement('div');
6551
        elColumnDragTarget.id = this.getId() + "-coltarget";
6552
        elColumnDragTarget.className = DT.CLASS_COLTARGET;
6553
        elColumnDragTarget.style.display = "none";
6554
        document.body.insertBefore(elColumnDragTarget, document.body.firstChild);
6555
 
6556
        // Internal tracker of Column drag target
6557
        this._elColumnDragTarget = elColumnDragTarget;
6558
 
6559
    }
6560
    return this._elColumnDragTarget;
6561
},
6562
 
6563
/**
6564
 * Disables resizeability on key Column TH elements.
6565
 *
6566
 * @method _destroyResizeableColumns
6567
 * @private
6568
 */
6569
_destroyResizeableColumns : function() {
6570
    var aKeys = this._oColumnSet.keys;
6571
    for(var i=0, len=aKeys.length; i<len; i++) {
6572
        if(aKeys[i]._ddResizer) {
6573
            aKeys[i]._ddResizer = aKeys[i]._ddResizer.unreg();
6574
            Dom.removeClass(aKeys[i].getThEl(), DT.CLASS_RESIZEABLE);
6575
        }
6576
    }
6577
 
6578
    // Destroy resizer proxy
6579
    this._destroyColumnResizerProxyEl();
6580
},
6581
 
6582
/**
6583
 * Initializes resizeability on key Column TH elements.
6584
 *
6585
 * @method _initResizeableColumns
6586
 * @private
6587
 */
6588
_initResizeableColumns : function() {
6589
    this._destroyResizeableColumns();
6590
    if(util.DD) {
6591
        var oColumn, elTh, elThLiner, elThResizerLiner, elThResizer, elResizerProxy, cancelClick;
6592
        for(var i=0, len=this._oColumnSet.keys.length; i<len; i++) {
6593
            oColumn = this._oColumnSet.keys[i];
6594
            if(oColumn.resizeable) {
6595
                elTh = oColumn.getThEl();
6596
                Dom.addClass(elTh, DT.CLASS_RESIZEABLE);
6597
                elThLiner = oColumn.getThLinerEl();
6598
 
6599
                // Bug 1915349: So resizer is as tall as TH when rowspan > 1
6600
                // Create a separate resizer liner with position:relative
6601
                elThResizerLiner = elTh.appendChild(document.createElement("div"));
6602
                elThResizerLiner.className = DT.CLASS_RESIZERLINER;
6603
 
6604
                // Move TH contents into the new resizer liner
6605
                elThResizerLiner.appendChild(elThLiner);
6606
 
6607
                // Create the resizer
6608
                elThResizer = elThResizerLiner.appendChild(document.createElement("div"));
6609
                elThResizer.id = elTh.id + "-resizer"; // Needed for ColumnResizer
6610
                elThResizer.className = DT.CLASS_RESIZER;
6611
                oColumn._elResizer = elThResizer;
6612
 
6613
                // Create the resizer proxy, once per instance
6614
                elResizerProxy = this._initColumnResizerProxyEl();
6615
                oColumn._ddResizer = new YAHOO.util.ColumnResizer(
6616
                        this, oColumn, elTh, elThResizer, elResizerProxy);
6617
                cancelClick = function(e) {
6618
                    Ev.stopPropagation(e);
6619
                };
6620
                Ev.addListener(elThResizer,"click",cancelClick);
6621
            }
6622
        }
6623
    }
6624
    else {
6625
    }
6626
},
6627
 
6628
/**
6629
 * Destroys shared Column resizer proxy.
6630
 *
6631
 * @method _destroyColumnResizerProxyEl
6632
 * @return {HTMLElement} Reference to Column resizer proxy.
6633
 * @private
6634
 */
6635
_destroyColumnResizerProxyEl : function() {
6636
    if(this._elColumnResizerProxy) {
6637
        var el = this._elColumnResizerProxy;
6638
        YAHOO.util.Event.purgeElement(el);
6639
        el.parentNode.removeChild(el);
6640
        this._elColumnResizerProxy = null;
6641
    }
6642
},
6643
 
6644
/**
6645
 * Creates HTML markup for shared Column resizer proxy.
6646
 *
6647
 * @method _initColumnResizerProxyEl
6648
 * @return {HTMLElement} Reference to Column resizer proxy.
6649
 * @private
6650
 */
6651
_initColumnResizerProxyEl : function() {
6652
    if(!this._elColumnResizerProxy) {
6653
        // Attach Column resizer element as first child of body
6654
        var elColumnResizerProxy = document.createElement("div");
6655
        elColumnResizerProxy.id = this.getId() + "-colresizerproxy"; // Needed for ColumnResizer
6656
        elColumnResizerProxy.className = DT.CLASS_RESIZERPROXY;
6657
        document.body.insertBefore(elColumnResizerProxy, document.body.firstChild);
6658
 
6659
        // Internal tracker of Column resizer proxy
6660
        this._elColumnResizerProxy = elColumnResizerProxy;
6661
    }
6662
    return this._elColumnResizerProxy;
6663
},
6664
 
6665
/**
6666
 * Destroys elements associated with Column functionality: ColumnDD and ColumnResizers.
6667
 *
6668
 * @method _destroyColumnHelpers
6669
 * @private
6670
 */
6671
_destroyColumnHelpers : function() {
6672
    this._destroyDraggableColumns();
6673
    this._destroyResizeableColumns();
6674
},
6675
 
6676
/**
6677
 * Initializes elements associated with Column functionality: ColumnDD and ColumnResizers.
6678
 *
6679
 * @method _initColumnHelpers
6680
 * @private
6681
 */
6682
_initColumnHelpers : function() {
6683
    if(this.get("draggableColumns")) {
6684
        this._initDraggableColumns();
6685
    }
6686
    this._initResizeableColumns();
6687
},
6688
 
6689
/**
6690
 * Destroy's the DataTable TBODY element, if available.
6691
 *
6692
 * @method _destroyTbodyEl
6693
 * @private
6694
 */
6695
_destroyTbodyEl : function() {
6696
    var elTbody = this._elTbody;
6697
    if(elTbody) {
6698
        var elTable = elTbody.parentNode;
6699
        Ev.purgeElement(elTbody, true);
6700
        elTable.removeChild(elTbody);
6701
        this._elTbody = null;
6702
    }
6703
},
6704
 
6705
/**
6706
 * Initializes TBODY element for data.
6707
 *
6708
 * @method _initTbodyEl
6709
 * @param elTable {HTMLElement} TABLE element into which to create TBODY .
6710
 * @private
6711
 */
6712
_initTbodyEl : function(elTable) {
6713
    if(elTable) {
6714
        // Destroy previous
6715
        this._destroyTbodyEl();
6716
 
6717
        // Create TBODY
6718
        var elTbody = elTable.appendChild(document.createElement("tbody"));
6719
        elTbody.tabIndex = 0;
6720
        elTbody.className = DT.CLASS_DATA;
6721
 
6722
        // Set up DOM events for TBODY
6723
        Ev.addListener(elTbody, "focus", this._onTbodyFocus, this);
6724
        Ev.addListener(elTbody, "mousedown", this._onTableMousedown, this);
6725
        Ev.addListener(elTbody, "mouseup", this._onTableMouseup, this);
6726
        Ev.addListener(elTbody, "keydown", this._onTbodyKeydown, this);
6727
        Ev.addListener(elTbody, "click", this._onTbodyClick, this);
6728
 
6729
        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
6730
        // delegation at the TABLE level
6731
 
6732
        // Since we can't listen for click and dblclick on the same element...
6733
        // Attach separately to THEAD and TBODY
6734
        ///Ev.addListener(elTbody, "dblclick", this._onTableDblclick, this);
6735
 
6736
 
6737
        // IE puts focus outline in the wrong place
6738
        if(ua.ie) {
6739
            elTbody.hideFocus=true;
6740
        }
6741
 
6742
        this._elTbody = elTbody;
6743
    }
6744
},
6745
 
6746
/**
6747
 * Destroy's the DataTable message TBODY element, if available.
6748
 *
6749
 * @method _destroyMsgTbodyEl
6750
 * @private
6751
 */
6752
_destroyMsgTbodyEl : function() {
6753
    var elMsgTbody = this._elMsgTbody;
6754
    if(elMsgTbody) {
6755
        var elTable = elMsgTbody.parentNode;
6756
        Ev.purgeElement(elMsgTbody, true);
6757
        elTable.removeChild(elMsgTbody);
6758
        this._elTbody = null;
6759
    }
6760
},
6761
 
6762
/**
6763
 * Initializes TBODY element for messaging.
6764
 *
6765
 * @method _initMsgTbodyEl
6766
 * @param elTable {HTMLElement} TABLE element into which to create TBODY
6767
 * @private
6768
 */
6769
_initMsgTbodyEl : function(elTable) {
6770
    if(elTable) {
6771
        var elMsgTbody = document.createElement("tbody");
6772
        elMsgTbody.className = DT.CLASS_MESSAGE;
6773
        var elMsgTr = elMsgTbody.appendChild(document.createElement("tr"));
6774
        elMsgTr.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
6775
        this._elMsgTr = elMsgTr;
6776
        var elMsgTd = elMsgTr.appendChild(document.createElement("td"));
6777
        elMsgTd.colSpan = this._oColumnSet.keys.length || 1;
6778
        elMsgTd.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
6779
        this._elMsgTd = elMsgTd;
6780
        elMsgTbody = elTable.insertBefore(elMsgTbody, this._elTbody);
6781
        var elMsgLiner = elMsgTd.appendChild(document.createElement("div"));
6782
        elMsgLiner.className = DT.CLASS_LINER;
6783
        this._elMsgTbody = elMsgTbody;
6784
 
6785
        // Set up DOM events for TBODY
6786
        Ev.addListener(elMsgTbody, "focus", this._onTbodyFocus, this);
6787
        Ev.addListener(elMsgTbody, "mousedown", this._onTableMousedown, this);
6788
        Ev.addListener(elMsgTbody, "mouseup", this._onTableMouseup, this);
6789
        Ev.addListener(elMsgTbody, "keydown", this._onTbodyKeydown, this);
6790
        Ev.addListener(elMsgTbody, "click", this._onTbodyClick, this);
6791
 
6792
        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
6793
        // delegation at the TABLE level
6794
    }
6795
},
6796
 
6797
/**
6798
 * Initialize internal event listeners
6799
 *
6800
 * @method _initEvents
6801
 * @private
6802
 */
6803
_initEvents : function () {
6804
    // Initialize Column sort
6805
    this._initColumnSort();
6806
 
6807
    // Add the document level click listener
6808
    YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this);
6809
 
6810
    // Paginator integration
6811
    this.subscribe("paginatorChange",function () {
6812
        this._handlePaginatorChange.apply(this,arguments);
6813
    });
6814
 
6815
    this.subscribe("initEvent",function () {
6816
        this.renderPaginator();
6817
    });
6818
 
6819
    // Initialize CellEditor integration
6820
    this._initCellEditing();
6821
},
6822
 
6823
/**
6824
  * Initializes Column sorting.
6825
  *
6826
  * @method _initColumnSort
6827
  * @private
6828
  */
6829
_initColumnSort : function() {
6830
    this.subscribe("theadCellClickEvent", this.onEventSortColumn);
6831
 
6832
    // Backward compatibility
6833
    var oSortedBy = this.get("sortedBy");
6834
    if(oSortedBy) {
6835
        if(oSortedBy.dir == "desc") {
6836
            this._configs.sortedBy.value.dir = DT.CLASS_DESC;
6837
        }
6838
        else if(oSortedBy.dir == "asc") {
6839
            this._configs.sortedBy.value.dir = DT.CLASS_ASC;
6840
        }
6841
    }
6842
},
6843
 
6844
/**
6845
  * Initializes CellEditor integration.
6846
  *
6847
  * @method _initCellEditing
6848
  * @private
6849
  */
6850
_initCellEditing : function() {
6851
    this.subscribe("editorBlurEvent",function () {
6852
        this.onEditorBlurEvent.apply(this,arguments);
6853
    });
6854
    this.subscribe("editorBlockEvent",function () {
6855
        this.onEditorBlockEvent.apply(this,arguments);
6856
    });
6857
    this.subscribe("editorUnblockEvent",function () {
6858
        this.onEditorUnblockEvent.apply(this,arguments);
6859
    });
6860
},
6861
 
6862
 
6863
 
6864
 
6865
 
6866
 
6867
 
6868
 
6869
 
6870
 
6871
 
6872
 
6873
 
6874
 
6875
 
6876
 
6877
 
6878
 
6879
 
6880
 
6881
 
6882
 
6883
 
6884
 
6885
 
6886
 
6887
 
6888
 
6889
 
6890
 
6891
 
6892
 
6893
 
6894
// DOM MUTATION FUNCTIONS
6895
 
6896
/**
6897
 * Retruns classnames to represent current Column states.
6898
 * @method _getColumnClassnames
6899
 * @param oColumn {YAHOO.widget.Column} Column instance.
6900
 * @param aAddClasses {String[]} An array of additional classnames to add to the
6901
 * return value.
6902
 * @return {String} A String of classnames to be assigned to TH or TD elements
6903
 * for given Column.
6904
 * @private
6905
 */
6906
_getColumnClassNames : function (oColumn, aAddClasses) {
6907
    var allClasses;
6908
 
6909
    // Add CSS classes
6910
    if(lang.isString(oColumn.className)) {
6911
        // Single custom class
6912
        allClasses = [oColumn.className];
6913
    }
6914
    else if(lang.isArray(oColumn.className)) {
6915
        // Array of custom classes
6916
        allClasses = oColumn.className;
6917
    }
6918
    else {
6919
        // no custom classes
6920
        allClasses = [];
6921
    }
6922
 
6923
    // Hook for setting width with via dynamic style uses key since ID is too disposable
6924
    allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey();
6925
 
6926
    // Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
6927
    allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
6928
 
6929
    var isSortedBy = this.get("sortedBy") || {};
6930
    // Sorted
6931
    if(oColumn.key === isSortedBy.key) {
6932
        allClasses[allClasses.length] = isSortedBy.dir || '';
6933
    }
6934
    // Hidden
6935
    if(oColumn.hidden) {
6936
        allClasses[allClasses.length] = DT.CLASS_HIDDEN;
6937
    }
6938
    // Selected
6939
    if(oColumn.selected) {
6940
        allClasses[allClasses.length] = DT.CLASS_SELECTED;
6941
    }
6942
    // Sortable
6943
    if(oColumn.sortable) {
6944
        allClasses[allClasses.length] = DT.CLASS_SORTABLE;
6945
    }
6946
    // Resizeable
6947
    if(oColumn.resizeable) {
6948
        allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
6949
    }
6950
    // Editable
6951
    if(oColumn.editor) {
6952
        allClasses[allClasses.length] = DT.CLASS_EDITABLE;
6953
    }
6954
 
6955
    // Addtnl classes, including First/Last
6956
    if(aAddClasses) {
6957
        allClasses = allClasses.concat(aAddClasses);
6958
    }
6959
 
6960
    return allClasses.join(' ');
6961
},
6962
 
6963
/**
6964
 * Clears TR element template in response to any Column state change.
6965
 * @method _clearTrTemplateEl
6966
 * @private
6967
 */
6968
_clearTrTemplateEl : function () {
6969
    this._elTrTemplate = null;
6970
},
6971
 
6972
/**
6973
 * Returns a new TR element template with TD elements classed with current
6974
 * Column states.
6975
 * @method _getTrTemplateEl
6976
 * @return {HTMLElement} A TR element to be cloned and added to the DOM.
6977
 * @private
6978
 */
6979
_getTrTemplateEl : function (oRecord, index) {
6980
    // Template is already available
6981
    if(this._elTrTemplate) {
6982
        return this._elTrTemplate;
6983
    }
6984
    // Template needs to be created
6985
    else {
6986
        var d   = document,
6987
            tr  = d.createElement('tr'),
6988
            td  = d.createElement('td'),
6989
            div = d.createElement('div');
6990
 
6991
        // Append the liner element
6992
        td.appendChild(div);
6993
 
6994
        // Create TD elements into DOCUMENT FRAGMENT
6995
        var df = document.createDocumentFragment(),
6996
            allKeys = this._oColumnSet.keys,
6997
            elTd;
6998
 
6999
        // Set state for each TD;
7000
        var aAddClasses;
7001
        for(var i=0, keysLen=allKeys.length; i<keysLen; i++) {
7002
            // Clone the TD template
7003
            elTd = td.cloneNode(true);
7004
 
7005
            // Format the base TD
7006
            elTd = this._formatTdEl(allKeys[i], elTd, i, (i===keysLen-1));
7007
 
7008
            df.appendChild(elTd);
7009
        }
7010
        tr.appendChild(df);
7011
        tr.className = DT.CLASS_REC;
7012
        this._elTrTemplate = tr;
7013
        return tr;
7014
    }
7015
},
7016
 
7017
/**
7018
 * Formats a basic TD element.
7019
 * @method _formatTdEl
7020
 * @param oColumn {YAHOO.widget.Column} Associated Column instance.
7021
 * @param elTd {HTMLElement} An unformatted TD element.
7022
 * @param index {Number} Column key index.
7023
 * @param isLast {Boolean} True if Column is last key of the ColumnSet.
7024
 * @return {HTMLElement} A formatted TD element.
7025
 * @private
7026
 */
7027
_formatTdEl : function (oColumn, elTd, index, isLast) {
7028
    var oColumnSet = this._oColumnSet;
7029
 
7030
    // Set the TD's accessibility headers
7031
    var allHeaders = oColumnSet.headers,
7032
        allColHeaders = allHeaders[index],
7033
        sTdHeaders = "",
7034
        sHeader;
7035
    for(var j=0, headersLen=allColHeaders.length; j < headersLen; j++) {
7036
        sHeader = this._sId + "-th-" + allColHeaders[j] + ' ';
7037
        sTdHeaders += sHeader;
7038
    }
7039
    elTd.headers = sTdHeaders;
7040
 
7041
    // Class the TD element
7042
    var aAddClasses = [];
7043
    if(index === 0) {
7044
        aAddClasses[aAddClasses.length] = DT.CLASS_FIRST;
7045
    }
7046
    if(isLast) {
7047
        aAddClasses[aAddClasses.length] = DT.CLASS_LAST;
7048
    }
7049
    elTd.className = this._getColumnClassNames(oColumn, aAddClasses);
7050
 
7051
    // Class the liner element
7052
    elTd.firstChild.className = DT.CLASS_LINER;
7053
 
7054
    // Set Column width for fallback cases
7055
    if(oColumn.width && DT._bDynStylesFallback) {
7056
        // Validate minWidth
7057
        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
7058
                oColumn.minWidth : oColumn.width;
7059
        elTd.firstChild.style.overflow = 'hidden';
7060
        elTd.firstChild.style.width = nWidth + 'px';
7061
    }
7062
 
7063
    return elTd;
7064
},
7065
 
7066
 
7067
/**
7068
 * Create a new TR element for a given Record and appends it with the correct
7069
 * number of Column-state-classed TD elements. Striping is the responsibility of
7070
 * the calling function, which may decide to stripe the single row, a subset of
7071
 * rows, or all the rows.
7072
 * @method _createTrEl
7073
 * @param oRecord {YAHOO.widget.Record} Record instance
7074
 * @return {HTMLElement} The new TR element.  This must be added to the DOM.
7075
 * @private
7076
 */
7077
_addTrEl : function (oRecord) {
7078
    var elTrTemplate = this._getTrTemplateEl();
7079
 
7080
    // Clone the TR template.
7081
    var elTr = elTrTemplate.cloneNode(true);
7082
 
7083
    // Populate content
7084
    return this._updateTrEl(elTr,oRecord);
7085
},
7086
 
7087
/**
7088
 * Formats the contents of the given TR's TD elements with data from the given
7089
 * Record. Only innerHTML should change, nothing structural.
7090
 *
7091
 * @method _updateTrEl
7092
 * @param elTr {HTMLElement} The TR element to update.
7093
 * @param oRecord {YAHOO.widget.Record} The associated Record instance.
7094
 * @return {HTMLElement} DOM reference to the new TR element.
7095
 * @private
7096
 */
7097
_updateTrEl : function(elTr, oRecord) {
7098
    var ok = this.get("formatRow") ? this.get("formatRow").call(this, elTr, oRecord) : true;
7099
    if(ok) {
7100
        // Hide the row to prevent constant reflows
7101
        elTr.style.display = 'none';
7102
 
7103
        // Update TD elements with new data
7104
        var allTds = elTr.childNodes,
7105
            elTd;
7106
        for(var i=0,len=allTds.length; i<len; ++i) {
7107
            elTd = allTds[i];
7108
 
7109
            // Set the cell content
7110
            this.formatCell(allTds[i].firstChild, oRecord, this._oColumnSet.keys[i]);
7111
        }
7112
 
7113
        // Redisplay the row for reflow
7114
        elTr.style.display = '';
7115
    }
7116
 
7117
     // Record-to-TR association and tracking of FIRST/LAST
7118
    var oldId = elTr.id,
7119
        newId = oRecord.getId();
7120
    if(this._sFirstTrId === oldId) {
7121
        this._sFirstTrId = newId;
7122
    }
7123
    if(this._sLastTrId === oldId) {
7124
        this._sLastTrId = newId;
7125
    }
7126
    elTr.id = newId;
7127
    return elTr;
7128
},
7129
 
7130
 
7131
/**
7132
 * Deletes TR element by DOM reference or by DataTable page row index.
7133
 *
7134
 * @method _deleteTrEl
7135
 * @param row {HTMLElement | Number} TR element reference or Datatable page row index.
7136
 * @return {Boolean} Returns true if successful, else returns false.
7137
 * @private
7138
 */
7139
_deleteTrEl : function(row) {
7140
    var rowIndex;
7141
 
7142
    // Get page row index for the element
7143
    if(!lang.isNumber(row)) {
7144
        rowIndex = Dom.get(row).sectionRowIndex;
7145
    }
7146
    else {
7147
        rowIndex = row;
7148
    }
7149
    if(lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
7150
        // Cannot use tbody.deleteRow due to IE6 instability
7151
        //return this._elTbody.deleteRow(rowIndex);
7152
        return this._elTbody.removeChild(this._elTbody.rows[row]);
7153
    }
7154
    else {
7155
        return null;
7156
    }
7157
},
7158
 
7159
 
7160
 
7161
 
7162
 
7163
 
7164
 
7165
 
7166
 
7167
 
7168
 
7169
 
7170
 
7171
 
7172
 
7173
 
7174
 
7175
 
7176
 
7177
 
7178
 
7179
 
7180
 
7181
 
7182
 
7183
 
7184
 
7185
// CSS/STATE FUNCTIONS
7186
 
7187
 
7188
 
7189
 
7190
/**
7191
 * Removes the class YAHOO.widget.DataTable.CLASS_FIRST from the first TR element
7192
 * of the DataTable page and updates internal tracker.
7193
 *
7194
 * @method _unsetFirstRow
7195
 * @private
7196
 */
7197
_unsetFirstRow : function() {
7198
    // Remove FIRST
7199
    if(this._sFirstTrId) {
7200
        Dom.removeClass(this._sFirstTrId, DT.CLASS_FIRST);
7201
        this._sFirstTrId = null;
7202
    }
7203
},
7204
 
7205
/**
7206
 * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element
7207
 * of the DataTable page and updates internal tracker.
7208
 *
7209
 * @method _setFirstRow
7210
 * @private
7211
 */
7212
_setFirstRow : function() {
7213
    this._unsetFirstRow();
7214
    var elTr = this.getFirstTrEl();
7215
    if(elTr) {
7216
        // Set FIRST
7217
        Dom.addClass(elTr, DT.CLASS_FIRST);
7218
        this._sFirstTrId = elTr.id;
7219
    }
7220
},
7221
 
7222
/**
7223
 * Removes the class YAHOO.widget.DataTable.CLASS_LAST from the last TR element
7224
 * of the DataTable page and updates internal tracker.
7225
 *
7226
 * @method _unsetLastRow
7227
 * @private
7228
 */
7229
_unsetLastRow : function() {
7230
    // Unassign previous class
7231
    if(this._sLastTrId) {
7232
        Dom.removeClass(this._sLastTrId, DT.CLASS_LAST);
7233
        this._sLastTrId = null;
7234
    }
7235
},
7236
 
7237
/**
7238
 * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element
7239
 * of the DataTable page and updates internal tracker.
7240
 *
7241
 * @method _setLastRow
7242
 * @private
7243
 */
7244
_setLastRow : function() {
7245
    this._unsetLastRow();
7246
    var elTr = this.getLastTrEl();
7247
    if(elTr) {
7248
        // Assign class
7249
        Dom.addClass(elTr, DT.CLASS_LAST);
7250
        this._sLastTrId = elTr.id;
7251
    }
7252
},
7253
 
7254
/**
7255
 * Assigns the classes DT.CLASS_EVEN and DT.CLASS_ODD to one, many, or all TR elements.
7256
 *
7257
 * @method _setRowStripes
7258
 * @param row {HTMLElement | String | Number} (optional) HTML TR element reference
7259
 * or string ID, or page row index of where to start striping.
7260
 * @param range {Number} (optional) If given, how many rows to stripe, otherwise
7261
 * stripe all the rows until the end.
7262
 * @private
7263
 */
7264
_setRowStripes : function(row, range) {
7265
    // Default values stripe all rows
7266
    var allRows = this._elTbody.rows,
7267
        nStartIndex = 0,
7268
        nEndIndex = allRows.length,
7269
        aOdds = [], nOddIdx = 0,
7270
        aEvens = [], nEvenIdx = 0;
7271
 
7272
    // Stripe a subset
7273
    if((row !== null) && (row !== undefined)) {
7274
        // Validate given start row
7275
        var elStartRow = this.getTrEl(row);
7276
        if(elStartRow) {
7277
            nStartIndex = elStartRow.sectionRowIndex;
7278
 
7279
            // Validate given range
7280
            if(lang.isNumber(range) && (range > 1)) {
7281
                nEndIndex = nStartIndex + range;
7282
            }
7283
        }
7284
    }
7285
 
7286
    for(var i=nStartIndex; i<nEndIndex; i++) {
7287
        if(i%2) {
7288
            aOdds[nOddIdx++] = allRows[i];
7289
        } else {
7290
            aEvens[nEvenIdx++] = allRows[i];
7291
        }
7292
    }
7293
 
7294
    if (aOdds.length) {
7295
        Dom.replaceClass(aOdds, DT.CLASS_EVEN, DT.CLASS_ODD);
7296
    }
7297
 
7298
    if (aEvens.length) {
7299
        Dom.replaceClass(aEvens, DT.CLASS_ODD, DT.CLASS_EVEN);
7300
    }
7301
},
7302
 
7303
/**
7304
 * Assigns the class DT.CLASS_SELECTED to TR and TD elements.
7305
 *
7306
 * @method _setSelections
7307
 * @private
7308
 */
7309
_setSelections : function() {
7310
    // Keep track of selected rows
7311
    var allSelectedRows = this.getSelectedRows();
7312
    // Keep track of selected cells
7313
    var allSelectedCells = this.getSelectedCells();
7314
    // Anything to select?
7315
    if((allSelectedRows.length>0) || (allSelectedCells.length > 0)) {
7316
        var oColumnSet = this._oColumnSet,
7317
            el;
7318
        // Loop over each row
7319
        for(var i=0; i<allSelectedRows.length; i++) {
7320
            el = Dom.get(allSelectedRows[i]);
7321
            if(el) {
7322
                Dom.addClass(el, DT.CLASS_SELECTED);
7323
            }
7324
        }
7325
        // Loop over each cell
7326
        for(i=0; i<allSelectedCells.length; i++) {
7327
            el = Dom.get(allSelectedCells[i].recordId);
7328
            if(el) {
7329
                Dom.addClass(el.childNodes[oColumnSet.getColumn(allSelectedCells[i].columnKey).getKeyIndex()], DT.CLASS_SELECTED);
7330
            }
7331
        }
7332
    }
7333
},
7334
 
7335
 
7336
 
7337
 
7338
 
7339
 
7340
 
7341
 
7342
 
7343
 
7344
 
7345
 
7346
 
7347
 
7348
 
7349
 
7350
 
7351
 
7352
 
7353
 
7354
 
7355
 
7356
 
7357
 
7358
 
7359
 
7360
 
7361
 
7362
 
7363
 
7364
 
7365
 
7366
 
7367
 
7368
 
7369
 
7370
 
7371
 
7372
 
7373
 
7374
 
7375
 
7376
 
7377
/////////////////////////////////////////////////////////////////////////////
7378
//
7379
// Private DOM Event Handlers
7380
//
7381
/////////////////////////////////////////////////////////////////////////////
7382
 
7383
/**
7384
 * Validates minWidths whenever the render chain ends.
7385
 *
7386
 * @method _onRenderChainEnd
7387
 * @private
7388
 */
7389
_onRenderChainEnd : function() {
7390
    // Hide loading message
7391
    this.hideTableMessage();
7392
 
7393
    // Show empty message
7394
    if(this._elTbody.rows.length === 0) {
7395
        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
7396
    }
7397
 
7398
    // Execute in timeout thread to give implementers a chance
7399
    // to subscribe after the constructor
7400
    var oSelf = this;
7401
    setTimeout(function() {
7402
        if((oSelf instanceof DT) && oSelf._sId) {
7403
            // Init event
7404
            if(oSelf._bInit) {
7405
                oSelf._bInit = false;
7406
                oSelf.fireEvent("initEvent");
7407
            }
7408
 
7409
            // Render event
7410
            oSelf.fireEvent("renderEvent");
7411
            // Backward compatibility
7412
            oSelf.fireEvent("refreshEvent");
7413
 
7414
            // Post-render routine
7415
            oSelf.validateColumnWidths();
7416
 
7417
            // Post-render event
7418
            oSelf.fireEvent("postRenderEvent");
7419
 
7420
            /*if(YAHOO.example.Performance.trialStart) {
7421
                YAHOO.example.Performance.trialStart = null;
7422
            }*/
7423
 
7424
        }
7425
    }, 0);
7426
},
7427
 
7428
/**
7429
 * Handles click events on the DOCUMENT.
7430
 *
7431
 * @method _onDocumentClick
7432
 * @param e {HTMLEvent} The click event.
7433
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7434
 * @private
7435
 */
7436
_onDocumentClick : function(e, oSelf) {
7437
    var elTarget = Ev.getTarget(e);
7438
    var elTag = elTarget.nodeName.toLowerCase();
7439
 
7440
    if(!Dom.isAncestor(oSelf._elContainer, elTarget)) {
7441
        oSelf.fireEvent("tableBlurEvent");
7442
 
7443
        // Fires editorBlurEvent when click is not within the TABLE.
7444
        // For cases when click is within the TABLE, due to timing issues,
7445
        // the editorBlurEvent needs to get fired by the lower-level DOM click
7446
        // handlers below rather than by the TABLE click handler directly.
7447
        if(oSelf._oCellEditor) {
7448
            if(oSelf._oCellEditor.getContainerEl) {
7449
                var elContainer = oSelf._oCellEditor.getContainerEl();
7450
                // Only if the click was not within the CellEditor container
7451
                if(!Dom.isAncestor(elContainer, elTarget) &&
7452
                        (elContainer.id !== elTarget.id)) {
7453
                    oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
7454
                }
7455
            }
7456
            // Backward Compatibility
7457
            else if(oSelf._oCellEditor.isActive) {
7458
                // Only if the click was not within the Cell Editor container
7459
                if(!Dom.isAncestor(oSelf._oCellEditor.container, elTarget) &&
7460
                        (oSelf._oCellEditor.container.id !== elTarget.id)) {
7461
                    oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
7462
                }
7463
            }
7464
        }
7465
    }
7466
},
7467
 
7468
/**
7469
 * Handles focus events on the DataTable instance.
7470
 *
7471
 * @method _onTableFocus
7472
 * @param e {HTMLEvent} The focus event.
7473
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7474
 * @private
7475
 */
7476
_onTableFocus : function(e, oSelf) {
7477
    oSelf.fireEvent("tableFocusEvent");
7478
},
7479
 
7480
/**
7481
 * Handles focus events on the THEAD element.
7482
 *
7483
 * @method _onTheadFocus
7484
 * @param e {HTMLEvent} The focus event.
7485
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7486
 * @private
7487
 */
7488
_onTheadFocus : function(e, oSelf) {
7489
    oSelf.fireEvent("theadFocusEvent");
7490
    oSelf.fireEvent("tableFocusEvent");
7491
},
7492
 
7493
/**
7494
 * Handles focus events on the TBODY element.
7495
 *
7496
 * @method _onTbodyFocus
7497
 * @param e {HTMLEvent} The focus event.
7498
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7499
 * @private
7500
 */
7501
_onTbodyFocus : function(e, oSelf) {
7502
    oSelf.fireEvent("tbodyFocusEvent");
7503
    oSelf.fireEvent("tableFocusEvent");
7504
},
7505
 
7506
/**
7507
 * Handles mouseover events on the DataTable instance.
7508
 *
7509
 * @method _onTableMouseover
7510
 * @param e {HTMLEvent} The mouseover event.
7511
 * @param origTarget {HTMLElement} The mouseenter delegated element.
7512
 * @param container {HTMLElement} The mouseenter delegation container.
7513
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7514
 * @private
7515
 */
7516
_onTableMouseover : function(e, origTarget, container, oSelf) {
7517
    var elTarget = origTarget;
7518
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7519
    var bKeepBubbling = true;
7520
    while(elTarget && (elTag != "table")) {
7521
        switch(elTag) {
7522
            case "body":
7523
                 return;
7524
            case "a":
7525
                break;
7526
            case "td":
7527
                bKeepBubbling = oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
7528
                break;
7529
            case "span":
7530
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7531
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseoverEvent",{target:elTarget,event:e});
7532
                    // Backward compatibility
7533
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
7534
                }
7535
                break;
7536
            case "th":
7537
                bKeepBubbling = oSelf.fireEvent("theadCellMouseoverEvent",{target:elTarget,event:e});
7538
                // Backward compatibility
7539
                bKeepBubbling = oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
7540
                break;
7541
            case "tr":
7542
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7543
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseoverEvent",{target:elTarget,event:e});
7544
                    // Backward compatibility
7545
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
7546
                }
7547
                else {
7548
                    bKeepBubbling = oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
7549
                }
7550
                break;
7551
            default:
7552
                break;
7553
        }
7554
        if(bKeepBubbling === false) {
7555
            return;
7556
        }
7557
        else {
7558
            elTarget = elTarget.parentNode;
7559
            if(elTarget) {
7560
                elTag = elTarget.nodeName.toLowerCase();
7561
            }
7562
        }
7563
    }
7564
    oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elContainer),event:e});
7565
},
7566
 
7567
/**
7568
 * Handles mouseout events on the DataTable instance.
7569
 *
7570
 * @method _onTableMouseout
7571
 * @param e {HTMLEvent} The mouseout event.
7572
 * @param origTarget {HTMLElement} The mouseleave delegated element.
7573
 * @param container {HTMLElement} The mouseleave delegation container.
7574
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7575
 * @private
7576
 */
7577
_onTableMouseout : function(e, origTarget, container, oSelf) {
7578
    var elTarget = origTarget;
7579
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7580
    var bKeepBubbling = true;
7581
    while(elTarget && (elTag != "table")) {
7582
        switch(elTag) {
7583
            case "body":
7584
                return;
7585
            case "a":
7586
                break;
7587
            case "td":
7588
                bKeepBubbling = oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
7589
                break;
7590
            case "span":
7591
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7592
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseoutEvent",{target:elTarget,event:e});
7593
                    // Backward compatibility
7594
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
7595
                }
7596
                break;
7597
            case "th":
7598
                bKeepBubbling = oSelf.fireEvent("theadCellMouseoutEvent",{target:elTarget,event:e});
7599
                // Backward compatibility
7600
                bKeepBubbling = oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
7601
                break;
7602
            case "tr":
7603
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7604
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseoutEvent",{target:elTarget,event:e});
7605
                    // Backward compatibility
7606
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
7607
                }
7608
                else {
7609
                    bKeepBubbling = oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
7610
                }
7611
                break;
7612
            default:
7613
                break;
7614
        }
7615
        if(bKeepBubbling === false) {
7616
            return;
7617
        }
7618
        else {
7619
            elTarget = elTarget.parentNode;
7620
            if(elTarget) {
7621
                elTag = elTarget.nodeName.toLowerCase();
7622
            }
7623
        }
7624
    }
7625
    oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elContainer),event:e});
7626
},
7627
 
7628
/**
7629
 * Handles mousedown events on the DataTable instance.
7630
 *
7631
 * @method _onTableMousedown
7632
 * @param e {HTMLEvent} The mousedown event.
7633
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7634
 * @private
7635
 */
7636
_onTableMousedown : function(e, oSelf) {
7637
    var elTarget = Ev.getTarget(e);
7638
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7639
    var bKeepBubbling = true;
7640
    while(elTarget && (elTag != "table")) {
7641
        switch(elTag) {
7642
            case "body":
7643
                return;
7644
            case "a":
7645
                break;
7646
            case "td":
7647
                bKeepBubbling = oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
7648
                break;
7649
            case "span":
7650
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7651
                    bKeepBubbling = oSelf.fireEvent("theadLabelMousedownEvent",{target:elTarget,event:e});
7652
                    // Backward compatibility
7653
                    bKeepBubbling = oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
7654
                }
7655
                break;
7656
            case "th":
7657
                bKeepBubbling = oSelf.fireEvent("theadCellMousedownEvent",{target:elTarget,event:e});
7658
                // Backward compatibility
7659
                bKeepBubbling = oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
7660
                break;
7661
            case "tr":
7662
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7663
                    bKeepBubbling = oSelf.fireEvent("theadRowMousedownEvent",{target:elTarget,event:e});
7664
                    // Backward compatibility
7665
                    bKeepBubbling = oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
7666
                }
7667
                else {
7668
                    bKeepBubbling = oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
7669
                }
7670
                break;
7671
            default:
7672
                break;
7673
        }
7674
        if(bKeepBubbling === false) {
7675
            return;
7676
        }
7677
        else {
7678
            elTarget = elTarget.parentNode;
7679
            if(elTarget) {
7680
                elTag = elTarget.nodeName.toLowerCase();
7681
            }
7682
        }
7683
    }
7684
    oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elContainer),event:e});
7685
},
7686
 
7687
/**
7688
 * Handles mouseup events on the DataTable instance.
7689
 *
7690
 * @method _onTableMouseup
7691
 * @param e {HTMLEvent} The mouseup event.
7692
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7693
 * @private
7694
 */
7695
_onTableMouseup : function(e, oSelf) {
7696
    var elTarget = Ev.getTarget(e);
7697
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7698
    var bKeepBubbling = true;
7699
    while(elTarget && (elTag != "table")) {
7700
        switch(elTag) {
7701
            case "body":
7702
                return;
7703
            case "a":
7704
                break;
7705
            case "td":
7706
                bKeepBubbling = oSelf.fireEvent("cellMouseupEvent",{target:elTarget,event:e});
7707
                break;
7708
            case "span":
7709
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7710
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseupEvent",{target:elTarget,event:e});
7711
                    // Backward compatibility
7712
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseupEvent",{target:elTarget,event:e});
7713
                }
7714
                break;
7715
            case "th":
7716
                bKeepBubbling = oSelf.fireEvent("theadCellMouseupEvent",{target:elTarget,event:e});
7717
                // Backward compatibility
7718
                bKeepBubbling = oSelf.fireEvent("headerCellMouseupEvent",{target:elTarget,event:e});
7719
                break;
7720
            case "tr":
7721
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7722
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseupEvent",{target:elTarget,event:e});
7723
                    // Backward compatibility
7724
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseupEvent",{target:elTarget,event:e});
7725
                }
7726
                else {
7727
                    bKeepBubbling = oSelf.fireEvent("rowMouseupEvent",{target:elTarget,event:e});
7728
                }
7729
                break;
7730
            default:
7731
                break;
7732
        }
7733
        if(bKeepBubbling === false) {
7734
            return;
7735
        }
7736
        else {
7737
            elTarget = elTarget.parentNode;
7738
            if(elTarget) {
7739
                elTag = elTarget.nodeName.toLowerCase();
7740
            }
7741
        }
7742
    }
7743
    oSelf.fireEvent("tableMouseupEvent",{target:(elTarget || oSelf._elContainer),event:e});
7744
},
7745
 
7746
/**
7747
 * Handles dblclick events on the DataTable instance.
7748
 *
7749
 * @method _onTableDblclick
7750
 * @param e {HTMLEvent} The dblclick event.
7751
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7752
 * @private
7753
 */
7754
_onTableDblclick : function(e, oSelf) {
7755
    var elTarget = Ev.getTarget(e);
7756
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7757
    var bKeepBubbling = true;
7758
    while(elTarget && (elTag != "table")) {
7759
        switch(elTag) {
7760
            case "body":
7761
                return;
7762
            case "td":
7763
                bKeepBubbling = oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
7764
                break;
7765
            case "span":
7766
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7767
                    bKeepBubbling = oSelf.fireEvent("theadLabelDblclickEvent",{target:elTarget,event:e});
7768
                    // Backward compatibility
7769
                    bKeepBubbling = oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
7770
                }
7771
                break;
7772
            case "th":
7773
                bKeepBubbling = oSelf.fireEvent("theadCellDblclickEvent",{target:elTarget,event:e});
7774
                // Backward compatibility
7775
                bKeepBubbling = oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
7776
                break;
7777
            case "tr":
7778
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
7779
                    bKeepBubbling = oSelf.fireEvent("theadRowDblclickEvent",{target:elTarget,event:e});
7780
                    // Backward compatibility
7781
                    bKeepBubbling = oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
7782
                }
7783
                else {
7784
                    bKeepBubbling = oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
7785
                }
7786
                break;
7787
            default:
7788
                break;
7789
        }
7790
        if(bKeepBubbling === false) {
7791
            return;
7792
        }
7793
        else {
7794
            elTarget = elTarget.parentNode;
7795
            if(elTarget) {
7796
                elTag = elTarget.nodeName.toLowerCase();
7797
            }
7798
        }
7799
    }
7800
    oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elContainer),event:e});
7801
},
7802
/**
7803
 * Handles keydown events on the THEAD element.
7804
 *
7805
 * @method _onTheadKeydown
7806
 * @param e {HTMLEvent} The key event.
7807
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7808
 * @private
7809
 */
7810
_onTheadKeydown : function(e, oSelf) {
7811
    var elTarget = Ev.getTarget(e);
7812
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7813
    var bKeepBubbling = true;
7814
    while(elTarget && (elTag != "table")) {
7815
        switch(elTag) {
7816
            case "body":
7817
                return;
7818
            case "input":
7819
            case "textarea":
7820
                // TODO: implement textareaKeyEvent
7821
                break;
7822
            case "thead":
7823
                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
7824
                break;
7825
            default:
7826
                break;
7827
        }
7828
        if(bKeepBubbling === false) {
7829
            return;
7830
        }
7831
        else {
7832
            elTarget = elTarget.parentNode;
7833
            if(elTarget) {
7834
                elTag = elTarget.nodeName.toLowerCase();
7835
            }
7836
        }
7837
    }
7838
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
7839
},
7840
 
7841
/**
7842
 * Handles keydown events on the TBODY element. Handles selection behavior,
7843
 * provides hooks for ENTER to edit functionality.
7844
 *
7845
 * @method _onTbodyKeydown
7846
 * @param e {HTMLEvent} The key event.
7847
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7848
 * @private
7849
 */
7850
_onTbodyKeydown : function(e, oSelf) {
7851
    var sMode = oSelf.get("selectionMode");
7852
 
7853
    if(sMode == "standard") {
7854
        oSelf._handleStandardSelectionByKey(e);
7855
    }
7856
    else if(sMode == "single") {
7857
        oSelf._handleSingleSelectionByKey(e);
7858
    }
7859
    else if(sMode == "cellblock") {
7860
        oSelf._handleCellBlockSelectionByKey(e);
7861
    }
7862
    else if(sMode == "cellrange") {
7863
        oSelf._handleCellRangeSelectionByKey(e);
7864
    }
7865
    else if(sMode == "singlecell") {
7866
        oSelf._handleSingleCellSelectionByKey(e);
7867
    }
7868
 
7869
    if(oSelf._oCellEditor) {
7870
        if(oSelf._oCellEditor.fireEvent) {
7871
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
7872
        }
7873
        else if(oSelf._oCellEditor.isActive) {
7874
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
7875
        }
7876
    }
7877
 
7878
    var elTarget = Ev.getTarget(e);
7879
    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
7880
    var bKeepBubbling = true;
7881
    while(elTarget && (elTag != "table")) {
7882
        switch(elTag) {
7883
            case "body":
7884
                return;
7885
            case "tbody":
7886
                bKeepBubbling = oSelf.fireEvent("tbodyKeyEvent",{target:elTarget,event:e});
7887
                break;
7888
            default:
7889
                break;
7890
        }
7891
        if(bKeepBubbling === false) {
7892
            return;
7893
        }
7894
        else {
7895
            elTarget = elTarget.parentNode;
7896
            if(elTarget) {
7897
                elTag = elTarget.nodeName.toLowerCase();
7898
            }
7899
        }
7900
    }
7901
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
7902
},
7903
 
7904
/**
7905
 * Handles click events on the THEAD element.
7906
 *
7907
 * @method _onTheadClick
7908
 * @param e {HTMLEvent} The click event.
7909
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
7910
 * @private
7911
 */
7912
_onTheadClick : function(e, oSelf) {
7913
    // This blurs the CellEditor
7914
    if(oSelf._oCellEditor) {
7915
        if(oSelf._oCellEditor.fireEvent) {
7916
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
7917
        }
7918
        // Backward compatibility
7919
        else if(oSelf._oCellEditor.isActive) {
7920
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
7921
        }
7922
    }
7923
 
7924
    var elTarget = Ev.getTarget(e),
7925
        elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(),
7926
        bKeepBubbling = true;
7927
    while(elTarget && (elTag != "table")) {
7928
        switch(elTag) {
7929
            case "body":
7930
                return;
7931
            case "input":
7932
                var sType = elTarget.type.toLowerCase();
7933
                if(sType == "checkbox") {
7934
                    bKeepBubbling = oSelf.fireEvent("theadCheckboxClickEvent",{target:elTarget,event:e});
7935
                }
7936
                else if(sType == "radio") {
7937
                    bKeepBubbling = oSelf.fireEvent("theadRadioClickEvent",{target:elTarget,event:e});
7938
                }
7939
                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
7940
                    if(!elTarget.disabled) {
7941
                        bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
7942
                    }
7943
                    else {
7944
                        bKeepBubbling = false;
7945
                    }
7946
                }
7947
                else if (elTarget.disabled){
7948
                    bKeepBubbling = false;
7949
                }
7950
                break;
7951
            case "a":
7952
                bKeepBubbling = oSelf.fireEvent("theadLinkClickEvent",{target:elTarget,event:e});
7953
                break;
7954
            case "button":
7955
                if(!elTarget.disabled) {
7956
                    bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
7957
                }
7958
                else {
7959
                    bKeepBubbling = false;
7960
                }
7961
                break;
7962
            case "span":
7963
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
7964
                    bKeepBubbling = oSelf.fireEvent("theadLabelClickEvent",{target:elTarget,event:e});
7965
                    // Backward compatibility
7966
                    bKeepBubbling = oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
7967
                }
7968
                break;
7969
            case "th":
7970
                bKeepBubbling = oSelf.fireEvent("theadCellClickEvent",{target:elTarget,event:e});
7971
                // Backward compatibility
7972
                bKeepBubbling = oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
7973
                break;
7974
            case "tr":
7975
                bKeepBubbling = oSelf.fireEvent("theadRowClickEvent",{target:elTarget,event:e});
7976
                // Backward compatibility
7977
                bKeepBubbling = oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
7978
                break;
7979
            default:
7980
                break;
7981
        }
7982
        if(bKeepBubbling === false) {
7983
            return;
7984
        }
7985
        else {
7986
            elTarget = elTarget.parentNode;
7987
            if(elTarget) {
7988
                elTag = elTarget.nodeName.toLowerCase();
7989
            }
7990
        }
7991
    }
7992
    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
7993
},
7994
 
7995
/**
7996
 * Handles click events on the primary TBODY element.
7997
 *
7998
 * @method _onTbodyClick
7999
 * @param e {HTMLEvent} The click event.
8000
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
8001
 * @private
8002
 */
8003
_onTbodyClick : function(e, oSelf) {
8004
    // This blurs the CellEditor
8005
    if(oSelf._oCellEditor) {
8006
        if(oSelf._oCellEditor.fireEvent) {
8007
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
8008
        }
8009
        else if(oSelf._oCellEditor.isActive) {
8010
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
8011
        }
8012
    }
8013
 
8014
    // Fire Custom Events
8015
    var elTarget = Ev.getTarget(e),
8016
        elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(),
8017
        bKeepBubbling = true;
8018
    while(elTarget && (elTag != "table")) {
8019
        switch(elTag) {
8020
            case "body":
8021
                return;
8022
            case "input":
8023
                var sType = elTarget.type.toLowerCase();
8024
                if(sType == "checkbox") {
8025
                    bKeepBubbling = oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e});
8026
                }
8027
                else if(sType == "radio") {
8028
                    bKeepBubbling = oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e});
8029
                }
8030
                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
8031
                    if(!elTarget.disabled) {
8032
                        bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
8033
                    }
8034
                    else {
8035
                        bKeepBubbling = false;
8036
                    }
8037
                }
8038
                else if (elTarget.disabled){
8039
                    bKeepBubbling = false;
8040
                }
8041
                break;
8042
            case "a":
8043
                bKeepBubbling = oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
8044
                break;
8045
            case "button":
8046
                if(!elTarget.disabled) {
8047
                    bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
8048
                }
8049
                else {
8050
                    bKeepBubbling = false;
8051
                }
8052
                break;
8053
            case "td":
8054
                bKeepBubbling = oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
8055
                break;
8056
            case "tr":
8057
                bKeepBubbling = oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
8058
                break;
8059
            default:
8060
                break;
8061
        }
8062
        if(bKeepBubbling === false) {
8063
            return;
8064
        }
8065
        else {
8066
            elTarget = elTarget.parentNode;
8067
            if(elTarget) {
8068
                elTag = elTarget.nodeName.toLowerCase();
8069
            }
8070
        }
8071
    }
8072
    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
8073
},
8074
 
8075
/**
8076
 * Handles change events on SELECT elements within DataTable.
8077
 *
8078
 * @method _onDropdownChange
8079
 * @param e {HTMLEvent} The change event.
8080
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
8081
 * @private
8082
 */
8083
_onDropdownChange : function(e, oSelf) {
8084
    var elTarget = Ev.getTarget(e);
8085
    oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget});
8086
},
8087
 
8088
 
8089
 
8090
 
8091
 
8092
 
8093
 
8094
 
8095
 
8096
 
8097
 
8098
 
8099
 
8100
 
8101
 
8102
 
8103
 
8104
 
8105
 
8106
 
8107
 
8108
 
8109
 
8110
 
8111
 
8112
 
8113
 
8114
 
8115
 
8116
 
8117
 
8118
 
8119
/////////////////////////////////////////////////////////////////////////////
8120
//
8121
// Public member variables
8122
//
8123
/////////////////////////////////////////////////////////////////////////////
8124
/**
8125
 * Returns object literal of initial configs.
8126
 *
8127
 * @property configs
8128
 * @type Object
8129
 * @default {}
8130
 */
8131
configs: null,
8132
 
8133
 
8134
/////////////////////////////////////////////////////////////////////////////
8135
//
8136
// Public methods
8137
//
8138
/////////////////////////////////////////////////////////////////////////////
8139
 
8140
/**
8141
 * Returns unique id assigned to instance, which is a useful prefix for
8142
 * generating unique DOM ID strings.
8143
 *
8144
 * @method getId
8145
 * @return {String} Unique ID of the DataSource instance.
8146
 */
8147
getId : function() {
8148
    return this._sId;
8149
},
8150
 
8151
/**
8152
 * DataSource instance name, for logging.
8153
 *
8154
 * @method toString
8155
 * @return {String} Unique name of the DataSource instance.
8156
 */
8157
 
8158
toString : function() {
8159
    return "DataTable instance " + this._sId;
8160
},
8161
 
8162
/**
8163
 * Returns the DataTable instance's DataSource instance.
8164
 *
8165
 * @method getDataSource
8166
 * @return {YAHOO.util.DataSource} DataSource instance.
8167
 */
8168
getDataSource : function() {
8169
    return this._oDataSource;
8170
},
8171
 
8172
/**
8173
 * Returns the DataTable instance's ColumnSet instance.
8174
 *
8175
 * @method getColumnSet
8176
 * @return {YAHOO.widget.ColumnSet} ColumnSet instance.
8177
 */
8178
getColumnSet : function() {
8179
    return this._oColumnSet;
8180
},
8181
 
8182
/**
8183
 * Returns the DataTable instance's RecordSet instance.
8184
 *
8185
 * @method getRecordSet
8186
 * @return {YAHOO.widget.RecordSet} RecordSet instance.
8187
 */
8188
getRecordSet : function() {
8189
    return this._oRecordSet;
8190
},
8191
 
8192
/**
8193
 * Returns on object literal representing the DataTable instance's current
8194
 * state with the following properties:
8195
 * <dl>
8196
 * <dt>pagination</dt>
8197
 * <dd>Instance of YAHOO.widget.Paginator</dd>
8198
 *
8199
 * <dt>sortedBy</dt>
8200
 * <dd>
8201
 *     <dl>
8202
 *         <dt>sortedBy.key</dt>
8203
 *         <dd>{String} Key of sorted Column</dd>
8204
 *         <dt>sortedBy.dir</dt>
8205
 *         <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
8206
 *     </dl>
8207
 * </dd>
8208
 *
8209
 * <dt>selectedRows</dt>
8210
 * <dd>Array of selected rows by Record ID.</dd>
8211
 *
8212
 * <dt>selectedCells</dt>
8213
 * <dd>Selected cells as an array of object literals:
8214
 *     {recordId:sRecordId, columnKey:sColumnKey}</dd>
8215
 * </dl>
8216
 *
8217
 * @method getState
8218
 * @return {Object} DataTable instance state object literal values.
8219
 */
8220
getState : function() {
8221
    return {
8222
        totalRecords: this.get('paginator') ? this.get('paginator').get("totalRecords") : this._oRecordSet.getLength(),
8223
        pagination: this.get("paginator") ? this.get("paginator").getState() : null,
8224
        sortedBy: this.get("sortedBy"),
8225
        selectedRows: this.getSelectedRows(),
8226
        selectedCells: this.getSelectedCells()
8227
    };
8228
},
8229
 
8230
 
8231
 
8232
 
8233
 
8234
 
8235
 
8236
 
8237
 
8238
 
8239
 
8240
 
8241
 
8242
 
8243
 
8244
 
8245
 
8246
 
8247
 
8248
 
8249
 
8250
 
8251
 
8252
 
8253
 
8254
 
8255
 
8256
 
8257
 
8258
 
8259
 
8260
 
8261
 
8262
 
8263
 
8264
 
8265
 
8266
 
8267
 
8268
 
8269
 
8270
 
8271
 
8272
// DOM ACCESSORS
8273
 
8274
/**
8275
 * Returns DOM reference to the DataTable's container element.
8276
 *
8277
 * @method getContainerEl
8278
 * @return {HTMLElement} Reference to DIV element.
8279
 */
8280
getContainerEl : function() {
8281
    return this._elContainer;
8282
},
8283
 
8284
/**
8285
 * Returns DOM reference to the DataTable's TABLE element.
8286
 *
8287
 * @method getTableEl
8288
 * @return {HTMLElement} Reference to TABLE element.
8289
 */
8290
getTableEl : function() {
8291
    return this._elTable;
8292
},
8293
 
8294
/**
8295
 * Returns DOM reference to the DataTable's THEAD element.
8296
 *
8297
 * @method getTheadEl
8298
 * @return {HTMLElement} Reference to THEAD element.
8299
 */
8300
getTheadEl : function() {
8301
    return this._elThead;
8302
},
8303
 
8304
/**
8305
 * Returns DOM reference to the DataTable's primary TBODY element.
8306
 *
8307
 * @method getTbodyEl
8308
 * @return {HTMLElement} Reference to TBODY element.
8309
 */
8310
getTbodyEl : function() {
8311
    return this._elTbody;
8312
},
8313
 
8314
/**
8315
 * Returns DOM reference to the DataTable's secondary TBODY element that is
8316
 * used to display messages.
8317
 *
8318
 * @method getMsgTbodyEl
8319
 * @return {HTMLElement} Reference to TBODY element.
8320
 */
8321
getMsgTbodyEl : function() {
8322
    return this._elMsgTbody;
8323
},
8324
 
8325
/**
8326
 * Returns DOM reference to the TD element within the secondary TBODY that is
8327
 * used to display messages.
8328
 *
8329
 * @method getMsgTdEl
8330
 * @return {HTMLElement} Reference to TD element.
8331
 */
8332
getMsgTdEl : function() {
8333
    return this._elMsgTd;
8334
},
8335
 
8336
/**
8337
 * Returns the corresponding TR reference for a given DOM element, ID string or
8338
 * page row index. If the given identifier is a child of a TR element,
8339
 * then DOM tree is traversed until a parent TR element is returned, otherwise
8340
 * null. Returns null if the row is not considered a primary row (i.e., row
8341
 * extensions).
8342
 *
8343
 * @method getTrEl
8344
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to
8345
 * get: by element reference, ID string, page row index, or Record.
8346
 * @return {HTMLElement} Reference to TR element, or null.
8347
 */
8348
getTrEl : function(row) {
8349
    // By Record
8350
    if(row instanceof YAHOO.widget.Record) {
8351
        return document.getElementById(row.getId());
8352
    }
8353
    // By page row index
8354
    else if(lang.isNumber(row)) {
8355
        var dataRows = Dom.getElementsByClassName(DT.CLASS_REC, "tr", this._elTbody);
8356
        return dataRows && dataRows[row] ? dataRows[row] : null;
8357
    }
8358
    // By ID string or element reference
8359
    else if(row) {
8360
        var elRow = (lang.isString(row)) ? document.getElementById(row) : row;
8361
 
8362
        // Validate HTML element
8363
        if(elRow && elRow.ownerDocument == document) {
8364
            // Validate TR element
8365
            if(elRow.nodeName.toLowerCase() != "tr") {
8366
                // Traverse up the DOM to find the corresponding TR element
8367
                elRow = Dom.getAncestorByTagName(elRow,"tr");
8368
            }
8369
 
8370
            return elRow;
8371
        }
8372
    }
8373
 
8374
    return null;
8375
},
8376
 
8377
/**
8378
 * Returns DOM reference to the first primary TR element in the DataTable page, or null.
8379
 *
8380
 * @method getFirstTrEl
8381
 * @return {HTMLElement} Reference to TR element.
8382
 */
8383
getFirstTrEl : function() {
8384
    var allRows = this._elTbody.rows,
8385
        i=0;
8386
    while(allRows[i]) {
8387
        if(this.getRecord(allRows[i])) {
8388
            return allRows[i];
8389
        }
8390
        i++;
8391
    }
8392
    return null;
8393
 
8394
},
8395
 
8396
/**
8397
 * Returns DOM reference to the last primary TR element in the DataTable page, or null.
8398
 *
8399
 * @method getLastTrEl
8400
 * @return {HTMLElement} Reference to last TR element.
8401
 */
8402
getLastTrEl : function() {
8403
    var allRows = this._elTbody.rows,
8404
        i=allRows.length-1;
8405
    while(i>-1) {
8406
        if(this.getRecord(allRows[i])) {
8407
            return allRows[i];
8408
        }
8409
        i--;
8410
    }
8411
    return null;
8412
},
8413
 
8414
/**
8415
 * Returns DOM reference to the next TR element from the given primary TR element, or null.
8416
 *
8417
 * @method getNextTrEl
8418
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
8419
 * reference, ID string, page row index, or Record from which to get next TR element.
8420
 * @param forcePrimary {Boolean} (optional) If true, will only return TR elements
8421
 * that correspond to Records. Non-primary rows (such as row expansions)
8422
 * will be skipped.
8423
 * @return {HTMLElement} Reference to next TR element.
8424
 */
8425
getNextTrEl : function(row, forcePrimary) {
8426
    var nThisTrIndex = this.getTrIndex(row);
8427
    if(nThisTrIndex !== null) {
8428
        var allRows = this._elTbody.rows;
8429
        if(forcePrimary) {
8430
            while(nThisTrIndex < allRows.length-1) {
8431
                row = allRows[nThisTrIndex+1];
8432
                if(this.getRecord(row)) {
8433
                    return row;
8434
                }
8435
                nThisTrIndex++;
8436
            }
8437
        }
8438
        else {
8439
            if(nThisTrIndex < allRows.length-1) {
8440
                return allRows[nThisTrIndex+1];
8441
            }
8442
        }
8443
    }
8444
 
8445
    return null;
8446
},
8447
 
8448
/**
8449
 * Returns DOM reference to the previous TR element from the given primary TR element, or null.
8450
 *
8451
 * @method getPreviousTrEl
8452
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
8453
 * reference, ID string, page row index, or Record from which to get previous TR element.
8454
 * @param forcePrimary {Boolean} (optional) If true, will only return TR elements
8455
 * from rothat correspond to Records. Non-primary rows (such as row expansions)
8456
 * will be skipped.
8457
 * @return {HTMLElement} Reference to previous TR element.
8458
 */
8459
getPreviousTrEl : function(row, forcePrimary) {
8460
    var nThisTrIndex = this.getTrIndex(row);
8461
    if(nThisTrIndex !== null) {
8462
        var allRows = this._elTbody.rows;
8463
 
8464
        if(forcePrimary) {
8465
            while(nThisTrIndex > 0) {
8466
                row = allRows[nThisTrIndex-1];
8467
                if(this.getRecord(row)) {
8468
                    return row;
8469
                }
8470
                nThisTrIndex--;
8471
            }
8472
        }
8473
        else {
8474
            if(nThisTrIndex > 0) {
8475
                return allRows[nThisTrIndex-1];
8476
            }
8477
        }
8478
    }
8479
 
8480
    return null;
8481
},
8482
 
8483
 
8484
/**
8485
 * Workaround for IE bug where hidden or not-in-dom elements cause cellIndex
8486
 * value to be incorrect.
8487
 *
8488
 * @method getCellIndex
8489
 * @param cell {HTMLElement | Object} TD element or child of a TD element, or
8490
 * object literal of syntax {record:oRecord, column:oColumn}.
8491
 * @return {Number} TD.cellIndex value.
8492
 */
8493
getCellIndex : function(cell) {
8494
    cell = this.getTdEl(cell);
8495
    if(cell) {
8496
        if(ua.ie > 0) {
8497
            var i=0,
8498
                tr = cell.parentNode,
8499
                allCells = tr.childNodes,
8500
                len = allCells.length;
8501
            for(; i<len; i++) {
8502
                if(allCells[i] == cell) {
8503
                    return i;
8504
                }
8505
            }
8506
        }
8507
        else {
8508
            return cell.cellIndex;
8509
        }
8510
    }
8511
},
8512
 
8513
/**
8514
 * Returns DOM reference to a TD liner element.
8515
 *
8516
 * @method getTdLinerEl
8517
 * @param cell {HTMLElement | Object} TD element or child of a TD element, or
8518
 * object literal of syntax {record:oRecord, column:oColumn}.
8519
 * @return {HTMLElement} Reference to TD liner element.
8520
 */
8521
getTdLinerEl : function(cell) {
8522
    var elCell = this.getTdEl(cell);
8523
    return elCell.firstChild || null;
8524
},
8525
 
8526
/**
8527
 * Returns DOM reference to a TD element. Returns null if the row is not
8528
 * considered a primary row (i.e., row extensions).
8529
 *
8530
 * @method getTdEl
8531
 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
8532
 * object literal of syntax {record:oRecord, column:oColumn}.
8533
 * @return {HTMLElement} Reference to TD element.
8534
 */
8535
getTdEl : function(cell) {
8536
    var elCell;
8537
    var el = Dom.get(cell);
8538
 
8539
    // Validate HTML element
8540
    if(el && (el.ownerDocument == document)) {
8541
        // Validate TD element
8542
        if(el.nodeName.toLowerCase() != "td") {
8543
            // Traverse up the DOM to find the corresponding TR element
8544
            elCell = Dom.getAncestorByTagName(el, "td");
8545
        }
8546
        else {
8547
            elCell = el;
8548
        }
8549
 
8550
        // Make sure the TD is in this TBODY or is not in DOM
8551
        // Bug 2527707 and bug 2263558
8552
        if(elCell && ((elCell.parentNode.parentNode == this._elTbody) ||
8553
            (elCell.parentNode.parentNode === null) ||
8554
            (elCell.parentNode.parentNode.nodeType === 11))) {
8555
            // Now we can return the TD element
8556
            return elCell;
8557
        }
8558
    }
8559
    else if(cell) {
8560
        var oRecord, nColKeyIndex;
8561
 
8562
        if(lang.isString(cell.columnKey) && lang.isString(cell.recordId)) {
8563
            oRecord = this.getRecord(cell.recordId);
8564
            var oColumn = this.getColumn(cell.columnKey);
8565
            if(oColumn) {
8566
                nColKeyIndex = oColumn.getKeyIndex();
8567
            }
8568
 
8569
        }
8570
        if(cell.record && cell.column && cell.column.getKeyIndex) {
8571
            oRecord = cell.record;
8572
            nColKeyIndex = cell.column.getKeyIndex();
8573
        }
8574
        var elRow = this.getTrEl(oRecord);
8575
        if((nColKeyIndex !== null) && elRow && elRow.cells && elRow.cells.length > 0) {
8576
            return elRow.cells[nColKeyIndex] || null;
8577
        }
8578
    }
8579
 
8580
    return null;
8581
},
8582
 
8583
/**
8584
 * Returns DOM reference to the first primary TD element in the DataTable page (by default),
8585
 * the first TD element of the optionally given row, or null.
8586
 *
8587
 * @method getFirstTdEl
8588
 * @param row {HTMLElement} (optional) row from which to get first TD
8589
 * @return {HTMLElement} Reference to TD element.
8590
 */
8591
getFirstTdEl : function(row) {
8592
    var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getFirstTrEl();
8593
    if(elRow) {
8594
        if(elRow.cells && elRow.cells.length > 0) {
8595
            return elRow.cells[0];
8596
        }
8597
        else if(elRow.childNodes && elRow.childNodes.length > 0) {
8598
            return elRow.childNodes[0];
8599
        }
8600
    }
8601
    return null;
8602
},
8603
 
8604
/**
8605
 * Returns DOM reference to the last primary TD element in the DataTable page (by default),
8606
 * the first TD element of the optionally given row, or null.
8607
 *
8608
 * @method getLastTdEl
8609
 * @param row {HTMLElement} (optional) row from which to get first TD
8610
 * @return {HTMLElement} Reference to last TD element.
8611
 */
8612
getLastTdEl : function(row) {
8613
    var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getLastTrEl();
8614
    if(elRow) {
8615
        if(elRow.cells && elRow.cells.length > 0) {
8616
            return elRow.cells[elRow.cells.length-1];
8617
        }
8618
        else if(elRow.childNodes && elRow.childNodes.length > 0) {
8619
            return elRow.childNodes[elRow.childNodes.length-1];
8620
        }
8621
    }
8622
    return null;
8623
},
8624
 
8625
/**
8626
 * Returns DOM reference to the next TD element from the given cell, or null.
8627
 *
8628
 * @method getNextTdEl
8629
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8630
 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
8631
 * @return {HTMLElement} Reference to next TD element, or null.
8632
 */
8633
getNextTdEl : function(cell) {
8634
    var elCell = this.getTdEl(cell);
8635
    if(elCell) {
8636
        var nThisTdIndex = this.getCellIndex(elCell);
8637
        var elRow = this.getTrEl(elCell);
8638
        if(elRow.cells && (elRow.cells.length) > 0 && (nThisTdIndex < elRow.cells.length-1)) {
8639
            return elRow.cells[nThisTdIndex+1];
8640
        }
8641
        else if(elRow.childNodes && (elRow.childNodes.length) > 0 && (nThisTdIndex < elRow.childNodes.length-1)) {
8642
            return elRow.childNodes[nThisTdIndex+1];
8643
        }
8644
        else {
8645
            var elNextRow = this.getNextTrEl(elRow);
8646
            if(elNextRow) {
8647
                return elNextRow.cells[0];
8648
            }
8649
        }
8650
    }
8651
    return null;
8652
},
8653
 
8654
/**
8655
 * Returns DOM reference to the previous TD element from the given cell, or null.
8656
 *
8657
 * @method getPreviousTdEl
8658
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8659
 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
8660
 * @return {HTMLElement} Reference to previous TD element, or null.
8661
 */
8662
getPreviousTdEl : function(cell) {
8663
    var elCell = this.getTdEl(cell);
8664
    if(elCell) {
8665
        var nThisTdIndex = this.getCellIndex(elCell);
8666
        var elRow = this.getTrEl(elCell);
8667
        if(nThisTdIndex > 0) {
8668
            if(elRow.cells && elRow.cells.length > 0) {
8669
                return elRow.cells[nThisTdIndex-1];
8670
            }
8671
            else if(elRow.childNodes && elRow.childNodes.length > 0) {
8672
                return elRow.childNodes[nThisTdIndex-1];
8673
            }
8674
        }
8675
        else {
8676
            var elPreviousRow = this.getPreviousTrEl(elRow);
8677
            if(elPreviousRow) {
8678
                return this.getLastTdEl(elPreviousRow);
8679
            }
8680
        }
8681
    }
8682
    return null;
8683
},
8684
 
8685
/**
8686
 * Returns DOM reference to the above TD element from the given cell, or null.
8687
 *
8688
 * @method getAboveTdEl
8689
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8690
 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
8691
 * @param forcePrimary {Boolean} (optional) If true, will only return TD elements
8692
 * from rows that correspond to Records. Non-primary rows (such as row expansions)
8693
 * will be skipped.
8694
 * @return {HTMLElement} Reference to above TD element, or null.
8695
 */
8696
getAboveTdEl : function(cell, forcePrimary) {
8697
    var elCell = this.getTdEl(cell);
8698
    if(elCell) {
8699
        var elPreviousRow = this.getPreviousTrEl(elCell, forcePrimary);
8700
        if(elPreviousRow ) {
8701
            var cellIndex = this.getCellIndex(elCell);
8702
            if(elPreviousRow.cells && elPreviousRow.cells.length > 0) {
8703
                return elPreviousRow.cells[cellIndex] ? elPreviousRow.cells[cellIndex] : null;
8704
            }
8705
            else if(elPreviousRow.childNodes && elPreviousRow.childNodes.length > 0) {
8706
                return elPreviousRow.childNodes[cellIndex] ? elPreviousRow.childNodes[cellIndex] : null;
8707
            }
8708
        }
8709
    }
8710
    return null;
8711
},
8712
 
8713
/**
8714
 * Returns DOM reference to the below TD element from the given cell, or null.
8715
 *
8716
 * @method getBelowTdEl
8717
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
8718
 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
8719
 * @param forcePrimary {Boolean} (optional) If true, will only return TD elements
8720
 * from rows that correspond to Records. Non-primary rows (such as row expansions)
8721
 * will be skipped.
8722
 * @return {HTMLElement} Reference to below TD element, or null.
8723
 */
8724
getBelowTdEl : function(cell, forcePrimary) {
8725
    var elCell = this.getTdEl(cell);
8726
    if(elCell) {
8727
        var elNextRow = this.getNextTrEl(elCell, forcePrimary);
8728
        if(elNextRow) {
8729
            var cellIndex = this.getCellIndex(elCell);
8730
            if(elNextRow.cells && elNextRow.cells.length > 0) {
8731
                return elNextRow.cells[cellIndex] ? elNextRow.cells[cellIndex] : null;
8732
            }
8733
            else if(elNextRow.childNodes && elNextRow.childNodes.length > 0) {
8734
                return elNextRow.childNodes[cellIndex] ? elNextRow.childNodes[cellIndex] : null;
8735
            }
8736
        }
8737
    }
8738
    return null;
8739
},
8740
 
8741
/**
8742
 * Returns DOM reference to a TH liner element. Needed to normalize for resizeable
8743
 * Columns, which have an additional resizer liner DIV element between the TH
8744
 * element and the liner DIV element.
8745
 *
8746
 * @method getThLinerEl
8747
 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
8748
 * DOM element reference, or string ID.
8749
 * @return {HTMLElement} Reference to TH liner element.
8750
 */
8751
getThLinerEl : function(theadCell) {
8752
    var oColumn = this.getColumn(theadCell);
8753
    return (oColumn) ? oColumn.getThLinerEl() : null;
8754
},
8755
 
8756
/**
8757
 * Returns DOM reference to a TH element.
8758
 *
8759
 * @method getThEl
8760
 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
8761
 * DOM element reference, or string ID.
8762
 * @return {HTMLElement} Reference to TH element.
8763
 */
8764
getThEl : function(theadCell) {
8765
    var elTh;
8766
 
8767
    // Validate Column instance
8768
    if(theadCell instanceof YAHOO.widget.Column) {
8769
        var oColumn = theadCell;
8770
        elTh = oColumn.getThEl();
8771
        if(elTh) {
8772
            return elTh;
8773
        }
8774
    }
8775
    // Validate HTML element
8776
    else {
8777
        var el = Dom.get(theadCell);
8778
 
8779
        if(el && (el.ownerDocument == document)) {
8780
            // Validate TH element
8781
            if(el.nodeName.toLowerCase() != "th") {
8782
                // Traverse up the DOM to find the corresponding TR element
8783
                elTh = Dom.getAncestorByTagName(el,"th");
8784
            }
8785
            else {
8786
                elTh = el;
8787
            }
8788
 
8789
            return elTh;
8790
        }
8791
    }
8792
 
8793
    return null;
8794
},
8795
 
8796
/**
8797
 * Returns the page row index of given primary row. Returns null if the row is not on the
8798
 * current DataTable page, or if row is not considered a primary row (i.e., row
8799
 * extensions).
8800
 *
8801
 * @method getTrIndex
8802
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID
8803
 * string reference to an element within the DataTable page, a Record instance,
8804
 * or a Record's RecordSet index.
8805
 * @return {Number} Page row index, or null if data row does not exist or is not on current page.
8806
 */
8807
getTrIndex : function(row) {
8808
    var record = this.getRecord(row),
8809
        index = this.getRecordIndex(record),
8810
        tr;
8811
    if(record) {
8812
        tr = this.getTrEl(record);
8813
        if(tr) {
8814
            return tr.sectionRowIndex;
8815
        }
8816
        else {
8817
            var oPaginator = this.get("paginator");
8818
            if(oPaginator) {
8819
                return oPaginator.get('recordOffset') + index;
8820
            }
8821
            else {
8822
                return index;
8823
            }
8824
        }
8825
    }
8826
    return null;
8827
},
8828
 
8829
 
8830
 
8831
 
8832
 
8833
 
8834
 
8835
 
8836
 
8837
 
8838
 
8839
 
8840
 
8841
 
8842
 
8843
 
8844
 
8845
 
8846
 
8847
 
8848
 
8849
 
8850
 
8851
 
8852
 
8853
 
8854
 
8855
 
8856
 
8857
 
8858
 
8859
 
8860
 
8861
 
8862
 
8863
 
8864
 
8865
 
8866
 
8867
 
8868
 
8869
 
8870
 
8871
 
8872
 
8873
 
8874
// TABLE FUNCTIONS
8875
 
8876
/**
8877
 * Loads new data. Convenience method that calls DataSource's sendRequest()
8878
 * method under the hood.
8879
 *
8880
 * @method load
8881
 * @param oConfig {object} Optional configuration parameters:
8882
 *
8883
 * <dl>
8884
 * <dt>request</dt><dd>Pass in a new request, or initialRequest is used.</dd>
8885
 * <dt>callback</dt><dd>Pass in DataSource sendRequest() callback object, or the following is used:
8886
 *    <dl>
8887
 *      <dt>success</dt><dd>datatable.onDataReturnInitializeTable</dd>
8888
 *      <dt>failure</dt><dd>datatable.onDataReturnInitializeTable</dd>
8889
 *      <dt>scope</dt><dd>datatable</dd>
8890
 *      <dt>argument</dt><dd>datatable.getState()</dd>
8891
 *    </dl>
8892
 * </dd>
8893
 * <dt>datasource</dt><dd>Pass in a new DataSource instance to override the current DataSource for this transaction.</dd>
8894
 * </dl>
8895
 */
8896
load : function(oConfig) {
8897
    oConfig = oConfig || {};
8898
 
8899
    (oConfig.datasource || this._oDataSource).sendRequest(oConfig.request || this.get("initialRequest"), oConfig.callback || {
8900
        success: this.onDataReturnInitializeTable,
8901
        failure: this.onDataReturnInitializeTable,
8902
        scope: this,
8903
        argument: this.getState()
8904
    });
8905
},
8906
 
8907
/**
8908
 * Resets a RecordSet with the given data and populates the page view
8909
 * with the new data. Any previous data, and selection and sort states are
8910
 * cleared. New data should be added as a separate step.
8911
 *
8912
 * @method initializeTable
8913
 */
8914
initializeTable : function() {
8915
    // Reset init flag
8916
    this._bInit = true;
8917
 
8918
    // Clear the RecordSet
8919
    this._oRecordSet.reset();
8920
 
8921
    // Clear the Paginator's totalRecords if paginating
8922
    var pag = this.get('paginator');
8923
    if (pag) {
8924
        pag.set('totalRecords',0);
8925
    }
8926
 
8927
    // Clear selections
8928
    this._unselectAllTrEls();
8929
    this._unselectAllTdEls();
8930
    this._aSelections = null;
8931
    this._oAnchorRecord = null;
8932
    this._oAnchorCell = null;
8933
 
8934
    // Clear sort
8935
    this.set("sortedBy", null);
8936
},
8937
 
8938
/**
8939
 * Internal wrapper calls run() on render Chain instance.
8940
 *
8941
 * @method _runRenderChain
8942
 * @private
8943
 */
8944
_runRenderChain : function() {
8945
    this._oChainRender.run();
8946
},
8947
 
8948
/**
8949
 * Returns array of Records for current view. For example, if paginated, it
8950
 * returns the subset of Records for current page.
8951
 *
8952
 * @method _getViewRecords
8953
 * @protected
8954
 * @return {Array} Array of Records to display in current view.
8955
 */
8956
_getViewRecords : function() {
8957
    // Paginator is enabled, show a subset of Records
8958
    var oPaginator = this.get('paginator');
8959
    if(oPaginator) {
8960
        return this._oRecordSet.getRecords(
8961
                        oPaginator.getStartIndex(),
8962
                        oPaginator.getRowsPerPage());
8963
    }
8964
    // Not paginated, show all records
8965
    else {
8966
        return this._oRecordSet.getRecords();
8967
    }
8968
 
8969
},
8970
 
8971
/**
8972
 * Renders the view with existing Records from the RecordSet while
8973
 * maintaining sort, pagination, and selection states. For performance, reuses
8974
 * existing DOM elements when possible while deleting extraneous elements.
8975
 *
8976
 * @method render
8977
 */
8978
render : function() {
8979
//YAHOO.example.Performance.trialStart = new Date();
8980
 
8981
    this._oChainRender.stop();
8982
 
8983
    this.fireEvent("beforeRenderEvent");
8984
 
8985
    var i, j, k, len,
8986
        allRecords = this._getViewRecords();
8987
 
8988
 
8989
    // From the top, update in-place existing rows, so as to reuse DOM elements
8990
    var elTbody = this._elTbody,
8991
        loopN = this.get("renderLoopSize"),
8992
        nRecordsLength = allRecords.length;
8993
 
8994
    // Table has rows
8995
    if(nRecordsLength > 0) {
8996
        elTbody.style.display = "none";
8997
        while(elTbody.lastChild) {
8998
            elTbody.removeChild(elTbody.lastChild);
8999
        }
9000
        elTbody.style.display = "";
9001
 
9002
        // Set up the loop Chain to render rows
9003
        this._oChainRender.add({
9004
            method: function(oArg) {
9005
                if((this instanceof DT) && this._sId) {
9006
                    var i = oArg.nCurrentRecord,
9007
                        endRecordIndex = ((oArg.nCurrentRecord+oArg.nLoopLength) > nRecordsLength) ?
9008
                                nRecordsLength : (oArg.nCurrentRecord+oArg.nLoopLength),
9009
                        elRow, nextSibling;
9010
 
9011
                    elTbody.style.display = "none";
9012
 
9013
                    for(; i<endRecordIndex; i++) {
9014
                        elRow = Dom.get(allRecords[i].getId());
9015
                        elRow = elRow || this._addTrEl(allRecords[i]);
9016
                        nextSibling = elTbody.childNodes[i] || null;
9017
                        elTbody.insertBefore(elRow, nextSibling);
9018
                    }
9019
                    elTbody.style.display = "";
9020
 
9021
                    // Set up for the next loop
9022
                    oArg.nCurrentRecord = i;
9023
                }
9024
            },
9025
            scope: this,
9026
            iterations: (loopN > 0) ? Math.ceil(nRecordsLength/loopN) : 1,
9027
            argument: {
9028
                nCurrentRecord: 0,//nRecordsLength-1,  // Start at first Record
9029
                nLoopLength: (loopN > 0) ? loopN : nRecordsLength
9030
            },
9031
            timeout: (loopN > 0) ? 0 : -1
9032
        });
9033
 
9034
        // Post-render tasks
9035
        this._oChainRender.add({
9036
            method: function(oArg) {
9037
                if((this instanceof DT) && this._sId) {
9038
                    while(elTbody.rows.length > nRecordsLength) {
9039
                        elTbody.removeChild(elTbody.lastChild);
9040
                    }
9041
                    this._setFirstRow();
9042
                    this._setLastRow();
9043
                    this._setRowStripes();
9044
                    this._setSelections();
9045
                }
9046
            },
9047
            scope: this,
9048
            timeout: (loopN > 0) ? 0 : -1
9049
        });
9050
 
9051
    }
9052
    // Table has no rows
9053
    else {
9054
        // Set up the loop Chain to delete rows
9055
        var nTotal = elTbody.rows.length;
9056
        if(nTotal > 0) {
9057
            this._oChainRender.add({
9058
                method: function(oArg) {
9059
                    if((this instanceof DT) && this._sId) {
9060
                        var i = oArg.nCurrent,
9061
                            loopN = oArg.nLoopLength,
9062
                            nIterEnd = (i - loopN < 0) ? 0 : i - loopN;
9063
 
9064
                        elTbody.style.display = "none";
9065
 
9066
                        for(; i>nIterEnd; i--) {
9067
                            elTbody.deleteRow(-1);
9068
                        }
9069
                        elTbody.style.display = "";
9070
 
9071
                        // Set up for the next loop
9072
                        oArg.nCurrent = i;
9073
                    }
9074
                },
9075
                scope: this,
9076
                iterations: (loopN > 0) ? Math.ceil(nTotal/loopN) : 1,
9077
                argument: {
9078
                    nCurrent: nTotal,
9079
                    nLoopLength: (loopN > 0) ? loopN : nTotal
9080
                },
9081
                timeout: (loopN > 0) ? 0 : -1
9082
            });
9083
        }
9084
    }
9085
    this._runRenderChain();
9086
},
9087
 
9088
/**
9089
 * Disables DataTable UI.
9090
 *
9091
 * @method disable
9092
 */
9093
disable : function() {
9094
    this._disabled = true;
9095
    var elTable = this._elTable;
9096
    var elMask = this._elMask;
9097
    elMask.style.width = elTable.offsetWidth + "px";
9098
    elMask.style.height = elTable.offsetHeight + "px";
9099
    elMask.style.left = elTable.offsetLeft + "px";
9100
    elMask.style.display = "";
9101
    this.fireEvent("disableEvent");
9102
},
9103
 
9104
/**
9105
 * Undisables DataTable UI.
9106
 *
9107
 * @method undisable
9108
 */
9109
undisable : function() {
9110
    this._disabled = false;
9111
    this._elMask.style.display = "none";
9112
    this.fireEvent("undisableEvent");
9113
},
9114
 
9115
 /**
9116
 * Returns disabled state.
9117
 *
9118
 * @method isDisabled
9119
 * @return {Boolean} True if UI is disabled, otherwise false
9120
 */
9121
isDisabled : function() {
9122
    return this._disabled;
9123
},
9124
 
9125
/**
9126
 * Nulls out the entire DataTable instance and related objects, removes attached
9127
 * event listeners, and clears out DOM elements inside the container. After
9128
 * calling this method, the instance reference should be expliclitly nulled by
9129
 * implementer, as in myDataTable = null. Use with caution!
9130
 *
9131
 * @method destroy
9132
 */
9133
destroy : function() {
9134
    // Store for later
9135
    var instanceName = this.toString();
9136
 
9137
    this._oChainRender.stop();
9138
 
9139
    // Destroy ColumnDD and ColumnResizers
9140
    this._destroyColumnHelpers();
9141
 
9142
    // Destroy all CellEditors
9143
    var oCellEditor;
9144
    for(var i=0, len=this._oColumnSet.flat.length; i<len; i++) {
9145
        oCellEditor = this._oColumnSet.flat[i].editor;
9146
        if(oCellEditor && oCellEditor.destroy) {
9147
            oCellEditor.destroy();
9148
            this._oColumnSet.flat[i].editor = null;
9149
        }
9150
    }
9151
 
9152
    // Destroy Paginator
9153
    this._destroyPaginator();
9154
 
9155
    // Unhook custom events
9156
    this._oRecordSet.unsubscribeAll();
9157
    this.unsubscribeAll();
9158
 
9159
    // Unhook DOM events
9160
    Ev.removeListener(document, "click", this._onDocumentClick);
9161
 
9162
    // Clear out the container
9163
    this._destroyContainerEl(this._elContainer);
9164
 
9165
    // Null out objects
9166
    for(var param in this) {
9167
        if(lang.hasOwnProperty(this, param)) {
9168
            this[param] = null;
9169
        }
9170
    }
9171
 
9172
    // Clean up static values
9173
    DT._nCurrentCount--;
9174
 
9175
    if(DT._nCurrentCount < 1) {
9176
        if(DT._elDynStyleNode) {
9177
            document.getElementsByTagName('head')[0].removeChild(DT._elDynStyleNode);
9178
            DT._elDynStyleNode = null;
9179
        }
9180
    }
9181
 
9182
},
9183
 
9184
/**
9185
 * Displays message within secondary TBODY.
9186
 *
9187
 * @method showTableMessage
9188
 * @param sHTML {HTML} (optional) Value for innerHTML.
9189
 * @param sClassName {String} (optional) Classname.
9190
 */
9191
showTableMessage : function(sHTML, sClassName) {
9192
    var elCell = this._elMsgTd;
9193
    if(lang.isString(sHTML)) {
9194
        elCell.firstChild.innerHTML = sHTML;
9195
    }
9196
    if(lang.isString(sClassName)) {
9197
        elCell.className = sClassName;
9198
    }
9199
 
9200
    this._elMsgTbody.style.display = "";
9201
 
9202
    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
9203
},
9204
 
9205
/**
9206
 * Hides secondary TBODY.
9207
 *
9208
 * @method hideTableMessage
9209
 */
9210
hideTableMessage : function() {
9211
    if(this._elMsgTbody.style.display != "none") {
9212
        this._elMsgTbody.style.display = "none";
9213
        this._elMsgTbody.parentNode.style.width = "";
9214
        this.fireEvent("tableMsgHideEvent");
9215
    }
9216
},
9217
 
9218
/**
9219
 * Brings focus to the TBODY element. Alias to focusTbodyEl.
9220
 *
9221
 * @method focus
9222
 */
9223
focus : function() {
9224
    this.focusTbodyEl();
9225
},
9226
 
9227
/**
9228
 * Brings focus to the THEAD element.
9229
 *
9230
 * @method focusTheadEl
9231
 */
9232
focusTheadEl : function() {
9233
    this._focusEl(this._elThead);
9234
},
9235
 
9236
/**
9237
 * Brings focus to the TBODY element.
9238
 *
9239
 * @method focusTbodyEl
9240
 */
9241
focusTbodyEl : function() {
9242
    this._focusEl(this._elTbody);
9243
},
9244
 
9245
/**
9246
 * Setting display:none on DataTable or any parent may impact width validations.
9247
 * After setting display back to "", implementers should call this method to
9248
 * manually perform those validations.
9249
 *
9250
 * @method onShow
9251
 */
9252
onShow : function() {
9253
    this.validateColumnWidths();
9254
 
9255
    for(var allKeys = this._oColumnSet.keys, i=0, len=allKeys.length, col; i<len; i++) {
9256
        col = allKeys[i];
9257
        if(col._ddResizer) {
9258
            col._ddResizer.resetResizerEl();
9259
        }
9260
    }
9261
},
9262
 
9263
 
9264
 
9265
 
9266
 
9267
 
9268
 
9269
 
9270
 
9271
 
9272
 
9273
 
9274
 
9275
 
9276
 
9277
 
9278
 
9279
 
9280
 
9281
 
9282
 
9283
 
9284
 
9285
 
9286
 
9287
 
9288
 
9289
 
9290
 
9291
 
9292
 
9293
 
9294
 
9295
 
9296
 
9297
 
9298
 
9299
 
9300
 
9301
 
9302
 
9303
 
9304
 
9305
 
9306
 
9307
 
9308
 
9309
 
9310
 
9311
 
9312
 
9313
 
9314
 
9315
 
9316
 
9317
 
9318
 
9319
 
9320
 
9321
 
9322
 
9323
 
9324
 
9325
 
9326
 
9327
 
9328
 
9329
// RECORDSET FUNCTIONS
9330
 
9331
/**
9332
 * Returns Record index for given TR element or page row index.
9333
 *
9334
 * @method getRecordIndex
9335
 * @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR
9336
 * element reference or page row index.
9337
 * @return {Number} Record's RecordSet index, or null.
9338
 */
9339
getRecordIndex : function(row) {
9340
    var nTrIndex;
9341
 
9342
    if(!lang.isNumber(row)) {
9343
        // By Record
9344
        if(row instanceof YAHOO.widget.Record) {
9345
            return this._oRecordSet.getRecordIndex(row);
9346
        }
9347
        // By element reference
9348
        else {
9349
            // Find the TR element
9350
            var el = this.getTrEl(row);
9351
            if(el) {
9352
                nTrIndex = el.sectionRowIndex;
9353
            }
9354
        }
9355
    }
9356
    // By page row index
9357
    else {
9358
        nTrIndex = row;
9359
    }
9360
 
9361
    if(lang.isNumber(nTrIndex)) {
9362
        var oPaginator = this.get("paginator");
9363
        if(oPaginator) {
9364
            return oPaginator.get('recordOffset') + nTrIndex;
9365
        }
9366
        else {
9367
            return nTrIndex;
9368
        }
9369
    }
9370
 
9371
    return null;
9372
},
9373
 
9374
/**
9375
 * For the given identifier, returns the associated Record instance.
9376
 *
9377
 * @method getRecord
9378
 * @param row {HTMLElement | Number | String} DOM reference to a TR element (or
9379
 * child of a TR element), RecordSet position index, or Record ID.
9380
 * @return {YAHOO.widget.Record} Record instance.
9381
 */
9382
getRecord : function(row) {
9383
    var oRecord = this._oRecordSet.getRecord(row);
9384
 
9385
    if(!oRecord) {
9386
        // Validate TR element
9387
        var elRow = this.getTrEl(row);
9388
        if(elRow) {
9389
            oRecord = this._oRecordSet.getRecord(elRow.id);
9390
        }
9391
    }
9392
 
9393
    if(oRecord instanceof YAHOO.widget.Record) {
9394
        return this._oRecordSet.getRecord(oRecord);
9395
    }
9396
    else {
9397
        return null;
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
 
9439
 
9440
 
9441
 
9442
 
9443
 
9444
 
9445
 
9446
// COLUMN FUNCTIONS
9447
 
9448
/**
9449
 * For the given identifier, returns the associated Column instance. Note: For
9450
 * getting Columns by Column ID string, please use the method getColumnById().
9451
 *
9452
 * @method getColumn
9453
 * @param column {HTMLElement | String | Number} TH/TD element (or child of a
9454
 * TH/TD element), a Column key, or a ColumnSet key index.
9455
 * @return {YAHOO.widget.Column} Column instance.
9456
 */
9457
getColumn : function(column) {
9458
    var oColumn = this._oColumnSet.getColumn(column);
9459
 
9460
    if(!oColumn) {
9461
        // Validate TD element
9462
        var elCell = this.getTdEl(column);
9463
        if(elCell) {
9464
            oColumn = this._oColumnSet.getColumn(this.getCellIndex(elCell));
9465
        }
9466
        // Validate TH element
9467
        else {
9468
            elCell = this.getThEl(column);
9469
            if(elCell) {
9470
                // Find by TH el ID
9471
                var allColumns = this._oColumnSet.flat;
9472
                for(var i=0, len=allColumns.length; i<len; i++) {
9473
                    if(allColumns[i].getThEl().id === elCell.id) {
9474
                        oColumn = allColumns[i];
9475
                    }
9476
                }
9477
            }
9478
        }
9479
    }
9480
    if(!oColumn) {
9481
    }
9482
    return oColumn;
9483
},
9484
 
9485
/**
9486
 * For the given Column ID, returns the associated Column instance. Note: For
9487
 * getting Columns by key, please use the method getColumn().
9488
 *
9489
 * @method getColumnById
9490
 * @param column {String} Column ID string.
9491
 * @return {YAHOO.widget.Column} Column instance.
9492
 */
9493
getColumnById : function(column) {
9494
    return this._oColumnSet.getColumnById(column);
9495
},
9496
 
9497
/**
9498
 * For the given Column instance, returns next direction to sort.
9499
 *
9500
 * @method getColumnSortDir
9501
 * @param oColumn {YAHOO.widget.Column} Column instance.
9502
 * @param oSortedBy {Object} (optional) Specify the state, or use current state.
9503
 * @return {String} YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTableCLASS_DESC.
9504
 */
9505
getColumnSortDir : function(oColumn, oSortedBy) {
9506
    // Backward compatibility
9507
    if(oColumn.sortOptions && oColumn.sortOptions.defaultDir) {
9508
        if(oColumn.sortOptions.defaultDir == "asc") {
9509
            oColumn.sortOptions.defaultDir = DT.CLASS_ASC;
9510
        }
9511
        else if (oColumn.sortOptions.defaultDir == "desc") {
9512
            oColumn.sortOptions.defaultDir = DT.CLASS_DESC;
9513
        }
9514
    }
9515
 
9516
    // What is the Column's default sort direction?
9517
    var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultDir) ? oColumn.sortOptions.defaultDir : DT.CLASS_ASC;
9518
 
9519
    // Is the Column currently sorted?
9520
    var bSorted = false;
9521
    oSortedBy = oSortedBy || this.get("sortedBy");
9522
    if(oSortedBy && (oSortedBy.key === oColumn.key)) {
9523
        bSorted = true;
9524
        if(oSortedBy.dir) {
9525
            sortDir = (oSortedBy.dir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
9526
        }
9527
        else {
9528
            sortDir = (sortDir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
9529
        }
9530
    }
9531
    return sortDir;
9532
},
9533
 
9534
/**
9535
 * Overridable method gives implementers a hook to show loading message before
9536
 * sorting Column.
9537
 *
9538
 * @method doBeforeSortColumn
9539
 * @param oColumn {YAHOO.widget.Column} Column instance.
9540
 * @param sSortDir {String} YAHOO.widget.DataTable.CLASS_ASC or
9541
 * YAHOO.widget.DataTable.CLASS_DESC.
9542
 * @return {Boolean} Return true to continue sorting Column.
9543
 */
9544
doBeforeSortColumn : function(oColumn, sSortDir) {
9545
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
9546
    return true;
9547
},
9548
 
9549
/**
9550
 * Sorts given Column. If "dynamicData" is true, current selections are purged before
9551
 * a request is sent to the DataSource for data for the new state (using the
9552
 * request returned by "generateRequest()").
9553
 *
9554
 * @method sortColumn
9555
 * @param oColumn {YAHOO.widget.Column} Column instance.
9556
 * @param sDir {String} (Optional) YAHOO.widget.DataTable.CLASS_ASC or
9557
 * YAHOO.widget.DataTable.CLASS_DESC
9558
 */
9559
sortColumn : function(oColumn, sDir) {
9560
    if(oColumn && (oColumn instanceof YAHOO.widget.Column)) {
9561
        if(!oColumn.sortable) {
9562
            Dom.addClass(this.getThEl(oColumn), DT.CLASS_SORTABLE);
9563
        }
9564
 
9565
        // Validate given direction
9566
        if(sDir && (sDir !== DT.CLASS_ASC) && (sDir !== DT.CLASS_DESC)) {
9567
            sDir = null;
9568
        }
9569
 
9570
        // Get the sort dir
9571
        var sSortDir = sDir || this.getColumnSortDir(oColumn);
9572
 
9573
        // Is the Column currently sorted?
9574
        var oSortedBy = this.get("sortedBy") || {};
9575
        var bSorted = (oSortedBy.key === oColumn.key) ? true : false;
9576
 
9577
        var ok = this.doBeforeSortColumn(oColumn, sSortDir);
9578
        if(ok) {
9579
            // Server-side sort
9580
            if(this.get("dynamicData")) {
9581
                // Get current state
9582
                var oState = this.getState();
9583
 
9584
                // Reset record offset, if paginated
9585
                if(oState.pagination) {
9586
                    oState.pagination.recordOffset = 0;
9587
                }
9588
 
9589
                // Update sortedBy to new values
9590
                oState.sortedBy = {
9591
                    key: oColumn.key,
9592
                    dir: sSortDir
9593
                };
9594
 
9595
                // Get the request for the new state
9596
                var request = this.get("generateRequest")(oState, this);
9597
 
9598
                // Purge selections
9599
                this.unselectAllRows();
9600
                this.unselectAllCells();
9601
 
9602
                // Send request for new data
9603
                var callback = {
9604
                    success : this.onDataReturnSetRows,
9605
                    failure : this.onDataReturnSetRows,
9606
                    argument : oState, // Pass along the new state to the callback
9607
                    scope : this
9608
                };
9609
                this._oDataSource.sendRequest(request, callback);
9610
            }
9611
            // Client-side sort
9612
            else {
9613
                // Is there a custom sort handler function defined?
9614
                var sortFnc = (oColumn.sortOptions && lang.isFunction(oColumn.sortOptions.sortFunction)) ?
9615
                        // Custom sort function
9616
                        oColumn.sortOptions.sortFunction : null;
9617
 
9618
                // Sort the Records
9619
                if(!bSorted || sDir || sortFnc) {
9620
                    // Default sort function if necessary
9621
                    sortFnc = sortFnc || this.get("sortFunction");
9622
                    // Get the field to sort
9623
                    var sField = (oColumn.sortOptions && oColumn.sortOptions.field) ? oColumn.sortOptions.field : oColumn.field;
9624
 
9625
                    // Sort the Records
9626
                    this._oRecordSet.sortRecords(sortFnc, ((sSortDir == DT.CLASS_DESC) ? true : false), sField);
9627
                }
9628
                // Just reverse the Records
9629
                else {
9630
                    this._oRecordSet.reverseRecords();
9631
                }
9632
 
9633
                // Reset to first page if paginated
9634
                var oPaginator = this.get('paginator');
9635
                if (oPaginator) {
9636
                    // Set page silently, so as not to fire change event.
9637
                    oPaginator.setPage(1,true);
9638
                }
9639
 
9640
                // Update UI via sortedBy
9641
                this.render();
9642
                this.set("sortedBy", {key:oColumn.key, dir:sSortDir, column:oColumn});
9643
            }
9644
 
9645
            this.fireEvent("columnSortEvent",{column:oColumn,dir:sSortDir});
9646
            return;
9647
        }
9648
    }
9649
},
9650
 
9651
/**
9652
 * Sets given Column to given pixel width. If new width is less than minimum
9653
 * width, sets to minimum width. Updates oColumn.width value.
9654
 *
9655
 * @method setColumnWidth
9656
 * @param oColumn {YAHOO.widget.Column} Column instance.
9657
 * @param nWidth {Number} New width in pixels. A null value auto-sizes Column,
9658
 * subject to minWidth and maxAutoWidth validations.
9659
 */
9660
setColumnWidth : function(oColumn, nWidth) {
9661
    if(!(oColumn instanceof YAHOO.widget.Column)) {
9662
        oColumn = this.getColumn(oColumn);
9663
    }
9664
    if(oColumn) {
9665
        // Validate new width against minimum width
9666
        if(lang.isNumber(nWidth)) {
9667
            // This is why we must require a Number... :-|
9668
            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
9669
 
9670
            // Save state
9671
            oColumn.width = nWidth;
9672
 
9673
            // Resize the DOM elements
9674
            this._setColumnWidth(oColumn, nWidth+"px");
9675
 
9676
            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
9677
        }
9678
        // Unsets a width to auto-size
9679
        else if(nWidth === null) {
9680
            // Save state
9681
            oColumn.width = nWidth;
9682
 
9683
            // Resize the DOM elements
9684
            this._setColumnWidth(oColumn, "auto");
9685
            this.validateColumnWidths(oColumn);
9686
            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
9687
        }
9688
 
9689
        // Bug 2339454: resize then sort misaligment
9690
        this._clearTrTemplateEl();
9691
    }
9692
    else {
9693
    }
9694
},
9695
 
9696
/**
9697
 * Sets liner DIV elements of given Column to given width. When value should be
9698
 * auto-calculated to fit content overflow is set to visible, otherwise overflow
9699
 * is set to hidden. No validations against minimum width and no updating
9700
 * Column.width value.
9701
 *
9702
 * @method _setColumnWidth
9703
 * @param oColumn {YAHOO.widget.Column} Column instance.
9704
 * @param sWidth {String} New width value.
9705
 * @param sOverflow {String} Should be "hidden" when Column width is explicitly
9706
 * being set to a value, but should be "visible" when Column is meant to auto-fit content.
9707
 * @private
9708
 */
9709
_setColumnWidth : function(oColumn, sWidth, sOverflow) {
9710
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
9711
        sOverflow = sOverflow || (((sWidth === '') || (sWidth === 'auto')) ? 'visible' : 'hidden');
9712
 
9713
        // Dynamic style algorithm
9714
        if(!DT._bDynStylesFallback) {
9715
            this._setColumnWidthDynStyles(oColumn, sWidth, sOverflow);
9716
        }
9717
        // Dynamic function algorithm
9718
        else {
9719
            this._setColumnWidthDynFunction(oColumn, sWidth, sOverflow);
9720
        }
9721
    }
9722
    else {
9723
    }
9724
},
9725
 
9726
/**
9727
 * Updates width of a Column's liner DIV elements by dynamically creating a
9728
 * STYLE node and writing and updating CSS style rules to it. If this fails during
9729
 * runtime, the fallback method _setColumnWidthDynFunction() will be called.
9730
 * Notes: This technique is not performant in IE6. IE7 crashes if DataTable is
9731
 * nested within another TABLE element. For these cases, it is recommended to
9732
 * use the method _setColumnWidthDynFunction by setting _bDynStylesFallback to TRUE.
9733
 *
9734
 * @method _setColumnWidthDynStyles
9735
 * @param oColumn {YAHOO.widget.Column} Column instance.
9736
 * @param sWidth {String} New width value.
9737
 * @private
9738
 */
9739
_setColumnWidthDynStyles : function(oColumn, sWidth, sOverflow) {
9740
    var s = DT._elDynStyleNode,
9741
        rule;
9742
 
9743
    // Create a new STYLE node
9744
    if(!s) {
9745
        s = document.createElement('style');
9746
        s.type = 'text/css';
9747
        s = document.getElementsByTagName('head').item(0).appendChild(s);
9748
        DT._elDynStyleNode = s;
9749
    }
9750
 
9751
    // We have a STYLE node to update
9752
    if(s) {
9753
        // Use unique classname for this Column instance as a hook for resizing
9754
        var sClassname = "." + this.getId() + "-col-" + oColumn.getSanitizedKey() + " ." + DT.CLASS_LINER;
9755
 
9756
        // Hide for performance
9757
        if(this._elTbody) {
9758
            this._elTbody.style.display = 'none';
9759
        }
9760
 
9761
        rule = DT._oDynStyles[sClassname];
9762
 
9763
        // The Column does not yet have a rule
9764
        if(!rule) {
9765
            if(s.styleSheet && s.styleSheet.addRule) {
9766
                s.styleSheet.addRule(sClassname,"overflow:"+sOverflow);
9767
                s.styleSheet.addRule(sClassname,'width:'+sWidth);
9768
                rule = s.styleSheet.rules[s.styleSheet.rules.length-1];
9769
                DT._oDynStyles[sClassname] = rule;
9770
            }
9771
            else if(s.sheet && s.sheet.insertRule) {
9772
                s.sheet.insertRule(sClassname+" {overflow:"+sOverflow+";width:"+sWidth+";}",s.sheet.cssRules.length);
9773
                rule = s.sheet.cssRules[s.sheet.cssRules.length-1];
9774
                DT._oDynStyles[sClassname] = rule;
9775
            }
9776
        }
9777
        // We have a rule to update
9778
        else {
9779
            rule.style.overflow = sOverflow;
9780
            rule.style.width = sWidth;
9781
        }
9782
 
9783
        // Unhide
9784
        if(this._elTbody) {
9785
            this._elTbody.style.display = '';
9786
        }
9787
    }
9788
 
9789
    // That was not a success, we must call the fallback routine
9790
    if(!rule) {
9791
        DT._bDynStylesFallback = true;
9792
        this._setColumnWidthDynFunction(oColumn, sWidth);
9793
    }
9794
},
9795
 
9796
/**
9797
 * Updates width of a Column's liner DIV elements by dynamically creating a
9798
 * function to update all element style properties in one pass. Note: This
9799
 * technique is not supported in sandboxed environments that prohibit EVALs.
9800
 *
9801
 * @method _setColumnWidthDynFunction
9802
 * @param oColumn {YAHOO.widget.Column} Column instance.
9803
 * @param sWidth {String} New width value.
9804
 * @private
9805
 */
9806
_setColumnWidthDynFunction : function(oColumn, sWidth, sOverflow) {
9807
    // TODO: why is this here?
9808
    if(sWidth == 'auto') {
9809
        sWidth = '';
9810
    }
9811
 
9812
    // Create one function for each value of rows.length
9813
    var rowslen = this._elTbody ? this._elTbody.rows.length : 0;
9814
 
9815
    // Dynamically create the function
9816
    if (!this._aDynFunctions[rowslen]) {
9817
 
9818
        //Compile a custom function to do all the liner div width
9819
        //assignments at the same time.  A unique function is required
9820
        //for each unique number of rows in _elTbody.  This will
9821
        //result in a function declaration like:
9822
        //function (oColumn,sWidth,sOverflow) {
9823
        //    var colIdx = oColumn.getKeyIndex();
9824
        //    oColumn.getThLinerEl().style.overflow =
9825
        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.overflow =
9826
        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.overflow =
9827
        //    ... (for all row indices in this._elTbody.rows.length - 1)
9828
        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.overflow =
9829
        //    sOverflow;
9830
        //    oColumn.getThLinerEl().style.width =
9831
        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.width =
9832
        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.width =
9833
        //    ... (for all row indices in this._elTbody.rows.length - 1)
9834
        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.width =
9835
        //    sWidth;
9836
        //}
9837
 
9838
        var i,j,k;
9839
        var resizerDef = [
9840
            'var colIdx=oColumn.getKeyIndex();',
9841
            'oColumn.getThLinerEl().style.overflow='
9842
        ];
9843
        for (i=rowslen-1, j=2; i >= 0; --i) {
9844
            resizerDef[j++] = 'this._elTbody.rows[';
9845
            resizerDef[j++] = i;
9846
            resizerDef[j++] = '].cells[colIdx].firstChild.style.overflow=';
9847
        }
9848
        resizerDef[j] = 'sOverflow;';
9849
        resizerDef[j+1] = 'oColumn.getThLinerEl().style.width=';
9850
        for (i=rowslen-1, k=j+2; i >= 0; --i) {
9851
            resizerDef[k++] = 'this._elTbody.rows[';
9852
            resizerDef[k++] = i;
9853
            resizerDef[k++] = '].cells[colIdx].firstChild.style.width=';
9854
        }
9855
        resizerDef[k] = 'sWidth;';
9856
        this._aDynFunctions[rowslen] =
9857
            new Function('oColumn','sWidth','sOverflow',resizerDef.join(''));
9858
    }
9859
 
9860
    // Get the function to execute
9861
    var resizerFn = this._aDynFunctions[rowslen];
9862
 
9863
    // TODO: Hide TBODY for performance in _setColumnWidthDynFunction?
9864
    if (resizerFn) {
9865
        resizerFn.call(this,oColumn,sWidth,sOverflow);
9866
    }
9867
},
9868
 
9869
/**
9870
 * For one or all Columns, when Column is not hidden, width is not set, and minWidth
9871
 * and/or maxAutoWidth is set, validates auto-width against minWidth and maxAutoWidth.
9872
 *
9873
 * @method validateColumnWidths
9874
 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
9875
 */
9876
validateColumnWidths : function(oColumn) {
9877
    var elColgroup = this._elColgroup;
9878
    var elColgroupClone = elColgroup.cloneNode(true);
9879
    var bNeedsValidation = false;
9880
    var allKeys = this._oColumnSet.keys;
9881
    var elThLiner;
9882
    // Validate just one Column's minWidth and/or maxAutoWidth
9883
    if(oColumn && !oColumn.hidden && !oColumn.width && (oColumn.getKeyIndex() !== null)) {
9884
            elThLiner = oColumn.getThLinerEl();
9885
            if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
9886
                elColgroupClone.childNodes[oColumn.getKeyIndex()].style.width =
9887
                        oColumn.minWidth +
9888
                        (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
9889
                        (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
9890
                bNeedsValidation = true;
9891
            }
9892
            else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
9893
                this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
9894
            }
9895
    }
9896
    // Validate all Columns
9897
    else {
9898
        for(var i=0, len=allKeys.length; i<len; i++) {
9899
            oColumn = allKeys[i];
9900
            if(!oColumn.hidden && !oColumn.width) {
9901
                elThLiner = oColumn.getThLinerEl();
9902
                if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
9903
                    elColgroupClone.childNodes[i].style.width =
9904
                            oColumn.minWidth +
9905
                            (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
9906
                            (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
9907
                    bNeedsValidation = true;
9908
                }
9909
                else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
9910
                    this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
9911
                }
9912
            }
9913
        }
9914
    }
9915
    if(bNeedsValidation) {
9916
        elColgroup.parentNode.replaceChild(elColgroupClone, elColgroup);
9917
        this._elColgroup = elColgroupClone;
9918
    }
9919
},
9920
 
9921
/**
9922
 * Clears minWidth.
9923
 *
9924
 * @method _clearMinWidth
9925
 * @param oColumn {YAHOO.widget.Column} Which Column.
9926
 * @private
9927
 */
9928
_clearMinWidth : function(oColumn) {
9929
    if(oColumn.getKeyIndex() !== null) {
9930
        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = '';
9931
    }
9932
},
9933
 
9934
/**
9935
 * Restores minWidth.
9936
 *
9937
 * @method _restoreMinWidth
9938
 * @param oColumn {YAHOO.widget.Column} Which Column.
9939
 * @private
9940
 */
9941
_restoreMinWidth : function(oColumn) {
9942
    if(oColumn.minWidth && (oColumn.getKeyIndex() !== null)) {
9943
        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = oColumn.minWidth + 'px';
9944
    }
9945
},
9946
 
9947
/**
9948
 * Hides given Column. NOTE: You cannot hide/show nested Columns. You can only
9949
 * hide/show non-nested Columns, and top-level parent Columns (which will
9950
 * hide/show all children Columns).
9951
 *
9952
 * @method hideColumn
9953
 * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column
9954
 * instance, TH/TD element (or child of a TH/TD element), a Column key, or a
9955
 * ColumnSet key index.
9956
 */
9957
hideColumn : function(oColumn) {
9958
    if(!(oColumn instanceof YAHOO.widget.Column)) {
9959
        oColumn = this.getColumn(oColumn);
9960
    }
9961
    // Only top-level Columns can get hidden due to issues in FF2 and SF3
9962
    if(oColumn && !oColumn.hidden && oColumn.getTreeIndex() !== null) {
9963
 
9964
        var allrows = this.getTbodyEl().rows;
9965
        var l = allrows.length;
9966
        var allDescendants = this._oColumnSet.getDescendants(oColumn);
9967
 
9968
        // Hide each nested Column
9969
        for(var i=0, len=allDescendants.length; i<len; i++) {
9970
            var thisColumn = allDescendants[i];
9971
            thisColumn.hidden = true;
9972
 
9973
            // Style the head cell
9974
            Dom.addClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
9975
 
9976
            // Does this Column have body cells?
9977
            var thisKeyIndex = thisColumn.getKeyIndex();
9978
            if(thisKeyIndex !== null) {
9979
                // Clear minWidth
9980
                this._clearMinWidth(oColumn);
9981
 
9982
                // Style the body cells
9983
                for(var j=0;j<l;j++) {
9984
                    Dom.addClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
9985
                }
9986
            }
9987
 
9988
            this.fireEvent("columnHideEvent",{column:thisColumn});
9989
        }
9990
 
9991
        this._repaintOpera();
9992
        this._clearTrTemplateEl();
9993
    }
9994
    else {
9995
    }
9996
},
9997
 
9998
/**
9999
 * Shows given Column. NOTE: You cannot hide/show nested Columns. You can only
10000
 * hide/show non-nested Columns, and top-level parent Columns (which will
10001
 * hide/show all children Columns).
10002
 *
10003
 * @method showColumn
10004
 * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column
10005
 * instance, TH/TD element (or child of a TH/TD element), a Column key, or a
10006
 * ColumnSet key index.
10007
 */
10008
showColumn : function(oColumn) {
10009
    if(!(oColumn instanceof YAHOO.widget.Column)) {
10010
        oColumn = this.getColumn(oColumn);
10011
    }
10012
    // Only top-level Columns can get hidden
10013
    if(oColumn && oColumn.hidden && (oColumn.getTreeIndex() !== null)) {
10014
        var allrows = this.getTbodyEl().rows;
10015
        var l = allrows.length;
10016
        var allDescendants = this._oColumnSet.getDescendants(oColumn);
10017
 
10018
        // Show each nested Column
10019
        for(var i=0, len=allDescendants.length; i<len; i++) {
10020
            var thisColumn = allDescendants[i];
10021
            thisColumn.hidden = false;
10022
 
10023
            // Unstyle the head cell
10024
            Dom.removeClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
10025
 
10026
            // Does this Column have body cells?
10027
            var thisKeyIndex = thisColumn.getKeyIndex();
10028
            if(thisKeyIndex !== null) {
10029
                // Restore minWidth
10030
                this._restoreMinWidth(oColumn);
10031
 
10032
 
10033
                // Unstyle the body cells
10034
                for(var j=0;j<l;j++) {
10035
                    Dom.removeClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
10036
                }
10037
            }
10038
 
10039
            this.fireEvent("columnShowEvent",{column:thisColumn});
10040
        }
10041
        this._clearTrTemplateEl();
10042
    }
10043
    else {
10044
    }
10045
},
10046
 
10047
/**
10048
 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
10049
 * non-nested Columns, and top-level parent Columns (which will remove all
10050
 * children Columns).
10051
 *
10052
 * @method removeColumn
10053
 * @param oColumn {YAHOO.widget.Column} Column instance.
10054
 * @return oColumn {YAHOO.widget.Column} Removed Column instance.
10055
 */
10056
removeColumn : function(oColumn) {
10057
    // Validate Column
10058
    if(!(oColumn instanceof YAHOO.widget.Column)) {
10059
        oColumn = this.getColumn(oColumn);
10060
    }
10061
    if(oColumn) {
10062
        var nColTreeIndex = oColumn.getTreeIndex();
10063
        if(nColTreeIndex !== null) {
10064
            // Which key index(es)
10065
            var i, len,
10066
                aKeyIndexes = oColumn.getKeyIndex();
10067
            // Must be a parent Column
10068
            if(aKeyIndexes === null) {
10069
                var descKeyIndexes = [];
10070
                var allDescendants = this._oColumnSet.getDescendants(oColumn);
10071
                for(i=0, len=allDescendants.length; i<len; i++) {
10072
                    // Is this descendant a key Column?
10073
                    var thisKey = allDescendants[i].getKeyIndex();
10074
                    if(thisKey !== null) {
10075
                        descKeyIndexes[descKeyIndexes.length] = thisKey;
10076
                    }
10077
                }
10078
                if(descKeyIndexes.length > 0) {
10079
                    aKeyIndexes = descKeyIndexes;
10080
                }
10081
            }
10082
            // Must be a key Column
10083
            else {
10084
                aKeyIndexes = [aKeyIndexes];
10085
            }
10086
 
10087
            if(aKeyIndexes !== null) {
10088
                // Sort the indexes so we can remove from the right
10089
                aKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
10090
 
10091
                // Destroy previous THEAD
10092
                this._destroyTheadEl();
10093
 
10094
                // Create new THEAD
10095
                var aOrigColumnDefs = this._oColumnSet.getDefinitions();
10096
                oColumn = aOrigColumnDefs.splice(nColTreeIndex,1)[0];
10097
                this._initColumnSet(aOrigColumnDefs);
10098
                this._initTheadEl();
10099
 
10100
                // Remove COL
10101
                for(i=aKeyIndexes.length-1; i>-1; i--) {
10102
                    this._removeColgroupColEl(aKeyIndexes[i]);
10103
                }
10104
 
10105
                // Remove TD
10106
                var allRows = this._elTbody.rows;
10107
                if(allRows.length > 0) {
10108
                    var loopN = this.get("renderLoopSize"),
10109
                        loopEnd = allRows.length;
10110
                    this._oChainRender.add({
10111
                        method: function(oArg) {
10112
                            if((this instanceof DT) && this._sId) {
10113
                                var i = oArg.nCurrentRow,
10114
                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
10115
                                    aIndexes = oArg.aIndexes,
10116
                                    j;
10117
                                for(; i < len; ++i) {
10118
                                    for(j = aIndexes.length-1; j>-1; j--) {
10119
                                        allRows[i].removeChild(allRows[i].childNodes[aIndexes[j]]);
10120
                                    }
10121
                                }
10122
                                oArg.nCurrentRow = i;
10123
                            }
10124
                        },
10125
                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10126
                        argument: {nCurrentRow:0, aIndexes:aKeyIndexes},
10127
                        scope: this,
10128
                        timeout: (loopN > 0) ? 0 : -1
10129
                    });
10130
                    this._runRenderChain();
10131
                }
10132
 
10133
                this.fireEvent("columnRemoveEvent",{column:oColumn});
10134
                return oColumn;
10135
            }
10136
        }
10137
    }
10138
},
10139
 
10140
/**
10141
 * Inserts given Column at the index if given, otherwise at the end. NOTE: You
10142
 * can only add non-nested Columns and top-level parent Columns. You cannot add
10143
 * a nested Column to an existing parent.
10144
 *
10145
 * @method insertColumn
10146
 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
10147
 * definition or a Column instance.
10148
 * @param index {Number} (optional) New tree index.
10149
 * @return oColumn {YAHOO.widget.Column} Inserted Column instance.
10150
 */
10151
insertColumn : function(oColumn, index) {
10152
    // Validate Column
10153
    if(oColumn instanceof YAHOO.widget.Column) {
10154
        oColumn = oColumn.getDefinition();
10155
    }
10156
    else if(oColumn.constructor !== Object) {
10157
        return;
10158
    }
10159
 
10160
    // Validate index or append new Column to the end of the ColumnSet
10161
    var oColumnSet = this._oColumnSet;
10162
    if(!lang.isValue(index) || !lang.isNumber(index)) {
10163
        index = oColumnSet.tree[0].length;
10164
    }
10165
 
10166
    // Destroy previous THEAD
10167
    this._destroyTheadEl();
10168
 
10169
    // Create new THEAD
10170
    var aNewColumnDefs = this._oColumnSet.getDefinitions();
10171
    aNewColumnDefs.splice(index, 0, oColumn);
10172
    this._initColumnSet(aNewColumnDefs);
10173
    this._initTheadEl();
10174
 
10175
    // Need to refresh the reference
10176
    oColumnSet = this._oColumnSet;
10177
    var oNewColumn = oColumnSet.tree[0][index];
10178
 
10179
    // Get key index(es) for new Column
10180
    var i, len,
10181
        descKeyIndexes = [];
10182
    var allDescendants = oColumnSet.getDescendants(oNewColumn);
10183
    for(i=0, len=allDescendants.length; i<len; i++) {
10184
        // Is this descendant a key Column?
10185
        var thisKey = allDescendants[i].getKeyIndex();
10186
        if(thisKey !== null) {
10187
            descKeyIndexes[descKeyIndexes.length] = thisKey;
10188
        }
10189
    }
10190
 
10191
    if(descKeyIndexes.length > 0) {
10192
        // Sort the indexes
10193
        var newIndex = descKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
10194
 
10195
        // Add COL
10196
        for(i=descKeyIndexes.length-1; i>-1; i--) {
10197
            this._insertColgroupColEl(descKeyIndexes[i]);
10198
        }
10199
 
10200
        // Add TD
10201
        var allRows = this._elTbody.rows;
10202
        if(allRows.length > 0) {
10203
            var loopN = this.get("renderLoopSize"),
10204
                loopEnd = allRows.length;
10205
 
10206
            // Get templates for each new TD
10207
            var aTdTemplates = [],
10208
                elTdTemplate;
10209
            for(i=0, len=descKeyIndexes.length; i<len; i++) {
10210
                var thisKeyIndex = descKeyIndexes[i];
10211
                elTdTemplate = this._getTrTemplateEl().childNodes[i].cloneNode(true);
10212
                elTdTemplate = this._formatTdEl(this._oColumnSet.keys[thisKeyIndex], elTdTemplate, thisKeyIndex, (thisKeyIndex===this._oColumnSet.keys.length-1));
10213
                aTdTemplates[thisKeyIndex] = elTdTemplate;
10214
            }
10215
 
10216
            this._oChainRender.add({
10217
                method: function(oArg) {
10218
                    if((this instanceof DT) && this._sId) {
10219
                        var i = oArg.nCurrentRow, j,
10220
                            descKeyIndexes = oArg.descKeyIndexes,
10221
                            len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
10222
                            nextSibling;
10223
                        for(; i < len; ++i) {
10224
                            nextSibling = allRows[i].childNodes[newIndex] || null;
10225
                            for(j=descKeyIndexes.length-1; j>-1; j--) {
10226
                                allRows[i].insertBefore(oArg.aTdTemplates[descKeyIndexes[j]].cloneNode(true), nextSibling);
10227
                            }
10228
                        }
10229
                        oArg.nCurrentRow = i;
10230
                    }
10231
                },
10232
                iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10233
                argument: {nCurrentRow:0,aTdTemplates:aTdTemplates,descKeyIndexes:descKeyIndexes},
10234
                scope: this,
10235
                timeout: (loopN > 0) ? 0 : -1
10236
            });
10237
            this._runRenderChain();
10238
        }
10239
 
10240
        this.fireEvent("columnInsertEvent",{column:oColumn,index:index});
10241
        return oNewColumn;
10242
    }
10243
},
10244
 
10245
/**
10246
 * Removes given Column and inserts into given tree index. NOTE: You
10247
 * can only reorder non-nested Columns and top-level parent Columns. You cannot
10248
 * reorder a nested Column to an existing parent.
10249
 *
10250
 * @method reorderColumn
10251
 * @param oColumn {YAHOO.widget.Column} Column instance.
10252
 * @param index {Number} New tree index.
10253
 * @return oColumn {YAHOO.widget.Column} Reordered Column instance.
10254
 */
10255
reorderColumn : function(oColumn, index) {
10256
    // Validate Column and new index
10257
    if(!(oColumn instanceof YAHOO.widget.Column)) {
10258
        oColumn = this.getColumn(oColumn);
10259
    }
10260
    if(oColumn && YAHOO.lang.isNumber(index)) {
10261
        var nOrigTreeIndex = oColumn.getTreeIndex();
10262
        if((nOrigTreeIndex !== null) && (nOrigTreeIndex !== index)) {
10263
            // Which key index(es)
10264
            var i, len,
10265
                aOrigKeyIndexes = oColumn.getKeyIndex(),
10266
                allDescendants,
10267
                descKeyIndexes = [],
10268
                thisKey;
10269
            // Must be a parent Column...
10270
            if(aOrigKeyIndexes === null) {
10271
                allDescendants = this._oColumnSet.getDescendants(oColumn);
10272
                for(i=0, len=allDescendants.length; i<len; i++) {
10273
                    // Is this descendant a key Column?
10274
                    thisKey = allDescendants[i].getKeyIndex();
10275
                    if(thisKey !== null) {
10276
                        descKeyIndexes[descKeyIndexes.length] = thisKey;
10277
                    }
10278
                }
10279
                if(descKeyIndexes.length > 0) {
10280
                    aOrigKeyIndexes = descKeyIndexes;
10281
                }
10282
            }
10283
            // ...or else must be a key Column
10284
            else {
10285
                aOrigKeyIndexes = [aOrigKeyIndexes];
10286
            }
10287
 
10288
            if(aOrigKeyIndexes !== null) {
10289
                // Sort the indexes
10290
                aOrigKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
10291
 
10292
                // Destroy previous THEAD
10293
                this._destroyTheadEl();
10294
 
10295
                // Create new THEAD
10296
                var aColumnDefs = this._oColumnSet.getDefinitions();
10297
                var oColumnDef = aColumnDefs.splice(nOrigTreeIndex,1)[0];
10298
                aColumnDefs.splice(index, 0, oColumnDef);
10299
                this._initColumnSet(aColumnDefs);
10300
                this._initTheadEl();
10301
 
10302
                // Need to refresh the reference
10303
                var oNewColumn = this._oColumnSet.tree[0][index];
10304
 
10305
                // What are new key index(es)
10306
                var aNewKeyIndexes = oNewColumn.getKeyIndex();
10307
                // Must be a parent Column
10308
                if(aNewKeyIndexes === null) {
10309
                    descKeyIndexes = [];
10310
                    allDescendants = this._oColumnSet.getDescendants(oNewColumn);
10311
                    for(i=0, len=allDescendants.length; i<len; i++) {
10312
                        // Is this descendant a key Column?
10313
                        thisKey = allDescendants[i].getKeyIndex();
10314
                        if(thisKey !== null) {
10315
                            descKeyIndexes[descKeyIndexes.length] = thisKey;
10316
                        }
10317
                    }
10318
                    if(descKeyIndexes.length > 0) {
10319
                        aNewKeyIndexes = descKeyIndexes;
10320
                    }
10321
                }
10322
                // Must be a key Column
10323
                else {
10324
                    aNewKeyIndexes = [aNewKeyIndexes];
10325
                }
10326
 
10327
                // Sort the new indexes and grab the first one for the new location
10328
                var newIndex = aNewKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
10329
 
10330
                // Reorder COL
10331
                this._reorderColgroupColEl(aOrigKeyIndexes, newIndex);
10332
 
10333
                // Reorder TD
10334
                var allRows = this._elTbody.rows;
10335
                if(allRows.length > 0) {
10336
                    var loopN = this.get("renderLoopSize"),
10337
                        loopEnd = allRows.length;
10338
                    this._oChainRender.add({
10339
                        method: function(oArg) {
10340
                            if((this instanceof DT) && this._sId) {
10341
                                var i = oArg.nCurrentRow, j, tmpTds, nextSibling,
10342
                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
10343
                                    aIndexes = oArg.aIndexes, thisTr;
10344
                                // For each row
10345
                                for(; i < len; ++i) {
10346
                                    tmpTds = [];
10347
                                    thisTr = allRows[i];
10348
 
10349
                                    // Remove each TD
10350
                                    for(j=aIndexes.length-1; j>-1; j--) {
10351
                                        tmpTds.push(thisTr.removeChild(thisTr.childNodes[aIndexes[j]]));
10352
                                    }
10353
 
10354
                                    // Insert each TD
10355
                                    nextSibling = thisTr.childNodes[newIndex] || null;
10356
                                    for(j=tmpTds.length-1; j>-1; j--) {
10357
                                        thisTr.insertBefore(tmpTds[j], nextSibling);
10358
                                    }
10359
                                }
10360
                                oArg.nCurrentRow = i;
10361
                            }
10362
                        },
10363
                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10364
                        argument: {nCurrentRow:0, aIndexes:aOrigKeyIndexes},
10365
                        scope: this,
10366
                        timeout: (loopN > 0) ? 0 : -1
10367
                    });
10368
                    this._runRenderChain();
10369
                }
10370
 
10371
                this.fireEvent("columnReorderEvent",{column:oNewColumn, oldIndex:nOrigTreeIndex});
10372
                return oNewColumn;
10373
            }
10374
        }
10375
    }
10376
},
10377
 
10378
/**
10379
 * Selects given Column. NOTE: You cannot select/unselect nested Columns. You can only
10380
 * select/unselect non-nested Columns, and bottom-level key Columns.
10381
 *
10382
 * @method selectColumn
10383
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10384
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10385
 */
10386
selectColumn : function(oColumn) {
10387
    oColumn = this.getColumn(oColumn);
10388
    if(oColumn && !oColumn.selected) {
10389
        // Only bottom-level Columns can get hidden
10390
        if(oColumn.getKeyIndex() !== null) {
10391
            oColumn.selected = true;
10392
 
10393
            // Update head cell
10394
            var elTh = oColumn.getThEl();
10395
            Dom.addClass(elTh,DT.CLASS_SELECTED);
10396
 
10397
            // Update body cells
10398
            var allRows = this.getTbodyEl().rows;
10399
            var oChainRender = this._oChainRender;
10400
            oChainRender.add({
10401
                method: function(oArg) {
10402
                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10403
                        Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
10404
                    }
10405
                    oArg.rowIndex++;
10406
                },
10407
                scope: this,
10408
                iterations: allRows.length,
10409
                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
10410
            });
10411
 
10412
            this._clearTrTemplateEl();
10413
 
10414
            this._elTbody.style.display = "none";
10415
            this._runRenderChain();
10416
            this._elTbody.style.display = "";
10417
 
10418
            this.fireEvent("columnSelectEvent",{column:oColumn});
10419
        }
10420
        else {
10421
        }
10422
    }
10423
},
10424
 
10425
/**
10426
 * Unselects given Column. NOTE: You cannot select/unselect nested Columns. You can only
10427
 * select/unselect non-nested Columns, and bottom-level key Columns.
10428
 *
10429
 * @method unselectColumn
10430
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10431
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10432
 */
10433
unselectColumn : function(oColumn) {
10434
    oColumn = this.getColumn(oColumn);
10435
    if(oColumn && oColumn.selected) {
10436
        // Only bottom-level Columns can get hidden
10437
        if(oColumn.getKeyIndex() !== null) {
10438
            oColumn.selected = false;
10439
 
10440
            // Update head cell
10441
            var elTh = oColumn.getThEl();
10442
            Dom.removeClass(elTh,DT.CLASS_SELECTED);
10443
 
10444
            // Update body cells
10445
            var allRows = this.getTbodyEl().rows;
10446
            var oChainRender = this._oChainRender;
10447
            oChainRender.add({
10448
                method: function(oArg) {
10449
                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10450
                        Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
10451
                    }
10452
                    oArg.rowIndex++;
10453
                },
10454
                scope: this,
10455
                iterations:allRows.length,
10456
                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
10457
            });
10458
 
10459
            this._clearTrTemplateEl();
10460
 
10461
            this._elTbody.style.display = "none";
10462
            this._runRenderChain();
10463
            this._elTbody.style.display = "";
10464
 
10465
            this.fireEvent("columnUnselectEvent",{column:oColumn});
10466
        }
10467
        else {
10468
        }
10469
    }
10470
},
10471
 
10472
/**
10473
 * Returns an array selected Column instances.
10474
 *
10475
 * @method getSelectedColumns
10476
 * @return {YAHOO.widget.Column[]} Array of Column instances.
10477
 */
10478
getSelectedColumns : function(oColumn) {
10479
    var selectedColumns = [];
10480
    var aKeys = this._oColumnSet.keys;
10481
    for(var i=0,len=aKeys.length; i<len; i++) {
10482
        if(aKeys[i].selected) {
10483
            selectedColumns[selectedColumns.length] = aKeys[i];
10484
        }
10485
    }
10486
    return selectedColumns;
10487
},
10488
 
10489
/**
10490
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
10491
 * NOTE: You cannot highlight/unhighlight nested Columns. You can only
10492
 * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
10493
 *
10494
 * @method highlightColumn
10495
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10496
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10497
 */
10498
highlightColumn : function(column) {
10499
    var oColumn = this.getColumn(column);
10500
    // Only bottom-level Columns can get highlighted
10501
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
10502
        // Update head cell
10503
        var elTh = oColumn.getThEl();
10504
        Dom.addClass(elTh,DT.CLASS_HIGHLIGHTED);
10505
 
10506
        // Update body cells
10507
        var allRows = this.getTbodyEl().rows;
10508
        var oChainRender = this._oChainRender;
10509
        oChainRender.add({
10510
            method: function(oArg) {
10511
                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10512
                    Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
10513
                }
10514
                oArg.rowIndex++;
10515
            },
10516
            scope: this,
10517
            iterations:allRows.length,
10518
            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
10519
            timeout: -1
10520
        });
10521
        this._elTbody.style.display = "none";
10522
        this._runRenderChain();
10523
        this._elTbody.style.display = "";
10524
 
10525
        this.fireEvent("columnHighlightEvent",{column:oColumn});
10526
    }
10527
    else {
10528
    }
10529
},
10530
 
10531
/**
10532
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
10533
 * NOTE: You cannot highlight/unhighlight nested Columns. You can only
10534
 * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
10535
 *
10536
 * @method unhighlightColumn
10537
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
10538
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
10539
 */
10540
unhighlightColumn : function(column) {
10541
    var oColumn = this.getColumn(column);
10542
    // Only bottom-level Columns can get highlighted
10543
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
10544
        // Update head cell
10545
        var elTh = oColumn.getThEl();
10546
        Dom.removeClass(elTh,DT.CLASS_HIGHLIGHTED);
10547
 
10548
        // Update body cells
10549
        var allRows = this.getTbodyEl().rows;
10550
        var oChainRender = this._oChainRender;
10551
        oChainRender.add({
10552
            method: function(oArg) {
10553
                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
10554
                    Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
10555
                }
10556
                oArg.rowIndex++;
10557
            },
10558
            scope: this,
10559
            iterations:allRows.length,
10560
            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
10561
            timeout: -1
10562
        });
10563
        this._elTbody.style.display = "none";
10564
        this._runRenderChain();
10565
        this._elTbody.style.display = "";
10566
 
10567
        this.fireEvent("columnUnhighlightEvent",{column:oColumn});
10568
    }
10569
    else {
10570
    }
10571
},
10572
 
10573
 
10574
 
10575
 
10576
 
10577
 
10578
 
10579
 
10580
 
10581
 
10582
 
10583
 
10584
 
10585
 
10586
 
10587
 
10588
 
10589
 
10590
 
10591
 
10592
 
10593
 
10594
 
10595
 
10596
 
10597
 
10598
 
10599
 
10600
 
10601
 
10602
 
10603
 
10604
 
10605
 
10606
 
10607
 
10608
 
10609
 
10610
 
10611
 
10612
 
10613
 
10614
 
10615
 
10616
// ROW FUNCTIONS
10617
 
10618
/**
10619
 * Adds one new Record of data into the RecordSet at the index if given,
10620
 * otherwise at the end. If the new Record is in page view, the
10621
 * corresponding DOM elements are also updated.
10622
 *
10623
 * @method addRow
10624
 * @param oData {Object} Object literal of data for the row.
10625
 * @param index {Number} (optional) RecordSet position index at which to add data.
10626
 */
10627
addRow : function(oData, index) {
10628
    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
10629
        return;
10630
    }
10631
 
10632
    if(oData && lang.isObject(oData)) {
10633
        var oRecord = this._oRecordSet.addRecord(oData, index);
10634
        if(oRecord) {
10635
            var recIndex;
10636
            var oPaginator = this.get('paginator');
10637
 
10638
            // Paginated
10639
            if (oPaginator) {
10640
                // Update the paginator's totalRecords
10641
                var totalRecords = oPaginator.get('totalRecords');
10642
                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
10643
                    oPaginator.set('totalRecords',totalRecords + 1);
10644
                }
10645
 
10646
                recIndex = this.getRecordIndex(oRecord);
10647
                var endRecIndex = (oPaginator.getPageRecords())[1];
10648
 
10649
                // New record affects the view
10650
                if (recIndex <= endRecIndex) {
10651
                    // Defer UI updates to the render method
10652
                    this.render();
10653
                }
10654
 
10655
                this.fireEvent("rowAddEvent", {record:oRecord});
10656
                return;
10657
            }
10658
            // Not paginated
10659
            else {
10660
                recIndex = this.getRecordIndex(oRecord);
10661
                if(lang.isNumber(recIndex)) {
10662
                    // Add the TR element
10663
                    this._oChainRender.add({
10664
                        method: function(oArg) {
10665
                            if((this instanceof DT) && this._sId) {
10666
                                var oRecord = oArg.record;
10667
                                var recIndex = oArg.recIndex;
10668
                                var elNewTr = this._addTrEl(oRecord);
10669
                                if(elNewTr) {
10670
                                    var elNext = (this._elTbody.rows[recIndex]) ? this._elTbody.rows[recIndex] : null;
10671
                                    this._elTbody.insertBefore(elNewTr, elNext);
10672
 
10673
                                    // Set FIRST/LAST
10674
                                    if(recIndex === 0) {
10675
                                        this._setFirstRow();
10676
                                    }
10677
                                    if(elNext === null) {
10678
                                        this._setLastRow();
10679
                                    }
10680
                                    // Set EVEN/ODD
10681
                                    this._setRowStripes();
10682
 
10683
                                    this.hideTableMessage();
10684
 
10685
                                    this.fireEvent("rowAddEvent", {record:oRecord});
10686
                                }
10687
                            }
10688
                        },
10689
                        argument: {record: oRecord, recIndex: recIndex},
10690
                        scope: this,
10691
                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
10692
                    });
10693
                    this._runRenderChain();
10694
                    return;
10695
                }
10696
            }
10697
        }
10698
    }
10699
},
10700
 
10701
/**
10702
 * Convenience method to add multiple rows.
10703
 *
10704
 * @method addRows
10705
 * @param aData {Object[]} Array of object literal data for the rows.
10706
 * @param index {Number} (optional) RecordSet position index at which to add data.
10707
 */
10708
addRows : function(aData, index) {
10709
    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
10710
        return;
10711
    }
10712
 
10713
    if(lang.isArray(aData)) {
10714
        var aRecords = this._oRecordSet.addRecords(aData, index);
10715
        if(aRecords) {
10716
            var recIndex = this.getRecordIndex(aRecords[0]);
10717
 
10718
            // Paginated
10719
            var oPaginator = this.get('paginator');
10720
            if (oPaginator) {
10721
                // Update the paginator's totalRecords
10722
                var totalRecords = oPaginator.get('totalRecords');
10723
                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
10724
                    oPaginator.set('totalRecords',totalRecords + aRecords.length);
10725
                }
10726
 
10727
                var endRecIndex = (oPaginator.getPageRecords())[1];
10728
 
10729
                // At least one of the new records affects the view
10730
                if (recIndex <= endRecIndex) {
10731
                    this.render();
10732
                }
10733
 
10734
                this.fireEvent("rowsAddEvent", {records:aRecords});
10735
                return;
10736
            }
10737
            // Not paginated
10738
            else {
10739
                // Add the TR elements
10740
                var loopN = this.get("renderLoopSize");
10741
                var loopEnd = recIndex + aData.length;
10742
                var nRowsNeeded = (loopEnd - recIndex); // how many needed
10743
                var isLast = (recIndex >= this._elTbody.rows.length);
10744
                this._oChainRender.add({
10745
                    method: function(oArg) {
10746
                        if((this instanceof DT) && this._sId) {
10747
                            var aRecords = oArg.aRecords,
10748
                                i = oArg.nCurrentRow,
10749
                                j = oArg.nCurrentRecord,
10750
                                len = loopN > 0 ? Math.min(i + loopN,loopEnd) : loopEnd,
10751
                                df = document.createDocumentFragment(),
10752
                                elNext = (this._elTbody.rows[i]) ? this._elTbody.rows[i] : null;
10753
                            for(; i < len; i++, j++) {
10754
                                df.appendChild(this._addTrEl(aRecords[j]));
10755
                            }
10756
                            this._elTbody.insertBefore(df, elNext);
10757
                            oArg.nCurrentRow = i;
10758
                            oArg.nCurrentRecord = j;
10759
                        }
10760
                    },
10761
                    iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
10762
                    argument: {nCurrentRow:recIndex,nCurrentRecord:0,aRecords:aRecords},
10763
                    scope: this,
10764
                    timeout: (loopN > 0) ? 0 : -1
10765
                });
10766
                this._oChainRender.add({
10767
                    method: function(oArg) {
10768
                        var recIndex = oArg.recIndex;
10769
                        // Set FIRST/LAST
10770
                        if(recIndex === 0) {
10771
                            this._setFirstRow();
10772
                        }
10773
                        if(oArg.isLast) {
10774
                            this._setLastRow();
10775
                        }
10776
                        // Set EVEN/ODD
10777
                        this._setRowStripes();
10778
 
10779
                        this.fireEvent("rowsAddEvent", {records:aRecords});
10780
                    },
10781
                    argument: {recIndex: recIndex, isLast: isLast},
10782
                    scope: this,
10783
                    timeout: -1 // Needs to run immediately after the DOM insertions above
10784
                });
10785
                this._runRenderChain();
10786
                this.hideTableMessage();
10787
                return;
10788
            }
10789
        }
10790
    }
10791
},
10792
 
10793
/**
10794
 * For the given row, updates the associated Record with the given data. If the
10795
 * row is on current page, the corresponding DOM elements are also updated.
10796
 *
10797
 * @method updateRow
10798
 * @param row {YAHOO.widget.Record | Number | HTMLElement | String}
10799
 * Which row to update: By Record instance, by Record's RecordSet
10800
 * position index, by HTMLElement reference to the TR element, or by ID string
10801
 * of the TR element.
10802
 * @param oData {Object} Object literal of data for the row.
10803
 */
10804
updateRow : function(row, oData) {
10805
    var index = row;
10806
    if (!lang.isNumber(index)) {
10807
        index = this.getRecordIndex(row);
10808
    }
10809
 
10810
    // Update the Record
10811
    if(lang.isNumber(index) && (index >= 0)) {
10812
        var oRecordSet = this._oRecordSet,
10813
            oldRecord = oRecordSet.getRecord(index);
10814
 
10815
        if(oldRecord) {
10816
            var updatedRecord = this._oRecordSet.setRecord(oData, index),
10817
                elRow = this.getTrEl(oldRecord),
10818
                // Copy data from the Record for the event that gets fired later
10819
                oldData = oldRecord ? oldRecord.getData() : null;
10820
 
10821
            if(updatedRecord) {
10822
                // Update selected rows as necessary
10823
                var tracker = this._aSelections || [],
10824
                i=0,
10825
                oldId = oldRecord.getId(),
10826
                newId = updatedRecord.getId();
10827
                for(; i<tracker.length; i++) {
10828
                    if((tracker[i] === oldId)) {
10829
                        tracker[i] = newId;
10830
                    }
10831
                    else if(tracker[i].recordId === oldId) {
10832
                        tracker[i].recordId = newId;
10833
                    }
10834
                }
10835
 
10836
                // Update anchors as necessary
10837
                if(this._oAnchorRecord && this._oAnchorRecord.getId() === oldId) {
10838
                    this._oAnchorRecord = updatedRecord;
10839
                }
10840
                if(this._oAnchorCell && this._oAnchorCell.record.getId() === oldId) {
10841
                    this._oAnchorCell.record = updatedRecord;
10842
                }
10843
 
10844
                // Update the TR only if row is on current page
10845
                this._oChainRender.add({
10846
                    method: function() {
10847
                        if((this instanceof DT) && this._sId) {
10848
                            // Paginated
10849
                            var oPaginator = this.get('paginator');
10850
                            if (oPaginator) {
10851
                                var pageStartIndex = (oPaginator.getPageRecords())[0],
10852
                                    pageLastIndex = (oPaginator.getPageRecords())[1];
10853
 
10854
                                // At least one of the new records affects the view
10855
                                if ((index >= pageStartIndex) || (index <= pageLastIndex)) {
10856
                                    this.render();
10857
                                }
10858
                            }
10859
                            else {
10860
                                if(elRow) {
10861
                                    this._updateTrEl(elRow, updatedRecord);
10862
                                }
10863
                                else {
10864
                                    this.getTbodyEl().appendChild(this._addTrEl(updatedRecord));
10865
                                }
10866
                            }
10867
                            this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData});
10868
                        }
10869
                    },
10870
                    scope: this,
10871
                    timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
10872
                });
10873
                this._runRenderChain();
10874
                return;
10875
            }
10876
        }
10877
    }
10878
    return;
10879
},
10880
 
10881
/**
10882
 * Starting with the given row, updates associated Records with the given data.
10883
 * The number of rows to update are determined by the array of data provided.
10884
 * Undefined data (i.e., not an object literal) causes a row to be skipped. If
10885
 * any of the rows are on current page, the corresponding DOM elements are also
10886
 * updated.
10887
 *
10888
 * @method updateRows
10889
 * @param startrow {YAHOO.widget.Record | Number | HTMLElement | String}
10890
 * Starting row to update: By Record instance, by Record's RecordSet
10891
 * position index, by HTMLElement reference to the TR element, or by ID string
10892
 * of the TR element.
10893
 * @param aData {Object[]} Array of object literal of data for the rows.
10894
 */
10895
updateRows : function(startrow, aData) {
10896
    if(lang.isArray(aData)) {
10897
        var startIndex = startrow,
10898
            oRecordSet = this._oRecordSet,
10899
            lastRowIndex = oRecordSet.getLength();
10900
 
10901
        if (!lang.isNumber(startrow)) {
10902
            startIndex = this.getRecordIndex(startrow);
10903
        }
10904
 
10905
        if(lang.isNumber(startIndex) && (startIndex >= 0) && (startIndex < oRecordSet.getLength())) {
10906
            var lastIndex = startIndex + aData.length,
10907
                aOldRecords = oRecordSet.getRecords(startIndex, aData.length),
10908
                aNewRecords = oRecordSet.setRecords(aData, startIndex);
10909
            if(aNewRecords) {
10910
                var tracker = this._aSelections || [],
10911
                    i=0, j, newRecord, newId, oldId,
10912
                    anchorRecord = this._oAnchorRecord ? this._oAnchorRecord.getId() : null,
10913
                    anchorCell = this._oAnchorCell ? this._oAnchorCell.record.getId() : null;
10914
                for(; i<aOldRecords.length; i++) {
10915
                    oldId = aOldRecords[i].getId();
10916
                    newRecord = aNewRecords[i];
10917
                    newId = newRecord.getId();
10918
 
10919
                    // Update selected rows as necessary
10920
                    for(j=0; j<tracker.length; j++) {
10921
                        if((tracker[j] === oldId)) {
10922
                            tracker[j] = newId;
10923
                        }
10924
                        else if(tracker[j].recordId === oldId) {
10925
                            tracker[j].recordId = newId;
10926
                        }
10927
                    }
10928
 
10929
                    // Update anchors as necessary
10930
                    if(anchorRecord && anchorRecord === oldId) {
10931
                        this._oAnchorRecord = newRecord;
10932
                    }
10933
                    if(anchorCell && anchorCell === oldId) {
10934
                        this._oAnchorCell.record = newRecord;
10935
                    }
10936
               }
10937
 
10938
                // Paginated
10939
                var oPaginator = this.get('paginator');
10940
                if (oPaginator) {
10941
                    var pageStartIndex = (oPaginator.getPageRecords())[0],
10942
                        pageLastIndex = (oPaginator.getPageRecords())[1];
10943
 
10944
                    // At least one of the new records affects the view
10945
                    if ((startIndex >= pageStartIndex) || (lastIndex <= pageLastIndex)) {
10946
                        this.render();
10947
                    }
10948
 
10949
                    this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
10950
                    return;
10951
                }
10952
                // Not paginated
10953
                else {
10954
                    // Update the TR elements
10955
                    var loopN = this.get("renderLoopSize"),
10956
                        rowCount = aData.length, // how many needed
10957
                        isLast = (lastIndex >= lastRowIndex),
10958
                        isAdding = (lastIndex > lastRowIndex);
10959
 
10960
                    this._oChainRender.add({
10961
                        method: function(oArg) {
10962
                            if((this instanceof DT) && this._sId) {
10963
                                var aRecords = oArg.aRecords,
10964
                                    i = oArg.nCurrentRow,
10965
                                    j = oArg.nDataPointer,
10966
                                    len = loopN > 0 ? Math.min(i+loopN, startIndex+aRecords.length) : startIndex+aRecords.length;
10967
 
10968
                                for(; i < len; i++,j++) {
10969
                                    if(isAdding && (i>=lastRowIndex)) {
10970
                                        this._elTbody.appendChild(this._addTrEl(aRecords[j]));
10971
                                    }
10972
                                    else {
10973
                                        this._updateTrEl(this._elTbody.rows[i], aRecords[j]);
10974
                                    }
10975
                                }
10976
                                oArg.nCurrentRow = i;
10977
                                oArg.nDataPointer = j;
10978
                            }
10979
                        },
10980
                        iterations: (loopN > 0) ? Math.ceil(rowCount/loopN) : 1,
10981
                        argument: {nCurrentRow:startIndex,aRecords:aNewRecords,nDataPointer:0,isAdding:isAdding},
10982
                        scope: this,
10983
                        timeout: (loopN > 0) ? 0 : -1
10984
                    });
10985
                    this._oChainRender.add({
10986
                        method: function(oArg) {
10987
                            var recIndex = oArg.recIndex;
10988
                            // Set FIRST/LAST
10989
                            if(recIndex === 0) {
10990
                                this._setFirstRow();
10991
                            }
10992
                            if(oArg.isLast) {
10993
                                this._setLastRow();
10994
                            }
10995
                            // Set EVEN/ODD
10996
                            this._setRowStripes();
10997
 
10998
                            this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
10999
                        },
11000
                        argument: {recIndex: startIndex, isLast: isLast},
11001
                        scope: this,
11002
                        timeout: -1 // Needs to run immediately after the DOM insertions above
11003
                    });
11004
                    this._runRenderChain();
11005
                    this.hideTableMessage();
11006
                    return;
11007
                }
11008
            }
11009
        }
11010
    }
11011
},
11012
 
11013
/**
11014
 * Deletes the given row's Record from the RecordSet. If the row is on current page,
11015
 * the corresponding DOM elements are also deleted.
11016
 *
11017
 * @method deleteRow
11018
 * @param row {HTMLElement | String | Number} DOM element reference or ID string
11019
 * to DataTable page element or RecordSet index.
11020
 */
11021
deleteRow : function(row) {
11022
    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
11023
    if(lang.isNumber(nRecordIndex)) {
11024
        var oRecord = this.getRecord(nRecordIndex);
11025
        if(oRecord) {
11026
            var nTrIndex = this.getTrIndex(nRecordIndex);
11027
 
11028
            // Remove from selection tracker if there
11029
            var sRecordId = oRecord.getId();
11030
            var tracker = this._aSelections || [];
11031
            for(var j=tracker.length-1; j>-1; j--) {
11032
                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
11033
                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
11034
                    tracker.splice(j,1);
11035
                }
11036
            }
11037
 
11038
            // Delete Record from RecordSet
11039
            var oData = this._oRecordSet.deleteRecord(nRecordIndex);
11040
 
11041
            // Update the UI
11042
            if(oData) {
11043
                // If paginated and the deleted row was on this or a prior page, just
11044
                // re-render
11045
                var oPaginator = this.get('paginator');
11046
                if (oPaginator) {
11047
                    // Update the paginator's totalRecords
11048
                    var totalRecords = oPaginator.get('totalRecords'),
11049
                        // must capture before the totalRecords change because
11050
                        // Paginator shifts to previous page automatically
11051
                        rng = oPaginator.getPageRecords();
11052
 
11053
                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
11054
                        oPaginator.set('totalRecords',totalRecords - 1);
11055
                    }
11056
 
11057
                    // The deleted record was on this or a prior page, re-render
11058
                    if (!rng || nRecordIndex <= rng[1]) {
11059
                        this.render();
11060
                    }
11061
 
11062
                    this._oChainRender.add({
11063
                        method: function() {
11064
                            if((this instanceof DT) && this._sId) {
11065
                                this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex, oldData:oData, trElIndex:nTrIndex});
11066
                            }
11067
                        },
11068
                        scope: this,
11069
                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
11070
                    });
11071
                    this._runRenderChain();
11072
                }
11073
                // Not paginated
11074
                else {
11075
                    if(lang.isNumber(nTrIndex)) {
11076
                        this._oChainRender.add({
11077
                            method: function() {
11078
                                if((this instanceof DT) && this._sId) {
11079
                                    var isLast = (nRecordIndex === this._oRecordSet.getLength());//(nTrIndex == this.getLastTrEl().sectionRowIndex);
11080
                                    this._deleteTrEl(nTrIndex);
11081
 
11082
                                    // Post-delete tasks
11083
                                    if(this._elTbody.rows.length > 0) {
11084
                                        // Set FIRST/LAST
11085
                                        if(nTrIndex === 0) {
11086
                                            this._setFirstRow();
11087
                                        }
11088
                                        if(isLast) {
11089
                                            this._setLastRow();
11090
                                        }
11091
                                        // Set EVEN/ODD
11092
                                        if(nTrIndex != this._elTbody.rows.length) {
11093
                                            this._setRowStripes(nTrIndex);
11094
                                        }
11095
                                    }
11096
 
11097
                                    this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,oldData:oData, trElIndex:nTrIndex});
11098
                                }
11099
                            },
11100
                            scope: this,
11101
                            timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
11102
                        });
11103
                        this._runRenderChain();
11104
                        return;
11105
                    }
11106
                }
11107
            }
11108
        }
11109
    }
11110
    return null;
11111
},
11112
 
11113
/**
11114
 * Convenience method to delete multiple rows.
11115
 *
11116
 * @method deleteRows
11117
 * @param row {HTMLElement | String | Number} DOM element reference or ID string
11118
 * to DataTable page element or RecordSet index.
11119
 * @param count {Number} (optional) How many rows to delete. A negative value
11120
 * will delete towards the beginning.
11121
 */
11122
deleteRows : function(row, count) {
11123
    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
11124
    if(lang.isNumber(nRecordIndex)) {
11125
        var oRecord = this.getRecord(nRecordIndex);
11126
        if(oRecord) {
11127
            var nTrIndex = this.getTrIndex(nRecordIndex);
11128
 
11129
            // Remove from selection tracker if there
11130
            var sRecordId = oRecord.getId();
11131
            var tracker = this._aSelections || [];
11132
            for(var j=tracker.length-1; j>-1; j--) {
11133
                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
11134
                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
11135
                    tracker.splice(j,1);
11136
                }
11137
            }
11138
 
11139
            // Delete Record from RecordSet
11140
            var highIndex = nRecordIndex;
11141
            var lowIndex = nRecordIndex;
11142
 
11143
            // Validate count and account for negative value
11144
            if(count && lang.isNumber(count)) {
11145
                highIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex;
11146
                lowIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1;
11147
                count = (count > 0) ? count : count*-1;
11148
                if(lowIndex < 0) {
11149
                    lowIndex = 0;
11150
                    count = highIndex - lowIndex + 1;
11151
                }
11152
            }
11153
            else {
11154
                count = 1;
11155
            }
11156
 
11157
            var aData = this._oRecordSet.deleteRecords(lowIndex, count);
11158
 
11159
            // Update the UI
11160
            if(aData) {
11161
                var oPaginator = this.get('paginator'),
11162
                    loopN = this.get("renderLoopSize");
11163
                // If paginated and the deleted row was on this or a prior page, just
11164
                // re-render
11165
                if (oPaginator) {
11166
                    // Update the paginator's totalRecords
11167
                    var totalRecords = oPaginator.get('totalRecords'),
11168
                        // must capture before the totalRecords change because
11169
                        // Paginator shifts to previous page automatically
11170
                        rng = oPaginator.getPageRecords();
11171
 
11172
                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
11173
                        oPaginator.set('totalRecords',totalRecords - aData.length);
11174
                    }
11175
 
11176
                    // The records were on this or a prior page, re-render
11177
                    if (!rng || lowIndex <= rng[1]) {
11178
                        this.render();
11179
                    }
11180
 
11181
                    this._oChainRender.add({
11182
                        method: function(oArg) {
11183
                            if((this instanceof DT) && this._sId) {
11184
                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
11185
                            }
11186
                        },
11187
                        scope: this,
11188
                        timeout: (loopN > 0) ? 0 : -1
11189
                    });
11190
                    this._runRenderChain();
11191
                    return;
11192
                }
11193
                // Not paginated
11194
                else {
11195
                    if(lang.isNumber(nTrIndex)) {
11196
                        // Delete the TR elements starting with highest index
11197
                        var loopEnd = lowIndex;
11198
                        var nRowsNeeded = count; // how many needed
11199
                        this._oChainRender.add({
11200
                            method: function(oArg) {
11201
                                if((this instanceof DT) && this._sId) {
11202
                                    var i = oArg.nCurrentRow,
11203
                                        len = (loopN > 0) ? (Math.max(i - loopN,loopEnd)-1) : loopEnd-1;
11204
                                    for(; i>len; --i) {
11205
                                        this._deleteTrEl(i);
11206
                                    }
11207
                                    oArg.nCurrentRow = i;
11208
                                }
11209
                            },
11210
                            iterations: (loopN > 0) ? Math.ceil(count/loopN) : 1,
11211
                            argument: {nCurrentRow:highIndex},
11212
                            scope: this,
11213
                            timeout: (loopN > 0) ? 0 : -1
11214
                        });
11215
                        this._oChainRender.add({
11216
                            method: function() {
11217
                                // Post-delete tasks
11218
                                if(this._elTbody.rows.length > 0) {
11219
                                    this._setFirstRow();
11220
                                    this._setLastRow();
11221
                                    this._setRowStripes();
11222
                                }
11223
 
11224
                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
11225
                            },
11226
                            scope: this,
11227
                            timeout: -1 // Needs to run immediately after the DOM deletions above
11228
                        });
11229
                        this._runRenderChain();
11230
                        return;
11231
                    }
11232
                }
11233
            }
11234
        }
11235
    }
11236
    return null;
11237
},
11238
 
11239
 
11240
 
11241
 
11242
 
11243
 
11244
 
11245
 
11246
 
11247
 
11248
 
11249
 
11250
 
11251
 
11252
 
11253
 
11254
 
11255
 
11256
 
11257
 
11258
 
11259
 
11260
 
11261
 
11262
 
11263
 
11264
 
11265
 
11266
 
11267
 
11268
 
11269
 
11270
 
11271
 
11272
 
11273
 
11274
 
11275
 
11276
 
11277
 
11278
 
11279
 
11280
 
11281
 
11282
 
11283
 
11284
// CELL FUNCTIONS
11285
 
11286
/**
11287
 * Outputs markup into the given TD based on given Record.
11288
 *
11289
 * @method formatCell
11290
 * @param elLiner {HTMLElement} The liner DIV element within the TD.
11291
 * @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
11292
 * @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
11293
 */
11294
formatCell : function(elLiner, oRecord, oColumn) {
11295
    if(!oRecord) {
11296
        oRecord = this.getRecord(elLiner);
11297
    }
11298
    if(!oColumn) {
11299
        oColumn = this.getColumn(this.getCellIndex(elLiner.parentNode));
11300
    }
11301
 
11302
    if(oRecord && oColumn) {
11303
        var sField = oColumn.field;
11304
        var oData = oRecord.getData(sField);
11305
 
11306
        var fnFormatter = typeof oColumn.formatter === 'function' ?
11307
                          oColumn.formatter :
11308
                          DT.Formatter[oColumn.formatter+''] ||
11309
                          DT.Formatter.defaultFormatter;
11310
 
11311
        // Apply special formatter
11312
        if(fnFormatter) {
11313
            fnFormatter.call(this, elLiner, oRecord, oColumn, oData);
11314
        }
11315
        else {
11316
            elLiner.innerHTML = oData;
11317
        }
11318
 
11319
        this.fireEvent("cellFormatEvent", {record:oRecord, column:oColumn, key:oColumn.key, el:elLiner});
11320
    }
11321
    else {
11322
    }
11323
},
11324
 
11325
/**
11326
 * For the given row and column, updates the Record with the given data. If the
11327
 * cell is on current page, the corresponding DOM elements are also updated.
11328
 *
11329
 * @method updateCell
11330
 * @param oRecord {YAHOO.widget.Record} Record instance.
11331
 * @param oColumn {YAHOO.widget.Column | String | Number} A Column key, or a ColumnSet key index.
11332
 * @param oData {Object} New data value for the cell.
11333
 * @param skipRender {Boolean} Skips render step. Editors that update multiple
11334
 * cells in ScrollingDataTable should render only on the last call to updateCell().
11335
 */
11336
updateCell : function(oRecord, oColumn, oData, skipRender) {
11337
    // Validate Column and Record
11338
    oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn);
11339
    if(oColumn && oColumn.getField() && (oRecord instanceof YAHOO.widget.Record)) {
11340
        var sKey = oColumn.getField(),
11341
 
11342
        // Copy data from the Record for the event that gets fired later
11343
        //var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData());
11344
            oldData = oRecord.getData(sKey);
11345
 
11346
        // Update Record with new data
11347
        this._oRecordSet.updateRecordValue(oRecord, sKey, oData);
11348
 
11349
        // Update the TD only if row is on current page
11350
        var elTd = this.getTdEl({record: oRecord, column: oColumn});
11351
        if(elTd) {
11352
            this._oChainRender.add({
11353
                method: function() {
11354
                    if((this instanceof DT) && this._sId) {
11355
                        this.formatCell(elTd.firstChild, oRecord, oColumn);
11356
                        this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
11357
                    }
11358
                },
11359
                scope: this,
11360
                timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
11361
            });
11362
            // Bug 2529024
11363
            if(!skipRender) {
11364
                this._runRenderChain();
11365
            }
11366
        }
11367
        else {
11368
            this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
11369
        }
11370
    }
11371
},
11372
 
11373
 
11374
 
11375
 
11376
 
11377
 
11378
 
11379
 
11380
 
11381
 
11382
 
11383
 
11384
 
11385
 
11386
 
11387
 
11388
 
11389
 
11390
 
11391
 
11392
 
11393
 
11394
 
11395
 
11396
 
11397
 
11398
 
11399
 
11400
 
11401
 
11402
 
11403
 
11404
 
11405
 
11406
 
11407
 
11408
 
11409
 
11410
 
11411
 
11412
 
11413
 
11414
 
11415
 
11416
 
11417
 
11418
 
11419
 
11420
 
11421
 
11422
 
11423
// PAGINATION
11424
/**
11425
 * Method executed during set() operation for the "paginator" attribute.
11426
 * Adds and/or severs event listeners between DataTable and Paginator
11427
 *
11428
 * @method _updatePaginator
11429
 * @param newPag {Paginator} Paginator instance (or null) for DataTable to use
11430
 * @private
11431
 */
11432
_updatePaginator : function (newPag) {
11433
    var oldPag = this.get('paginator');
11434
    if (oldPag && newPag !== oldPag) {
11435
        oldPag.unsubscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
11436
    }
11437
    if (newPag) {
11438
        newPag.subscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
11439
    }
11440
},
11441
 
11442
/**
11443
 * Update the UI infrastructure in response to a "paginator" attribute change.
11444
 *
11445
 * @method _handlePaginatorChange
11446
 * @param e {Object} Change event object containing keys 'type','newValue',
11447
 *                   and 'prevValue'
11448
 * @private
11449
 */
11450
_handlePaginatorChange : function (e) {
11451
    if (e.prevValue === e.newValue) { return; }
11452
 
11453
    var newPag     = e.newValue,
11454
        oldPag     = e.prevValue,
11455
        containers = this._defaultPaginatorContainers();
11456
 
11457
    if (oldPag) {
11458
        if (oldPag.getContainerNodes()[0] == containers[0]) {
11459
            oldPag.set('containers',[]);
11460
        }
11461
        oldPag.destroy();
11462
 
11463
        // Convenience: share the default containers if possible.
11464
        // Otherwise, remove the default containers from the DOM.
11465
        if (containers[0]) {
11466
            if (newPag && !newPag.getContainerNodes().length) {
11467
                newPag.set('containers',containers);
11468
            } else {
11469
                // No new Paginator to use existing containers, OR new
11470
                // Paginator has configured containers.
11471
                for (var i = containers.length - 1; i >= 0; --i) {
11472
                    if (containers[i]) {
11473
                        containers[i].parentNode.removeChild(containers[i]);
11474
                    }
11475
                }
11476
            }
11477
        }
11478
    }
11479
 
11480
    if (!this._bInit) {
11481
        this.render();
11482
 
11483
    }
11484
 
11485
    if (newPag) {
11486
        this.renderPaginator();
11487
    }
11488
 
11489
},
11490
 
11491
/**
11492
 * Returns the default containers used for Paginators.  If create param is
11493
 * passed, the containers will be created and added to the DataTable container.
11494
 *
11495
 * @method _defaultPaginatorContainers
11496
 * @param create {boolean} Create the default containers if not found
11497
 * @private
11498
 */
11499
_defaultPaginatorContainers : function (create) {
11500
    var above_id = this._sId + '-paginator0',
11501
        below_id = this._sId + '-paginator1',
11502
        above    = Dom.get(above_id),
11503
        below    = Dom.get(below_id);
11504
 
11505
    if (create && (!above || !below)) {
11506
        // One above and one below the table
11507
        if (!above) {
11508
            above    = document.createElement('div');
11509
            above.id = above_id;
11510
            Dom.addClass(above, DT.CLASS_PAGINATOR);
11511
 
11512
            this._elContainer.insertBefore(above,this._elContainer.firstChild);
11513
        }
11514
 
11515
        if (!below) {
11516
            below    = document.createElement('div');
11517
            below.id = below_id;
11518
            Dom.addClass(below, DT.CLASS_PAGINATOR);
11519
 
11520
            this._elContainer.appendChild(below);
11521
        }
11522
    }
11523
 
11524
    return [above,below];
11525
},
11526
 
11527
/**
11528
 * Calls Paginator's destroy() method
11529
 *
11530
 * @method _destroyPaginator
11531
 * @private
11532
 */
11533
_destroyPaginator : function () {
11534
    var oldPag = this.get('paginator');
11535
    if (oldPag) {
11536
        oldPag.destroy();
11537
    }
11538
},
11539
 
11540
/**
11541
 * Renders the Paginator to the DataTable UI
11542
 *
11543
 * @method renderPaginator
11544
 */
11545
renderPaginator : function () {
11546
    var pag = this.get("paginator");
11547
    if (!pag) { return; }
11548
 
11549
    // Add the containers if the Paginator is not configured with containers
11550
    if (!pag.getContainerNodes().length) {
11551
        pag.set('containers',this._defaultPaginatorContainers(true));
11552
    }
11553
 
11554
    pag.render();
11555
},
11556
 
11557
/**
11558
 * Overridable method gives implementers a hook to show loading message before
11559
 * changing Paginator value.
11560
 *
11561
 * @method doBeforePaginatorChange
11562
 * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
11563
 * @return {Boolean} Return true to continue changing Paginator value.
11564
 */
11565
doBeforePaginatorChange : function(oPaginatorState) {
11566
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
11567
    return true;
11568
},
11569
 
11570
/**
11571
 * Responds to new Pagination states. By default, updates the UI to reflect the
11572
 * new state. If "dynamicData" is true, current selections are purged before
11573
 * a request is sent to the DataSource for data for the new state (using the
11574
 * request returned by "generateRequest()").
11575
 *
11576
 * @method onPaginatorChangeRequest
11577
 * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
11578
 */
11579
onPaginatorChangeRequest : function (oPaginatorState) {
11580
    var ok = this.doBeforePaginatorChange(oPaginatorState);
11581
    if(ok) {
11582
        // Server-side pagination
11583
        if(this.get("dynamicData")) {
11584
            // Get the current state
11585
            var oState = this.getState();
11586
 
11587
            // Update pagination values
11588
            oState.pagination = oPaginatorState;
11589
 
11590
            // Get the request for the new state
11591
            var request = this.get("generateRequest")(oState, this);
11592
 
11593
            // Purge selections
11594
            this.unselectAllRows();
11595
            this.unselectAllCells();
11596
 
11597
            // Get the new data from the server
11598
            var callback = {
11599
                success : this.onDataReturnSetRows,
11600
                failure : this.onDataReturnSetRows,
11601
                argument : oState, // Pass along the new state to the callback
11602
                scope : this
11603
            };
11604
            this._oDataSource.sendRequest(request, callback);
11605
        }
11606
        // Client-side pagination
11607
        else {
11608
            // Set the core pagination values silently (the second param)
11609
            // to avoid looping back through the changeRequest mechanism
11610
            oPaginatorState.paginator.setStartIndex(oPaginatorState.recordOffset,true);
11611
            oPaginatorState.paginator.setRowsPerPage(oPaginatorState.rowsPerPage,true);
11612
 
11613
            // Update the UI
11614
            this.render();
11615
        }
11616
    }
11617
    else {
11618
    }
11619
},
11620
 
11621
 
11622
 
11623
 
11624
 
11625
 
11626
 
11627
 
11628
 
11629
 
11630
 
11631
 
11632
 
11633
 
11634
 
11635
 
11636
 
11637
 
11638
 
11639
 
11640
 
11641
 
11642
 
11643
 
11644
 
11645
 
11646
 
11647
 
11648
 
11649
 
11650
 
11651
 
11652
 
11653
 
11654
 
11655
 
11656
 
11657
 
11658
 
11659
 
11660
 
11661
 
11662
 
11663
 
11664
 
11665
 
11666
 
11667
 
11668
 
11669
 
11670
// SELECTION/HIGHLIGHTING
11671
 
11672
/*
11673
 * Reference to last highlighted cell element
11674
 *
11675
 * @property _elLastHighlightedTd
11676
 * @type HTMLElement
11677
 * @private
11678
 */
11679
_elLastHighlightedTd : null,
11680
 
11681
/*
11682
 * ID string of last highlighted row element
11683
 *
11684
 * @property _sLastHighlightedTrElId
11685
 * @type String
11686
 * @private
11687
 */
11688
//_sLastHighlightedTrElId : null,
11689
 
11690
/**
11691
 * Array to track row selections (by sRecordId) and/or cell selections
11692
 * (by {recordId:sRecordId, columnKey:sColumnKey})
11693
 *
11694
 * @property _aSelections
11695
 * @type Object[]
11696
 * @private
11697
 */
11698
_aSelections : null,
11699
 
11700
/**
11701
 * Record instance of the row selection anchor.
11702
 *
11703
 * @property _oAnchorRecord
11704
 * @type YAHOO.widget.Record
11705
 * @private
11706
 */
11707
_oAnchorRecord : null,
11708
 
11709
/**
11710
 * Object literal representing cell selection anchor:
11711
 * {recordId:sRecordId, columnKey:sColumnKey}.
11712
 *
11713
 * @property _oAnchorCell
11714
 * @type Object
11715
 * @private
11716
 */
11717
_oAnchorCell : null,
11718
 
11719
/**
11720
 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
11721
 * from all TR elements on the page.
11722
 *
11723
 * @method _unselectAllTrEls
11724
 * @private
11725
 */
11726
_unselectAllTrEls : function() {
11727
    var selectedRows = Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
11728
    Dom.removeClass(selectedRows, DT.CLASS_SELECTED);
11729
},
11730
 
11731
/**
11732
 * Returns object literal of values that represent the selection trigger. Used
11733
 * to determine selection behavior resulting from a key event.
11734
 *
11735
 * @method _getSelectionTrigger
11736
 * @private
11737
 */
11738
_getSelectionTrigger : function() {
11739
    var sMode = this.get("selectionMode");
11740
    var oTrigger = {};
11741
    var oTriggerCell, oTriggerRecord, nTriggerRecordIndex, elTriggerRow, nTriggerTrIndex;
11742
 
11743
    // Cell mode
11744
    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
11745
        oTriggerCell = this.getLastSelectedCell();
11746
        // No selected cells found
11747
        if(!oTriggerCell) {
11748
            return null;
11749
        }
11750
        else {
11751
            oTriggerRecord = this.getRecord(oTriggerCell.recordId);
11752
            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
11753
            elTriggerRow = this.getTrEl(oTriggerRecord);
11754
            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
11755
 
11756
            // Selected cell not found on this page
11757
            if(nTriggerTrIndex === null) {
11758
                return null;
11759
            }
11760
            else {
11761
                oTrigger.record = oTriggerRecord;
11762
                oTrigger.recordIndex = nTriggerRecordIndex;
11763
                oTrigger.el = this.getTdEl(oTriggerCell);
11764
                oTrigger.trIndex = nTriggerTrIndex;
11765
                oTrigger.column = this.getColumn(oTriggerCell.columnKey);
11766
                oTrigger.colKeyIndex = oTrigger.column.getKeyIndex();
11767
                oTrigger.cell = oTriggerCell;
11768
                return oTrigger;
11769
            }
11770
        }
11771
    }
11772
    // Row mode
11773
    else {
11774
        oTriggerRecord = this.getLastSelectedRecord();
11775
        // No selected rows found
11776
        if(!oTriggerRecord) {
11777
                return null;
11778
        }
11779
        else {
11780
            // Selected row found, but is it on current page?
11781
            oTriggerRecord = this.getRecord(oTriggerRecord);
11782
            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
11783
            elTriggerRow = this.getTrEl(oTriggerRecord);
11784
            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
11785
 
11786
            // Selected row not found on this page
11787
            if(nTriggerTrIndex === null) {
11788
                return null;
11789
            }
11790
            else {
11791
                oTrigger.record = oTriggerRecord;
11792
                oTrigger.recordIndex = nTriggerRecordIndex;
11793
                oTrigger.el = elTriggerRow;
11794
                oTrigger.trIndex = nTriggerTrIndex;
11795
                return oTrigger;
11796
            }
11797
        }
11798
    }
11799
},
11800
 
11801
/**
11802
 * Returns object literal of values that represent the selection anchor. Used
11803
 * to determine selection behavior resulting from a user event.
11804
 *
11805
 * @method _getSelectionAnchor
11806
 * @param oTrigger {Object} (Optional) Object literal of selection trigger values
11807
 * (for key events).
11808
 * @private
11809
 */
11810
_getSelectionAnchor : function(oTrigger) {
11811
    var sMode = this.get("selectionMode");
11812
    var oAnchor = {};
11813
    var oAnchorRecord, nAnchorRecordIndex, nAnchorTrIndex;
11814
 
11815
    // Cell mode
11816
    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
11817
        // Validate anchor cell
11818
        var oAnchorCell = this._oAnchorCell;
11819
        if(!oAnchorCell) {
11820
            if(oTrigger) {
11821
                oAnchorCell = this._oAnchorCell = oTrigger.cell;
11822
            }
11823
            else {
11824
                return null;
11825
            }
11826
        }
11827
        oAnchorRecord = this._oAnchorCell.record;
11828
        nAnchorRecordIndex = this._oRecordSet.getRecordIndex(oAnchorRecord);
11829
        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
11830
        // If anchor cell is not on this page...
11831
        if(nAnchorTrIndex === null) {
11832
            // ...set TR index equal to top TR
11833
            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
11834
                nAnchorTrIndex = 0;
11835
            }
11836
            // ...set TR index equal to bottom TR
11837
            else {
11838
                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
11839
            }
11840
        }
11841
 
11842
        oAnchor.record = oAnchorRecord;
11843
        oAnchor.recordIndex = nAnchorRecordIndex;
11844
        oAnchor.trIndex = nAnchorTrIndex;
11845
        oAnchor.column = this._oAnchorCell.column;
11846
        oAnchor.colKeyIndex = oAnchor.column.getKeyIndex();
11847
        oAnchor.cell = oAnchorCell;
11848
        return oAnchor;
11849
    }
11850
    // Row mode
11851
    else {
11852
        oAnchorRecord = this._oAnchorRecord;
11853
        if(!oAnchorRecord) {
11854
            if(oTrigger) {
11855
                oAnchorRecord = this._oAnchorRecord = oTrigger.record;
11856
            }
11857
            else {
11858
                return null;
11859
            }
11860
        }
11861
 
11862
        nAnchorRecordIndex = this.getRecordIndex(oAnchorRecord);
11863
        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
11864
        // If anchor row is not on this page...
11865
        if(nAnchorTrIndex === null) {
11866
            // ...set TR index equal to top TR
11867
            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
11868
                nAnchorTrIndex = 0;
11869
            }
11870
            // ...set TR index equal to bottom TR
11871
            else {
11872
                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
11873
            }
11874
        }
11875
 
11876
        oAnchor.record = oAnchorRecord;
11877
        oAnchor.recordIndex = nAnchorRecordIndex;
11878
        oAnchor.trIndex = nAnchorTrIndex;
11879
        return oAnchor;
11880
    }
11881
},
11882
 
11883
/**
11884
 * Determines selection behavior resulting from a mouse event when selection mode
11885
 * is set to "standard".
11886
 *
11887
 * @method _handleStandardSelectionByMouse
11888
 * @param oArgs.event {HTMLEvent} Event object.
11889
 * @param oArgs.target {HTMLElement} Target element.
11890
 * @private
11891
 */
11892
_handleStandardSelectionByMouse : function(oArgs) {
11893
    var elTarget = oArgs.target;
11894
 
11895
    // Validate target row
11896
    var elTargetRow = this.getTrEl(elTarget);
11897
    if(elTargetRow) {
11898
        var e = oArgs.event;
11899
        var bSHIFT = e.shiftKey;
11900
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
11901
 
11902
        var oTargetRecord = this.getRecord(elTargetRow);
11903
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
11904
 
11905
        var oAnchor = this._getSelectionAnchor();
11906
 
11907
        var i;
11908
 
11909
        // Both SHIFT and CTRL
11910
        if(bSHIFT && bCTRL) {
11911
            // Validate anchor
11912
            if(oAnchor) {
11913
                if(this.isSelected(oAnchor.record)) {
11914
                    // Select all rows between anchor row and target row, including target row
11915
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
11916
                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex; i++) {
11917
                            if(!this.isSelected(i)) {
11918
                                this.selectRow(i);
11919
                            }
11920
                        }
11921
                    }
11922
                    // Select all rows between target row and anchor row, including target row
11923
                    else {
11924
                        for(i=oAnchor.recordIndex-1; i>=nTargetRecordIndex; i--) {
11925
                            if(!this.isSelected(i)) {
11926
                                this.selectRow(i);
11927
                            }
11928
                        }
11929
                    }
11930
                }
11931
                else {
11932
                    // Unselect all rows between anchor row and target row
11933
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
11934
                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex-1; i++) {
11935
                            if(this.isSelected(i)) {
11936
                                this.unselectRow(i);
11937
                            }
11938
                        }
11939
                    }
11940
                    // Unselect all rows between target row and anchor row
11941
                    else {
11942
                        for(i=nTargetRecordIndex+1; i<=oAnchor.recordIndex-1; i++) {
11943
                            if(this.isSelected(i)) {
11944
                                this.unselectRow(i);
11945
                            }
11946
                        }
11947
                    }
11948
                    // Select the target row
11949
                    this.selectRow(oTargetRecord);
11950
                }
11951
            }
11952
            // Invalid anchor
11953
            else {
11954
                // Set anchor
11955
                this._oAnchorRecord = oTargetRecord;
11956
 
11957
                // Toggle selection of target
11958
                if(this.isSelected(oTargetRecord)) {
11959
                    this.unselectRow(oTargetRecord);
11960
                }
11961
                else {
11962
                    this.selectRow(oTargetRecord);
11963
                }
11964
            }
11965
        }
11966
         // Only SHIFT
11967
        else if(bSHIFT) {
11968
            this.unselectAllRows();
11969
 
11970
            // Validate anchor
11971
            if(oAnchor) {
11972
                // Select all rows between anchor row and target row,
11973
                // including the anchor row and target row
11974
                if(oAnchor.recordIndex < nTargetRecordIndex) {
11975
                    for(i=oAnchor.recordIndex; i<=nTargetRecordIndex; i++) {
11976
                        this.selectRow(i);
11977
                    }
11978
                }
11979
                // Select all rows between target row and anchor row,
11980
                // including the target row and anchor row
11981
                else {
11982
                    for(i=oAnchor.recordIndex; i>=nTargetRecordIndex; i--) {
11983
                        this.selectRow(i);
11984
                    }
11985
                }
11986
            }
11987
            // Invalid anchor
11988
            else {
11989
                // Set anchor
11990
                this._oAnchorRecord = oTargetRecord;
11991
 
11992
                // Select target row only
11993
                this.selectRow(oTargetRecord);
11994
            }
11995
        }
11996
        // Only CTRL
11997
        else if(bCTRL) {
11998
            // Set anchor
11999
            this._oAnchorRecord = oTargetRecord;
12000
 
12001
            // Toggle selection of target
12002
            if(this.isSelected(oTargetRecord)) {
12003
                this.unselectRow(oTargetRecord);
12004
            }
12005
            else {
12006
                this.selectRow(oTargetRecord);
12007
            }
12008
        }
12009
        // Neither SHIFT nor CTRL
12010
        else {
12011
            this._handleSingleSelectionByMouse(oArgs);
12012
            return;
12013
        }
12014
    }
12015
},
12016
 
12017
/**
12018
 * Determines selection behavior resulting from a key event when selection mode
12019
 * is set to "standard".
12020
 *
12021
 * @method _handleStandardSelectionByKey
12022
 * @param e {HTMLEvent} Event object.
12023
 * @private
12024
 */
12025
_handleStandardSelectionByKey : function(e) {
12026
    var nKey = Ev.getCharCode(e);
12027
 
12028
    if((nKey == 38) || (nKey == 40)) {
12029
        var bSHIFT = e.shiftKey;
12030
 
12031
        // Validate trigger
12032
        var oTrigger = this._getSelectionTrigger();
12033
        // Arrow selection only works if last selected row is on current page
12034
        if(!oTrigger) {
12035
            return null;
12036
        }
12037
 
12038
        Ev.stopEvent(e);
12039
 
12040
        // Validate anchor
12041
        var oAnchor = this._getSelectionAnchor(oTrigger);
12042
 
12043
        // Determine which direction we're going to
12044
        if(bSHIFT) {
12045
            // Selecting down away from anchor row
12046
            if((nKey == 40) && (oAnchor.recordIndex <= oTrigger.trIndex)) {
12047
                this.selectRow(this.getNextTrEl(oTrigger.el));
12048
            }
12049
            // Selecting up away from anchor row
12050
            else if((nKey == 38) && (oAnchor.recordIndex >= oTrigger.trIndex)) {
12051
                this.selectRow(this.getPreviousTrEl(oTrigger.el));
12052
            }
12053
            // Unselect trigger
12054
            else {
12055
                this.unselectRow(oTrigger.el);
12056
            }
12057
        }
12058
        else {
12059
            this._handleSingleSelectionByKey(e);
12060
        }
12061
    }
12062
},
12063
 
12064
/**
12065
 * Determines selection behavior resulting from a mouse event when selection mode
12066
 * is set to "single".
12067
 *
12068
 * @method _handleSingleSelectionByMouse
12069
 * @param oArgs.event {HTMLEvent} Event object.
12070
 * @param oArgs.target {HTMLElement} Target element.
12071
 * @private
12072
 */
12073
_handleSingleSelectionByMouse : function(oArgs) {
12074
    var elTarget = oArgs.target;
12075
 
12076
    // Validate target row
12077
    var elTargetRow = this.getTrEl(elTarget);
12078
    if(elTargetRow) {
12079
        var oTargetRecord = this.getRecord(elTargetRow);
12080
 
12081
        // Set anchor
12082
        this._oAnchorRecord = oTargetRecord;
12083
 
12084
        // Select only target
12085
        this.unselectAllRows();
12086
        this.selectRow(oTargetRecord);
12087
    }
12088
},
12089
 
12090
/**
12091
 * Determines selection behavior resulting from a key event when selection mode
12092
 * is set to "single".
12093
 *
12094
 * @method _handleSingleSelectionByKey
12095
 * @param e {HTMLEvent} Event object.
12096
 * @private
12097
 */
12098
_handleSingleSelectionByKey : function(e) {
12099
    var nKey = Ev.getCharCode(e);
12100
 
12101
    if((nKey == 38) || (nKey == 40)) {
12102
        // Validate trigger
12103
        var oTrigger = this._getSelectionTrigger();
12104
        // Arrow selection only works if last selected row is on current page
12105
        if(!oTrigger) {
12106
            return null;
12107
        }
12108
 
12109
        Ev.stopEvent(e);
12110
 
12111
        // Determine the new row to select
12112
        var elNew;
12113
        if(nKey == 38) { // arrow up
12114
            elNew = this.getPreviousTrEl(oTrigger.el);
12115
 
12116
            // Validate new row
12117
            if(elNew === null) {
12118
                //TODO: wrap around to last tr on current page
12119
                //elNew = this.getLastTrEl();
12120
 
12121
                //TODO: wrap back to last tr of previous page
12122
 
12123
                // Top row selection is sticky
12124
                elNew = this.getFirstTrEl();
12125
            }
12126
        }
12127
        else if(nKey == 40) { // arrow down
12128
            elNew = this.getNextTrEl(oTrigger.el);
12129
 
12130
            // Validate new row
12131
            if(elNew === null) {
12132
                //TODO: wrap around to first tr on current page
12133
                //elNew = this.getFirstTrEl();
12134
 
12135
                //TODO: wrap forward to first tr of previous page
12136
 
12137
                // Bottom row selection is sticky
12138
                elNew = this.getLastTrEl();
12139
            }
12140
        }
12141
 
12142
        // Unselect all rows
12143
        this.unselectAllRows();
12144
 
12145
        // Select the new row
12146
        this.selectRow(elNew);
12147
 
12148
        // Set new anchor
12149
        this._oAnchorRecord = this.getRecord(elNew);
12150
    }
12151
},
12152
 
12153
/**
12154
 * Determines selection behavior resulting from a mouse event when selection mode
12155
 * is set to "cellblock".
12156
 *
12157
 * @method _handleCellBlockSelectionByMouse
12158
 * @param oArgs.event {HTMLEvent} Event object.
12159
 * @param oArgs.target {HTMLElement} Target element.
12160
 * @private
12161
 */
12162
_handleCellBlockSelectionByMouse : function(oArgs) {
12163
    var elTarget = oArgs.target;
12164
 
12165
    // Validate target cell
12166
    var elTargetCell = this.getTdEl(elTarget);
12167
    if(elTargetCell) {
12168
        var e = oArgs.event;
12169
        var bSHIFT = e.shiftKey;
12170
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
12171
 
12172
        var elTargetRow = this.getTrEl(elTargetCell);
12173
        var nTargetTrIndex = this.getTrIndex(elTargetRow);
12174
        var oTargetColumn = this.getColumn(elTargetCell);
12175
        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
12176
        var oTargetRecord = this.getRecord(elTargetRow);
12177
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
12178
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
12179
 
12180
        var oAnchor = this._getSelectionAnchor();
12181
 
12182
        var allRows = this.getTbodyEl().rows;
12183
        var startIndex, endIndex, currentRow, i, j;
12184
 
12185
        // Both SHIFT and CTRL
12186
        if(bSHIFT && bCTRL) {
12187
 
12188
            // Validate anchor
12189
            if(oAnchor) {
12190
                // Anchor is selected
12191
                if(this.isSelected(oAnchor.cell)) {
12192
                    // All cells are on the same row
12193
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12194
                        // Select all cells between anchor cell and target cell, including target cell
12195
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12196
                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
12197
                                this.selectCell(elTargetRow.cells[i]);
12198
                            }
12199
                        }
12200
                        // Select all cells between target cell and anchor cell, including target cell
12201
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12202
                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
12203
                                this.selectCell(elTargetRow.cells[i]);
12204
                            }
12205
                        }
12206
                    }
12207
                    // Anchor row is above target row
12208
                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
12209
                        startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
12210
                        endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
12211
 
12212
                        // Select all cells from startIndex to endIndex on rows between anchor row and target row
12213
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12214
                            for(j=startIndex; j<=endIndex; j++) {
12215
                                this.selectCell(allRows[i].cells[j]);
12216
                            }
12217
                        }
12218
                    }
12219
                    // Anchor row is below target row
12220
                    else {
12221
                        startIndex = Math.min(oAnchor.trIndex, nTargetColKeyIndex);
12222
                        endIndex = Math.max(oAnchor.trIndex, nTargetColKeyIndex);
12223
 
12224
                        // Select all cells from startIndex to endIndex on rows between target row and anchor row
12225
                        for(i=oAnchor.trIndex; i>=nTargetTrIndex; i--) {
12226
                            for(j=endIndex; j>=startIndex; j--) {
12227
                                this.selectCell(allRows[i].cells[j]);
12228
                            }
12229
                        }
12230
                    }
12231
                }
12232
                // Anchor cell is unselected
12233
                else {
12234
                    // All cells are on the same row
12235
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12236
                        // Unselect all cells between anchor cell and target cell
12237
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12238
                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
12239
                                this.unselectCell(elTargetRow.cells[i]);
12240
                            }
12241
                        }
12242
                        // Select all cells between target cell and anchor cell
12243
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12244
                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
12245
                                this.unselectCell(elTargetRow.cells[i]);
12246
                            }
12247
                        }
12248
                    }
12249
                    // Anchor row is above target row
12250
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
12251
                        // Unselect all cells from anchor cell to target cell
12252
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12253
                            currentRow = allRows[i];
12254
                            for(j=0; j<currentRow.cells.length; j++) {
12255
                                // This is the anchor row, only unselect cells after the anchor cell
12256
                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
12257
                                    if(j>oAnchor.colKeyIndex) {
12258
                                        this.unselectCell(currentRow.cells[j]);
12259
                                    }
12260
                                }
12261
                                // This is the target row, only unelect cells before the target cell
12262
                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
12263
                                    if(j<nTargetColKeyIndex) {
12264
                                        this.unselectCell(currentRow.cells[j]);
12265
                                    }
12266
                                }
12267
                                // Unselect all cells on this row
12268
                                else {
12269
                                    this.unselectCell(currentRow.cells[j]);
12270
                                }
12271
                            }
12272
                        }
12273
                    }
12274
                    // Anchor row is below target row
12275
                    else {
12276
                        // Unselect all cells from target cell to anchor cell
12277
                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12278
                            currentRow = allRows[i];
12279
                            for(j=0; j<currentRow.cells.length; j++) {
12280
                                // This is the target row, only unselect cells after the target cell
12281
                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
12282
                                    if(j>nTargetColKeyIndex) {
12283
                                        this.unselectCell(currentRow.cells[j]);
12284
                                    }
12285
                                }
12286
                                // This is the anchor row, only unselect cells before the anchor cell
12287
                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
12288
                                    if(j<oAnchor.colKeyIndex) {
12289
                                        this.unselectCell(currentRow.cells[j]);
12290
                                    }
12291
                                }
12292
                                // Unselect all cells on this row
12293
                                else {
12294
                                    this.unselectCell(currentRow.cells[j]);
12295
                                }
12296
                            }
12297
                        }
12298
                    }
12299
 
12300
                    // Select the target cell
12301
                    this.selectCell(elTargetCell);
12302
                }
12303
            }
12304
            // Invalid anchor
12305
            else {
12306
                // Set anchor
12307
                this._oAnchorCell = oTargetCell;
12308
 
12309
                // Toggle selection of target
12310
                if(this.isSelected(oTargetCell)) {
12311
                    this.unselectCell(oTargetCell);
12312
                }
12313
                else {
12314
                    this.selectCell(oTargetCell);
12315
                }
12316
            }
12317
 
12318
        }
12319
         // Only SHIFT
12320
        else if(bSHIFT) {
12321
            this.unselectAllCells();
12322
 
12323
            // Validate anchor
12324
            if(oAnchor) {
12325
                // All cells are on the same row
12326
                if(oAnchor.recordIndex === nTargetRecordIndex) {
12327
                    // Select all cells between anchor cell and target cell,
12328
                    // including the anchor cell and target cell
12329
                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12330
                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
12331
                            this.selectCell(elTargetRow.cells[i]);
12332
                        }
12333
                    }
12334
                    // Select all cells between target cell and anchor cell
12335
                    // including the target cell and anchor cell
12336
                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12337
                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
12338
                            this.selectCell(elTargetRow.cells[i]);
12339
                        }
12340
                    }
12341
                }
12342
                // Anchor row is above target row
12343
                else if(oAnchor.recordIndex < nTargetRecordIndex) {
12344
                    // Select the cellblock from anchor cell to target cell
12345
                    // including the anchor cell and the target cell
12346
                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
12347
                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
12348
 
12349
                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12350
                        for(j=startIndex; j<=endIndex; j++) {
12351
                            this.selectCell(allRows[i].cells[j]);
12352
                        }
12353
                    }
12354
                }
12355
                // Anchor row is below target row
12356
                else {
12357
                    // Select the cellblock from target cell to anchor cell
12358
                    // including the target cell and the anchor cell
12359
                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
12360
                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
12361
 
12362
                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12363
                        for(j=startIndex; j<=endIndex; j++) {
12364
                            this.selectCell(allRows[i].cells[j]);
12365
                        }
12366
                    }
12367
                }
12368
            }
12369
            // Invalid anchor
12370
            else {
12371
                // Set anchor
12372
                this._oAnchorCell = oTargetCell;
12373
 
12374
                // Select target only
12375
                this.selectCell(oTargetCell);
12376
            }
12377
        }
12378
        // Only CTRL
12379
        else if(bCTRL) {
12380
 
12381
            // Set anchor
12382
            this._oAnchorCell = oTargetCell;
12383
 
12384
            // Toggle selection of target
12385
            if(this.isSelected(oTargetCell)) {
12386
                this.unselectCell(oTargetCell);
12387
            }
12388
            else {
12389
                this.selectCell(oTargetCell);
12390
            }
12391
 
12392
        }
12393
        // Neither SHIFT nor CTRL
12394
        else {
12395
            this._handleSingleCellSelectionByMouse(oArgs);
12396
        }
12397
    }
12398
},
12399
 
12400
/**
12401
 * Determines selection behavior resulting from a key event when selection mode
12402
 * is set to "cellblock".
12403
 *
12404
 * @method _handleCellBlockSelectionByKey
12405
 * @param e {HTMLEvent} Event object.
12406
 * @private
12407
 */
12408
_handleCellBlockSelectionByKey : function(e) {
12409
    var nKey = Ev.getCharCode(e);
12410
    var bSHIFT = e.shiftKey;
12411
    if((nKey == 9) || !bSHIFT) {
12412
        this._handleSingleCellSelectionByKey(e);
12413
        return;
12414
    }
12415
 
12416
    if((nKey > 36) && (nKey < 41)) {
12417
        // Validate trigger
12418
        var oTrigger = this._getSelectionTrigger();
12419
        // Arrow selection only works if last selected row is on current page
12420
        if(!oTrigger) {
12421
            return null;
12422
        }
12423
 
12424
        Ev.stopEvent(e);
12425
 
12426
        // Validate anchor
12427
        var oAnchor = this._getSelectionAnchor(oTrigger);
12428
 
12429
        var i, startIndex, endIndex, elNew, elNewRow;
12430
        var allRows = this.getTbodyEl().rows;
12431
        var elThisRow = oTrigger.el.parentNode;
12432
 
12433
        // Determine which direction we're going to
12434
 
12435
        if(nKey == 40) { // arrow down
12436
            // Selecting away from anchor cell
12437
            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
12438
                // Select the horiz block on the next row...
12439
                // ...making sure there is room below the trigger row
12440
                elNewRow = this.getNextTrEl(oTrigger.el);
12441
                if(elNewRow) {
12442
                    startIndex = oAnchor.colKeyIndex;
12443
                    endIndex = oTrigger.colKeyIndex;
12444
                    // ...going left
12445
                    if(startIndex > endIndex) {
12446
                        for(i=startIndex; i>=endIndex; i--) {
12447
                            elNew = elNewRow.cells[i];
12448
                            this.selectCell(elNew);
12449
                        }
12450
                    }
12451
                    // ... going right
12452
                    else {
12453
                        for(i=startIndex; i<=endIndex; i++) {
12454
                            elNew = elNewRow.cells[i];
12455
                            this.selectCell(elNew);
12456
                        }
12457
                    }
12458
                }
12459
            }
12460
            // Unselecting towards anchor cell
12461
            else {
12462
                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12463
                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12464
                // Unselect the horiz block on this row towards the next row
12465
                for(i=startIndex; i<=endIndex; i++) {
12466
                    this.unselectCell(elThisRow.cells[i]);
12467
                }
12468
            }
12469
        }
12470
        // Arrow up
12471
        else if(nKey == 38) {
12472
            // Selecting away from anchor cell
12473
            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
12474
                // Select the horiz block on the previous row...
12475
                // ...making sure there is room
12476
                elNewRow = this.getPreviousTrEl(oTrigger.el);
12477
                if(elNewRow) {
12478
                    // Select in order from anchor to trigger...
12479
                    startIndex = oAnchor.colKeyIndex;
12480
                    endIndex = oTrigger.colKeyIndex;
12481
                    // ...going left
12482
                    if(startIndex > endIndex) {
12483
                        for(i=startIndex; i>=endIndex; i--) {
12484
                            elNew = elNewRow.cells[i];
12485
                            this.selectCell(elNew);
12486
                        }
12487
                    }
12488
                    // ... going right
12489
                    else {
12490
                        for(i=startIndex; i<=endIndex; i++) {
12491
                            elNew = elNewRow.cells[i];
12492
                            this.selectCell(elNew);
12493
                        }
12494
                    }
12495
                }
12496
            }
12497
            // Unselecting towards anchor cell
12498
            else {
12499
                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12500
                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
12501
                // Unselect the horiz block on this row towards the previous row
12502
                for(i=startIndex; i<=endIndex; i++) {
12503
                    this.unselectCell(elThisRow.cells[i]);
12504
                }
12505
            }
12506
        }
12507
        // Arrow right
12508
        else if(nKey == 39) {
12509
            // Selecting away from anchor cell
12510
            if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
12511
                // Select the next vert block to the right...
12512
                // ...making sure there is room
12513
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
12514
                    // Select in order from anchor to trigger...
12515
                    startIndex = oAnchor.trIndex;
12516
                    endIndex = oTrigger.trIndex;
12517
                    // ...going up
12518
                    if(startIndex > endIndex) {
12519
                        for(i=startIndex; i>=endIndex; i--) {
12520
                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
12521
                            this.selectCell(elNew);
12522
                        }
12523
                    }
12524
                    // ... going down
12525
                    else {
12526
                        for(i=startIndex; i<=endIndex; i++) {
12527
                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
12528
                            this.selectCell(elNew);
12529
                        }
12530
                    }
12531
                }
12532
            }
12533
            // Unselecting towards anchor cell
12534
            else {
12535
                // Unselect the vert block on this column towards the right
12536
                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
12537
                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
12538
                for(i=startIndex; i<=endIndex; i++) {
12539
                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
12540
                }
12541
            }
12542
        }
12543
        // Arrow left
12544
        else if(nKey == 37) {
12545
            // Selecting away from anchor cell
12546
            if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
12547
                //Select the previous vert block to the left
12548
                if(oTrigger.colKeyIndex > 0) {
12549
                    // Select in order from anchor to trigger...
12550
                    startIndex = oAnchor.trIndex;
12551
                    endIndex = oTrigger.trIndex;
12552
                    // ...going up
12553
                    if(startIndex > endIndex) {
12554
                        for(i=startIndex; i>=endIndex; i--) {
12555
                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
12556
                            this.selectCell(elNew);
12557
                        }
12558
                    }
12559
                    // ... going down
12560
                    else {
12561
                        for(i=startIndex; i<=endIndex; i++) {
12562
                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
12563
                            this.selectCell(elNew);
12564
                        }
12565
                    }
12566
                }
12567
            }
12568
            // Unselecting towards anchor cell
12569
            else {
12570
                // Unselect the vert block on this column towards the left
12571
                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
12572
                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
12573
                for(i=startIndex; i<=endIndex; i++) {
12574
                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
12575
                }
12576
            }
12577
        }
12578
    }
12579
},
12580
 
12581
/**
12582
 * Determines selection behavior resulting from a mouse event when selection mode
12583
 * is set to "cellrange".
12584
 *
12585
 * @method _handleCellRangeSelectionByMouse
12586
 * @param oArgs.event {HTMLEvent} Event object.
12587
 * @param oArgs.target {HTMLElement} Target element.
12588
 * @private
12589
 */
12590
_handleCellRangeSelectionByMouse : function(oArgs) {
12591
    var elTarget = oArgs.target;
12592
 
12593
    // Validate target cell
12594
    var elTargetCell = this.getTdEl(elTarget);
12595
    if(elTargetCell) {
12596
        var e = oArgs.event;
12597
        var bSHIFT = e.shiftKey;
12598
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
12599
 
12600
        var elTargetRow = this.getTrEl(elTargetCell);
12601
        var nTargetTrIndex = this.getTrIndex(elTargetRow);
12602
        var oTargetColumn = this.getColumn(elTargetCell);
12603
        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
12604
        var oTargetRecord = this.getRecord(elTargetRow);
12605
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
12606
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
12607
 
12608
        var oAnchor = this._getSelectionAnchor();
12609
 
12610
        var allRows = this.getTbodyEl().rows;
12611
        var currentRow, i, j;
12612
 
12613
        // Both SHIFT and CTRL
12614
        if(bSHIFT && bCTRL) {
12615
 
12616
            // Validate anchor
12617
            if(oAnchor) {
12618
                // Anchor is selected
12619
                if(this.isSelected(oAnchor.cell)) {
12620
                    // All cells are on the same row
12621
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12622
                        // Select all cells between anchor cell and target cell, including target cell
12623
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12624
                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
12625
                                this.selectCell(elTargetRow.cells[i]);
12626
                            }
12627
                        }
12628
                        // Select all cells between target cell and anchor cell, including target cell
12629
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12630
                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
12631
                                this.selectCell(elTargetRow.cells[i]);
12632
                            }
12633
                        }
12634
                    }
12635
                    // Anchor row is above target row
12636
                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
12637
                        // Select all cells on anchor row from anchor cell to the end of the row
12638
                        for(i=oAnchor.colKeyIndex+1; i<elTargetRow.cells.length; i++) {
12639
                            this.selectCell(elTargetRow.cells[i]);
12640
                        }
12641
 
12642
                        // Select all cells on all rows between anchor row and target row
12643
                        for(i=oAnchor.trIndex+1; i<nTargetTrIndex; i++) {
12644
                            for(j=0; j<allRows[i].cells.length; j++){
12645
                                this.selectCell(allRows[i].cells[j]);
12646
                            }
12647
                        }
12648
 
12649
                        // Select all cells on target row from first cell to the target cell
12650
                        for(i=0; i<=nTargetColKeyIndex; i++) {
12651
                            this.selectCell(elTargetRow.cells[i]);
12652
                        }
12653
                    }
12654
                    // Anchor row is below target row
12655
                    else {
12656
                        // Select all cells on target row from target cell to the end of the row
12657
                        for(i=nTargetColKeyIndex; i<elTargetRow.cells.length; i++) {
12658
                            this.selectCell(elTargetRow.cells[i]);
12659
                        }
12660
 
12661
                        // Select all cells on all rows between target row and anchor row
12662
                        for(i=nTargetTrIndex+1; i<oAnchor.trIndex; i++) {
12663
                            for(j=0; j<allRows[i].cells.length; j++){
12664
                                this.selectCell(allRows[i].cells[j]);
12665
                            }
12666
                        }
12667
 
12668
                        // Select all cells on anchor row from first cell to the anchor cell
12669
                        for(i=0; i<oAnchor.colKeyIndex; i++) {
12670
                            this.selectCell(elTargetRow.cells[i]);
12671
                        }
12672
                    }
12673
                }
12674
                // Anchor cell is unselected
12675
                else {
12676
                    // All cells are on the same row
12677
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
12678
                        // Unselect all cells between anchor cell and target cell
12679
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12680
                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
12681
                                this.unselectCell(elTargetRow.cells[i]);
12682
                            }
12683
                        }
12684
                        // Select all cells between target cell and anchor cell
12685
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12686
                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
12687
                                this.unselectCell(elTargetRow.cells[i]);
12688
                            }
12689
                        }
12690
                    }
12691
                    // Anchor row is above target row
12692
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
12693
                        // Unselect all cells from anchor cell to target cell
12694
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12695
                            currentRow = allRows[i];
12696
                            for(j=0; j<currentRow.cells.length; j++) {
12697
                                // This is the anchor row, only unselect cells after the anchor cell
12698
                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
12699
                                    if(j>oAnchor.colKeyIndex) {
12700
                                        this.unselectCell(currentRow.cells[j]);
12701
                                    }
12702
                                }
12703
                                // This is the target row, only unelect cells before the target cell
12704
                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
12705
                                    if(j<nTargetColKeyIndex) {
12706
                                        this.unselectCell(currentRow.cells[j]);
12707
                                    }
12708
                                }
12709
                                // Unselect all cells on this row
12710
                                else {
12711
                                    this.unselectCell(currentRow.cells[j]);
12712
                                }
12713
                            }
12714
                        }
12715
                    }
12716
                    // Anchor row is below target row
12717
                    else {
12718
                        // Unselect all cells from target cell to anchor cell
12719
                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12720
                            currentRow = allRows[i];
12721
                            for(j=0; j<currentRow.cells.length; j++) {
12722
                                // This is the target row, only unselect cells after the target cell
12723
                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
12724
                                    if(j>nTargetColKeyIndex) {
12725
                                        this.unselectCell(currentRow.cells[j]);
12726
                                    }
12727
                                }
12728
                                // This is the anchor row, only unselect cells before the anchor cell
12729
                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
12730
                                    if(j<oAnchor.colKeyIndex) {
12731
                                        this.unselectCell(currentRow.cells[j]);
12732
                                    }
12733
                                }
12734
                                // Unselect all cells on this row
12735
                                else {
12736
                                    this.unselectCell(currentRow.cells[j]);
12737
                                }
12738
                            }
12739
                        }
12740
                    }
12741
 
12742
                    // Select the target cell
12743
                    this.selectCell(elTargetCell);
12744
                }
12745
            }
12746
            // Invalid anchor
12747
            else {
12748
                // Set anchor
12749
                this._oAnchorCell = oTargetCell;
12750
 
12751
                // Toggle selection of target
12752
                if(this.isSelected(oTargetCell)) {
12753
                    this.unselectCell(oTargetCell);
12754
                }
12755
                else {
12756
                    this.selectCell(oTargetCell);
12757
                }
12758
            }
12759
        }
12760
         // Only SHIFT
12761
        else if(bSHIFT) {
12762
 
12763
            this.unselectAllCells();
12764
 
12765
            // Validate anchor
12766
            if(oAnchor) {
12767
                // All cells are on the same row
12768
                if(oAnchor.recordIndex === nTargetRecordIndex) {
12769
                    // Select all cells between anchor cell and target cell,
12770
                    // including the anchor cell and target cell
12771
                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
12772
                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
12773
                            this.selectCell(elTargetRow.cells[i]);
12774
                        }
12775
                    }
12776
                    // Select all cells between target cell and anchor cell
12777
                    // including the target cell and anchor cell
12778
                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
12779
                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
12780
                            this.selectCell(elTargetRow.cells[i]);
12781
                        }
12782
                    }
12783
                }
12784
                // Anchor row is above target row
12785
                else if(oAnchor.recordIndex < nTargetRecordIndex) {
12786
                    // Select all cells from anchor cell to target cell
12787
                    // including the anchor cell and target cell
12788
                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
12789
                        currentRow = allRows[i];
12790
                        for(j=0; j<currentRow.cells.length; j++) {
12791
                            // This is the anchor row, only select the anchor cell and after
12792
                            if(currentRow.sectionRowIndex == oAnchor.trIndex) {
12793
                                if(j>=oAnchor.colKeyIndex) {
12794
                                    this.selectCell(currentRow.cells[j]);
12795
                                }
12796
                            }
12797
                            // This is the target row, only select the target cell and before
12798
                            else if(currentRow.sectionRowIndex == nTargetTrIndex) {
12799
                                if(j<=nTargetColKeyIndex) {
12800
                                    this.selectCell(currentRow.cells[j]);
12801
                                }
12802
                            }
12803
                            // Select all cells on this row
12804
                            else {
12805
                                this.selectCell(currentRow.cells[j]);
12806
                            }
12807
                        }
12808
                    }
12809
                }
12810
                // Anchor row is below target row
12811
                else {
12812
                    // Select all cells from target cell to anchor cell,
12813
                    // including the target cell and anchor cell
12814
                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
12815
                        currentRow = allRows[i];
12816
                        for(j=0; j<currentRow.cells.length; j++) {
12817
                            // This is the target row, only select the target cell and after
12818
                            if(currentRow.sectionRowIndex == nTargetTrIndex) {
12819
                                if(j>=nTargetColKeyIndex) {
12820
                                    this.selectCell(currentRow.cells[j]);
12821
                                }
12822
                            }
12823
                            // This is the anchor row, only select the anchor cell and before
12824
                            else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
12825
                                if(j<=oAnchor.colKeyIndex) {
12826
                                    this.selectCell(currentRow.cells[j]);
12827
                                }
12828
                            }
12829
                            // Select all cells on this row
12830
                            else {
12831
                                this.selectCell(currentRow.cells[j]);
12832
                            }
12833
                        }
12834
                    }
12835
                }
12836
            }
12837
            // Invalid anchor
12838
            else {
12839
                // Set anchor
12840
                this._oAnchorCell = oTargetCell;
12841
 
12842
                // Select target only
12843
                this.selectCell(oTargetCell);
12844
            }
12845
 
12846
 
12847
        }
12848
        // Only CTRL
12849
        else if(bCTRL) {
12850
 
12851
            // Set anchor
12852
            this._oAnchorCell = oTargetCell;
12853
 
12854
            // Toggle selection of target
12855
            if(this.isSelected(oTargetCell)) {
12856
                this.unselectCell(oTargetCell);
12857
            }
12858
            else {
12859
                this.selectCell(oTargetCell);
12860
            }
12861
 
12862
        }
12863
        // Neither SHIFT nor CTRL
12864
        else {
12865
            this._handleSingleCellSelectionByMouse(oArgs);
12866
        }
12867
    }
12868
},
12869
 
12870
/**
12871
 * Determines selection behavior resulting from a key event when selection mode
12872
 * is set to "cellrange".
12873
 *
12874
 * @method _handleCellRangeSelectionByKey
12875
 * @param e {HTMLEvent} Event object.
12876
 * @private
12877
 */
12878
_handleCellRangeSelectionByKey : function(e) {
12879
    var nKey = Ev.getCharCode(e);
12880
    var bSHIFT = e.shiftKey;
12881
    if((nKey == 9) || !bSHIFT) {
12882
        this._handleSingleCellSelectionByKey(e);
12883
        return;
12884
    }
12885
 
12886
    if((nKey > 36) && (nKey < 41)) {
12887
        // Validate trigger
12888
        var oTrigger = this._getSelectionTrigger();
12889
        // Arrow selection only works if last selected row is on current page
12890
        if(!oTrigger) {
12891
            return null;
12892
        }
12893
 
12894
        Ev.stopEvent(e);
12895
 
12896
        // Validate anchor
12897
        var oAnchor = this._getSelectionAnchor(oTrigger);
12898
 
12899
        var i, elNewRow, elNew;
12900
        var allRows = this.getTbodyEl().rows;
12901
        var elThisRow = oTrigger.el.parentNode;
12902
 
12903
        // Arrow down
12904
        if(nKey == 40) {
12905
            elNewRow = this.getNextTrEl(oTrigger.el);
12906
 
12907
            // Selecting away from anchor cell
12908
            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
12909
                // Select all cells to the end of this row
12910
                for(i=oTrigger.colKeyIndex+1; i<elThisRow.cells.length; i++){
12911
                    elNew = elThisRow.cells[i];
12912
                    this.selectCell(elNew);
12913
                }
12914
 
12915
                // Select some of the cells on the next row down
12916
                if(elNewRow) {
12917
                    for(i=0; i<=oTrigger.colKeyIndex; i++){
12918
                        elNew = elNewRow.cells[i];
12919
                        this.selectCell(elNew);
12920
                    }
12921
                }
12922
            }
12923
            // Unselecting towards anchor cell
12924
            else {
12925
                // Unselect all cells to the end of this row
12926
                for(i=oTrigger.colKeyIndex; i<elThisRow.cells.length; i++){
12927
                    this.unselectCell(elThisRow.cells[i]);
12928
                }
12929
 
12930
                // Unselect some of the cells on the next row down
12931
                if(elNewRow) {
12932
                    for(i=0; i<oTrigger.colKeyIndex; i++){
12933
                        this.unselectCell(elNewRow.cells[i]);
12934
                    }
12935
                }
12936
            }
12937
        }
12938
        // Arrow up
12939
        else if(nKey == 38) {
12940
            elNewRow = this.getPreviousTrEl(oTrigger.el);
12941
 
12942
            // Selecting away from anchor cell
12943
            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
12944
                // Select all the cells to the beginning of this row
12945
                for(i=oTrigger.colKeyIndex-1; i>-1; i--){
12946
                    elNew = elThisRow.cells[i];
12947
                    this.selectCell(elNew);
12948
                }
12949
 
12950
                // Select some of the cells from the end of the previous row
12951
                if(elNewRow) {
12952
                    for(i=elThisRow.cells.length-1; i>=oTrigger.colKeyIndex; i--){
12953
                        elNew = elNewRow.cells[i];
12954
                        this.selectCell(elNew);
12955
                    }
12956
                }
12957
            }
12958
            // Unselecting towards anchor cell
12959
            else {
12960
                // Unselect all the cells to the beginning of this row
12961
                for(i=oTrigger.colKeyIndex; i>-1; i--){
12962
                    this.unselectCell(elThisRow.cells[i]);
12963
                }
12964
 
12965
                // Unselect some of the cells from the end of the previous row
12966
                if(elNewRow) {
12967
                    for(i=elThisRow.cells.length-1; i>oTrigger.colKeyIndex; i--){
12968
                        this.unselectCell(elNewRow.cells[i]);
12969
                    }
12970
                }
12971
            }
12972
        }
12973
        // Arrow right
12974
        else if(nKey == 39) {
12975
            elNewRow = this.getNextTrEl(oTrigger.el);
12976
 
12977
            // Selecting away from anchor cell
12978
            if(oAnchor.recordIndex < oTrigger.recordIndex) {
12979
                // Select the next cell to the right
12980
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
12981
                    elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
12982
                    this.selectCell(elNew);
12983
                }
12984
                // Select the first cell of the next row
12985
                else if(elNewRow) {
12986
                    elNew = elNewRow.cells[0];
12987
                    this.selectCell(elNew);
12988
                }
12989
            }
12990
            // Unselecting towards anchor cell
12991
            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
12992
                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
12993
 
12994
                // Unselect this cell towards the right
12995
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
12996
                }
12997
                // Unselect this cells towards the first cell of the next row
12998
                else {
12999
                }
13000
            }
13001
            // Anchor is on this row
13002
            else {
13003
                // Selecting away from anchor
13004
                if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
13005
                    // Select the next cell to the right
13006
                    if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
13007
                        elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
13008
                        this.selectCell(elNew);
13009
                    }
13010
                    // Select the first cell on the next row
13011
                    else if(oTrigger.trIndex < allRows.length-1){
13012
                        elNew = elNewRow.cells[0];
13013
                        this.selectCell(elNew);
13014
                    }
13015
                }
13016
                // Unselecting towards anchor
13017
                else {
13018
                    // Unselect this cell towards the right
13019
                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
13020
                }
13021
            }
13022
        }
13023
        // Arrow left
13024
        else if(nKey == 37) {
13025
            elNewRow = this.getPreviousTrEl(oTrigger.el);
13026
 
13027
            // Unselecting towards the anchor
13028
            if(oAnchor.recordIndex < oTrigger.recordIndex) {
13029
                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
13030
 
13031
                // Unselect this cell towards the left
13032
                if(oTrigger.colKeyIndex > 0) {
13033
                }
13034
                // Unselect this cell towards the last cell of the previous row
13035
                else {
13036
                }
13037
            }
13038
            // Selecting towards the anchor
13039
            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
13040
                // Select the next cell to the left
13041
                if(oTrigger.colKeyIndex > 0) {
13042
                    elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
13043
                    this.selectCell(elNew);
13044
                }
13045
                // Select the last cell of the previous row
13046
                else if(oTrigger.trIndex > 0){
13047
                    elNew = elNewRow.cells[elNewRow.cells.length-1];
13048
                    this.selectCell(elNew);
13049
                }
13050
            }
13051
            // Anchor is on this row
13052
            else {
13053
                // Selecting away from anchor cell
13054
                if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
13055
                    // Select the next cell to the left
13056
                    if(oTrigger.colKeyIndex > 0) {
13057
                        elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
13058
                        this.selectCell(elNew);
13059
                    }
13060
                    // Select the last cell of the previous row
13061
                    else if(oTrigger.trIndex > 0){
13062
                        elNew = elNewRow.cells[elNewRow.cells.length-1];
13063
                        this.selectCell(elNew);
13064
                    }
13065
                }
13066
                // Unselecting towards anchor cell
13067
                else {
13068
                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
13069
 
13070
                    // Unselect this cell towards the left
13071
                    if(oTrigger.colKeyIndex > 0) {
13072
                    }
13073
                    // Unselect this cell towards the last cell of the previous row
13074
                    else {
13075
                    }
13076
                }
13077
            }
13078
        }
13079
    }
13080
},
13081
 
13082
/**
13083
 * Determines selection behavior resulting from a mouse event when selection mode
13084
 * is set to "singlecell".
13085
 *
13086
 * @method _handleSingleCellSelectionByMouse
13087
 * @param oArgs.event {HTMLEvent} Event object.
13088
 * @param oArgs.target {HTMLElement} Target element.
13089
 * @private
13090
 */
13091
_handleSingleCellSelectionByMouse : function(oArgs) {
13092
    var elTarget = oArgs.target;
13093
 
13094
    // Validate target cell
13095
    var elTargetCell = this.getTdEl(elTarget);
13096
    if(elTargetCell) {
13097
        var elTargetRow = this.getTrEl(elTargetCell);
13098
        var oTargetRecord = this.getRecord(elTargetRow);
13099
        var oTargetColumn = this.getColumn(elTargetCell);
13100
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
13101
 
13102
        // Set anchor
13103
        this._oAnchorCell = oTargetCell;
13104
 
13105
        // Select only target
13106
        this.unselectAllCells();
13107
        this.selectCell(oTargetCell);
13108
    }
13109
},
13110
 
13111
/**
13112
 * Determines selection behavior resulting from a key event when selection mode
13113
 * is set to "singlecell".
13114
 *
13115
 * @method _handleSingleCellSelectionByKey
13116
 * @param e {HTMLEvent} Event object.
13117
 * @private
13118
 */
13119
_handleSingleCellSelectionByKey : function(e) {
13120
    var nKey = Ev.getCharCode(e);
13121
    if((nKey == 9) || ((nKey > 36) && (nKey < 41))) {
13122
        var bSHIFT = e.shiftKey;
13123
 
13124
        // Validate trigger
13125
        var oTrigger = this._getSelectionTrigger();
13126
        // Arrow selection only works if last selected row is on current page
13127
        if(!oTrigger) {
13128
            return null;
13129
        }
13130
 
13131
        // Determine the new cell to select
13132
        var elNew;
13133
        if(nKey == 40) { // Arrow down
13134
            elNew = this.getBelowTdEl(oTrigger.el);
13135
 
13136
            // Validate new cell
13137
            if(elNew === null) {
13138
                //TODO: wrap around to first tr on current page
13139
 
13140
                //TODO: wrap forward to first tr of next page
13141
 
13142
                // Bottom selection is sticky
13143
                elNew = oTrigger.el;
13144
            }
13145
        }
13146
        else if(nKey == 38) { // Arrow up
13147
            elNew = this.getAboveTdEl(oTrigger.el);
13148
 
13149
            // Validate new cell
13150
            if(elNew === null) {
13151
                //TODO: wrap around to last tr on current page
13152
 
13153
                //TODO: wrap back to last tr of previous page
13154
 
13155
                // Top selection is sticky
13156
                elNew = oTrigger.el;
13157
            }
13158
        }
13159
        else if((nKey == 39) || (!bSHIFT && (nKey == 9))) { // Arrow right or tab
13160
            elNew = this.getNextTdEl(oTrigger.el);
13161
 
13162
            // Validate new cell
13163
            if(elNew === null) {
13164
                //TODO: wrap around to first td on current page
13165
 
13166
                //TODO: wrap forward to first td of next page
13167
 
13168
                // Top-left selection is sticky, and release TAB focus
13169
                //elNew = oTrigger.el;
13170
                return;
13171
            }
13172
        }
13173
        else if((nKey == 37) || (bSHIFT && (nKey == 9))) { // Arrow left or shift-tab
13174
            elNew = this.getPreviousTdEl(oTrigger.el);
13175
 
13176
            // Validate new cell
13177
            if(elNew === null) {
13178
                //TODO: wrap around to last td on current page
13179
 
13180
                //TODO: wrap back to last td of previous page
13181
 
13182
                // Bottom-right selection is sticky, and release TAB focus
13183
                //elNew = oTrigger.el;
13184
                return;
13185
            }
13186
        }
13187
 
13188
        Ev.stopEvent(e);
13189
 
13190
        // Unselect all cells
13191
        this.unselectAllCells();
13192
 
13193
        // Select the new cell
13194
        this.selectCell(elNew);
13195
 
13196
        // Set new anchor
13197
        this._oAnchorCell = {record:this.getRecord(elNew), column:this.getColumn(elNew)};
13198
    }
13199
},
13200
 
13201
/**
13202
 * Returns array of selected TR elements on the page.
13203
 *
13204
 * @method getSelectedTrEls
13205
 * @return {HTMLElement[]} Array of selected TR elements.
13206
 */
13207
getSelectedTrEls : function() {
13208
    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
13209
},
13210
 
13211
/**
13212
 * Sets given row to the selected state.
13213
 *
13214
 * @method selectRow
13215
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
13216
 * reference or ID string, Record instance, or RecordSet position index.
13217
 */
13218
selectRow : function(row) {
13219
    var oRecord, elRow;
13220
 
13221
    if(row instanceof YAHOO.widget.Record) {
13222
        oRecord = this._oRecordSet.getRecord(row);
13223
        elRow = this.getTrEl(oRecord);
13224
    }
13225
    else if(lang.isNumber(row)) {
13226
        oRecord = this.getRecord(row);
13227
        elRow = this.getTrEl(oRecord);
13228
    }
13229
    else {
13230
        elRow = this.getTrEl(row);
13231
        oRecord = this.getRecord(elRow);
13232
    }
13233
 
13234
    if(oRecord) {
13235
        // Update selection trackers
13236
        var tracker = this._aSelections || [];
13237
        var sRecordId = oRecord.getId();
13238
        var index = -1;
13239
 
13240
        // Remove if already there:
13241
        // Use Array.indexOf if available...
13242
        /*if(tracker.indexOf && (tracker.indexOf(sRecordId) >  -1)) {
13243
            tracker.splice(tracker.indexOf(sRecordId),1);
13244
        }*/
13245
        if(tracker.indexOf) {
13246
            index = tracker.indexOf(sRecordId);
13247
 
13248
        }
13249
        // ...or do it the old-fashioned way
13250
        else {
13251
            for(var j=tracker.length-1; j>-1; j--) {
13252
                if(tracker[j] === sRecordId){
13253
                    index = j;
13254
                    break;
13255
                }
13256
            }
13257
        }
13258
        if(index > -1) {
13259
            tracker.splice(index,1);
13260
        }
13261
 
13262
        // Add to the end
13263
        tracker.push(sRecordId);
13264
        this._aSelections = tracker;
13265
 
13266
        // Update trackers
13267
        if(!this._oAnchorRecord) {
13268
            this._oAnchorRecord = oRecord;
13269
        }
13270
 
13271
        // Update UI
13272
        if(elRow) {
13273
            Dom.addClass(elRow, DT.CLASS_SELECTED);
13274
        }
13275
 
13276
        this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
13277
    }
13278
    else {
13279
    }
13280
},
13281
 
13282
/**
13283
 * Sets given row to the unselected state.
13284
 *
13285
 * @method unselectRow
13286
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
13287
 * reference or ID string, Record instance, or RecordSet position index.
13288
 */
13289
unselectRow : function(row) {
13290
    var elRow = this.getTrEl(row);
13291
 
13292
    var oRecord;
13293
    if(row instanceof YAHOO.widget.Record) {
13294
        oRecord = this._oRecordSet.getRecord(row);
13295
    }
13296
    else if(lang.isNumber(row)) {
13297
        oRecord = this.getRecord(row);
13298
    }
13299
    else {
13300
        oRecord = this.getRecord(elRow);
13301
    }
13302
 
13303
    if(oRecord) {
13304
        // Update selection trackers
13305
        var tracker = this._aSelections || [];
13306
        var sRecordId = oRecord.getId();
13307
        var index = -1;
13308
 
13309
        // Use Array.indexOf if available...
13310
        if(tracker.indexOf) {
13311
            index = tracker.indexOf(sRecordId);
13312
        }
13313
        // ...or do it the old-fashioned way
13314
        else {
13315
            for(var j=tracker.length-1; j>-1; j--) {
13316
                if(tracker[j] === sRecordId){
13317
                    index = j;
13318
                    break;
13319
                }
13320
            }
13321
        }
13322
        if(index > -1) {
13323
            // Update tracker
13324
            tracker.splice(index,1);
13325
            this._aSelections = tracker;
13326
 
13327
            // Update the UI
13328
            Dom.removeClass(elRow, DT.CLASS_SELECTED);
13329
 
13330
            this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
13331
 
13332
            return;
13333
        }
13334
    }
13335
},
13336
 
13337
/**
13338
 * Clears out all row selections.
13339
 *
13340
 * @method unselectAllRows
13341
 */
13342
unselectAllRows : function() {
13343
    // Remove all rows from tracker
13344
    var tracker = this._aSelections || [],
13345
        recId,
13346
        removed = [];
13347
    for(var j=tracker.length-1; j>-1; j--) {
13348
       if(lang.isString(tracker[j])){
13349
            recId = tracker.splice(j,1);
13350
            removed[removed.length] = this.getRecord(lang.isArray(recId) ? recId[0] : recId);
13351
        }
13352
    }
13353
 
13354
    // Update tracker
13355
    this._aSelections = tracker;
13356
 
13357
    // Update UI
13358
    this._unselectAllTrEls();
13359
 
13360
    this.fireEvent("unselectAllRowsEvent", {records: removed});
13361
},
13362
 
13363
/**
13364
 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
13365
 * from all TD elements in the internal tracker.
13366
 *
13367
 * @method _unselectAllTdEls
13368
 * @private
13369
 */
13370
_unselectAllTdEls : function() {
13371
    var selectedCells = Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
13372
    Dom.removeClass(selectedCells, DT.CLASS_SELECTED);
13373
},
13374
 
13375
/**
13376
 * Returns array of selected TD elements on the page.
13377
 *
13378
 * @method getSelectedTdEls
13379
 * @return {HTMLElement[]} Array of selected TD elements.
13380
 */
13381
getSelectedTdEls : function() {
13382
    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
13383
},
13384
 
13385
/**
13386
 * Sets given cell to the selected state.
13387
 *
13388
 * @method selectCell
13389
 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
13390
 * object literal of syntax {record:oRecord, column:oColumn}.
13391
 */
13392
selectCell : function(cell) {
13393
//TODO: accept {record} in selectCell()
13394
    var elCell = this.getTdEl(cell);
13395
 
13396
    if(elCell) {
13397
        var oRecord = this.getRecord(elCell);
13398
        var oColumn = this.getColumn(this.getCellIndex(elCell));
13399
        var sColumnKey = oColumn.getKey();
13400
 
13401
        if(oRecord && sColumnKey) {
13402
            // Get Record ID
13403
            var tracker = this._aSelections || [];
13404
            var sRecordId = oRecord.getId();
13405
 
13406
            // Remove if there
13407
            for(var j=tracker.length-1; j>-1; j--) {
13408
               if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
13409
                    tracker.splice(j,1);
13410
                    break;
13411
                }
13412
            }
13413
 
13414
            // Add to the end
13415
            tracker.push({recordId:sRecordId, columnKey:sColumnKey});
13416
 
13417
            // Update trackers
13418
            this._aSelections = tracker;
13419
            if(!this._oAnchorCell) {
13420
                this._oAnchorCell = {record:oRecord, column:oColumn};
13421
            }
13422
 
13423
            // Update the UI
13424
            Dom.addClass(elCell, DT.CLASS_SELECTED);
13425
 
13426
            this.fireEvent("cellSelectEvent", {record:oRecord, column:oColumn, key: sColumnKey, el:elCell});
13427
            return;
13428
        }
13429
    }
13430
},
13431
 
13432
/**
13433
 * Sets given cell to the unselected state.
13434
 *
13435
 * @method unselectCell
13436
 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
13437
 * object literal of syntax {record:oRecord, column:oColumn}.
13438
 * @param cell {HTMLElement | String} DOM element reference or ID string
13439
 * to DataTable page element or RecordSet index.
13440
 */
13441
unselectCell : function(cell) {
13442
    var elCell = this.getTdEl(cell);
13443
 
13444
    if(elCell) {
13445
        var oRecord = this.getRecord(elCell);
13446
        var oColumn = this.getColumn(this.getCellIndex(elCell));
13447
        var sColumnKey = oColumn.getKey();
13448
 
13449
        if(oRecord && sColumnKey) {
13450
            // Get Record ID
13451
            var tracker = this._aSelections || [];
13452
            var id = oRecord.getId();
13453
 
13454
            // Is it selected?
13455
            for(var j=tracker.length-1; j>-1; j--) {
13456
                if((tracker[j].recordId === id) && (tracker[j].columnKey === sColumnKey)){
13457
                    // Remove from tracker
13458
                    tracker.splice(j,1);
13459
 
13460
                    // Update tracker
13461
                    this._aSelections = tracker;
13462
 
13463
                    // Update the UI
13464
                    Dom.removeClass(elCell, DT.CLASS_SELECTED);
13465
 
13466
                    this.fireEvent("cellUnselectEvent", {record:oRecord, column: oColumn, key:sColumnKey, el:elCell});
13467
                    return;
13468
                }
13469
            }
13470
        }
13471
    }
13472
},
13473
 
13474
/**
13475
 * Clears out all cell selections.
13476
 *
13477
 * @method unselectAllCells
13478
 */
13479
unselectAllCells : function() {
13480
    // Remove all cells from tracker
13481
    var tracker = this._aSelections || [];
13482
    for(var j=tracker.length-1; j>-1; j--) {
13483
       if(lang.isObject(tracker[j])){
13484
            tracker.splice(j,1);
13485
        }
13486
    }
13487
 
13488
    // Update tracker
13489
    this._aSelections = tracker;
13490
 
13491
    // Update UI
13492
    this._unselectAllTdEls();
13493
 
13494
    //TODO: send data to unselectAllCellsEvent handler
13495
    this.fireEvent("unselectAllCellsEvent");
13496
},
13497
 
13498
/**
13499
 * Returns true if given item is selected, false otherwise.
13500
 *
13501
 * @method isSelected
13502
 * @param o {String | HTMLElement | YAHOO.widget.Record | Number
13503
 * {record:YAHOO.widget.Record, column:YAHOO.widget.Column} } TR or TD element by
13504
 * reference or ID string, a Record instance, a RecordSet position index,
13505
 * or an object literal representation
13506
 * of a cell.
13507
 * @return {Boolean} True if item is selected.
13508
 */
13509
isSelected : function(o) {
13510
    if(o && (o.ownerDocument == document)) {
13511
        return (Dom.hasClass(this.getTdEl(o),DT.CLASS_SELECTED) || Dom.hasClass(this.getTrEl(o),DT.CLASS_SELECTED));
13512
    }
13513
    else {
13514
        var oRecord, sRecordId, j;
13515
        var tracker = this._aSelections;
13516
        if(tracker && tracker.length > 0) {
13517
            // Looking for a Record?
13518
            if(o instanceof YAHOO.widget.Record) {
13519
                oRecord = o;
13520
            }
13521
            else if(lang.isNumber(o)) {
13522
                oRecord = this.getRecord(o);
13523
            }
13524
            if(oRecord) {
13525
                sRecordId = oRecord.getId();
13526
 
13527
                // Is it there?
13528
                // Use Array.indexOf if available...
13529
                if(tracker.indexOf) {
13530
                    if(tracker.indexOf(sRecordId) >  -1) {
13531
                        return true;
13532
                    }
13533
                }
13534
                // ...or do it the old-fashioned way
13535
                else {
13536
                    for(j=tracker.length-1; j>-1; j--) {
13537
                       if(tracker[j] === sRecordId){
13538
                        return true;
13539
                       }
13540
                    }
13541
                }
13542
            }
13543
            // Looking for a cell
13544
            else if(o.record && o.column){
13545
                sRecordId = o.record.getId();
13546
                var sColumnKey = o.column.getKey();
13547
 
13548
                for(j=tracker.length-1; j>-1; j--) {
13549
                    if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
13550
                        return true;
13551
                    }
13552
                }
13553
            }
13554
        }
13555
    }
13556
    return false;
13557
},
13558
 
13559
/**
13560
 * Returns selected rows as an array of Record IDs.
13561
 *
13562
 * @method getSelectedRows
13563
 * @return {String[]} Array of selected rows by Record ID.
13564
 */
13565
getSelectedRows : function() {
13566
    var aSelectedRows = [];
13567
    var tracker = this._aSelections || [];
13568
    for(var j=0; j<tracker.length; j++) {
13569
       if(lang.isString(tracker[j])){
13570
            aSelectedRows.push(tracker[j]);
13571
        }
13572
    }
13573
    return aSelectedRows;
13574
},
13575
 
13576
/**
13577
 * Returns selected cells as an array of object literals:
13578
 *     {recordId:sRecordId, columnKey:sColumnKey}.
13579
 *
13580
 * @method getSelectedCells
13581
 * @return {Object[]} Array of selected cells by Record ID and Column ID.
13582
 */
13583
getSelectedCells : function() {
13584
    var aSelectedCells = [];
13585
    var tracker = this._aSelections || [];
13586
    for(var j=0; j<tracker.length; j++) {
13587
       if(tracker[j] && lang.isObject(tracker[j])){
13588
            aSelectedCells.push(tracker[j]);
13589
        }
13590
    }
13591
    return aSelectedCells;
13592
},
13593
 
13594
/**
13595
 * Returns last selected Record ID.
13596
 *
13597
 * @method getLastSelectedRecord
13598
 * @return {String} Record ID of last selected row.
13599
 */
13600
getLastSelectedRecord : function() {
13601
    var tracker = this._aSelections;
13602
    if(tracker && tracker.length > 0) {
13603
        for(var i=tracker.length-1; i>-1; i--) {
13604
           if(lang.isString(tracker[i])){
13605
                return tracker[i];
13606
            }
13607
        }
13608
    }
13609
},
13610
 
13611
/**
13612
 * Returns last selected cell as an object literal:
13613
 *     {recordId:sRecordId, columnKey:sColumnKey}.
13614
 *
13615
 * @method getLastSelectedCell
13616
 * @return {Object} Object literal representation of a cell.
13617
 */
13618
getLastSelectedCell : function() {
13619
    var tracker = this._aSelections;
13620
    if(tracker && tracker.length > 0) {
13621
        for(var i=tracker.length-1; i>-1; i--) {
13622
           if(tracker[i].recordId && tracker[i].columnKey){
13623
                return tracker[i];
13624
            }
13625
        }
13626
    }
13627
},
13628
 
13629
/**
13630
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row.
13631
 *
13632
 * @method highlightRow
13633
 * @param row {HTMLElement | String} DOM element reference or ID string.
13634
 */
13635
highlightRow : function(row) {
13636
    var elRow = this.getTrEl(row);
13637
 
13638
    if(elRow) {
13639
        // Make sure previous row is unhighlighted
13640
/*        if(this._sLastHighlightedTrElId) {
13641
            Dom.removeClass(this._sLastHighlightedTrElId,DT.CLASS_HIGHLIGHTED);
13642
        }*/
13643
        var oRecord = this.getRecord(elRow);
13644
        Dom.addClass(elRow,DT.CLASS_HIGHLIGHTED);
13645
        //this._sLastHighlightedTrElId = elRow.id;
13646
        this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow});
13647
        return;
13648
    }
13649
},
13650
 
13651
/**
13652
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row.
13653
 *
13654
 * @method unhighlightRow
13655
 * @param row {HTMLElement | String} DOM element reference or ID string.
13656
 */
13657
unhighlightRow : function(row) {
13658
    var elRow = this.getTrEl(row);
13659
 
13660
    if(elRow) {
13661
        var oRecord = this.getRecord(elRow);
13662
        Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED);
13663
        this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
13664
        return;
13665
    }
13666
},
13667
 
13668
/**
13669
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell.
13670
 *
13671
 * @method highlightCell
13672
 * @param cell {HTMLElement | String} DOM element reference or ID string.
13673
 */
13674
highlightCell : function(cell) {
13675
    var elCell = this.getTdEl(cell);
13676
 
13677
    if(elCell) {
13678
        // Make sure previous cell is unhighlighted
13679
        if(this._elLastHighlightedTd) {
13680
            this.unhighlightCell(this._elLastHighlightedTd);
13681
        }
13682
 
13683
        var oRecord = this.getRecord(elCell);
13684
        var oColumn = this.getColumn(this.getCellIndex(elCell));
13685
        var sColumnKey = oColumn.getKey();
13686
        Dom.addClass(elCell,DT.CLASS_HIGHLIGHTED);
13687
        this._elLastHighlightedTd = elCell;
13688
        this.fireEvent("cellHighlightEvent", {record:oRecord, column:oColumn, key:sColumnKey, el:elCell});
13689
        return;
13690
    }
13691
},
13692
 
13693
/**
13694
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell.
13695
 *
13696
 * @method unhighlightCell
13697
 * @param cell {HTMLElement | String} DOM element reference or ID string.
13698
 */
13699
unhighlightCell : function(cell) {
13700
    var elCell = this.getTdEl(cell);
13701
 
13702
    if(elCell) {
13703
        var oRecord = this.getRecord(elCell);
13704
        Dom.removeClass(elCell,DT.CLASS_HIGHLIGHTED);
13705
        this._elLastHighlightedTd = null;
13706
        this.fireEvent("cellUnhighlightEvent", {record:oRecord, column:this.getColumn(this.getCellIndex(elCell)), key:this.getColumn(this.getCellIndex(elCell)).getKey(), el:elCell});
13707
        return;
13708
    }
13709
},
13710
 
13711
 
13712
 
13713
 
13714
 
13715
 
13716
 
13717
 
13718
 
13719
 
13720
 
13721
 
13722
 
13723
 
13724
 
13725
 
13726
 
13727
 
13728
 
13729
 
13730
 
13731
 
13732
 
13733
 
13734
 
13735
 
13736
 
13737
 
13738
 
13739
 
13740
 
13741
 
13742
 
13743
 
13744
 
13745
 
13746
 
13747
 
13748
 
13749
 
13750
 
13751
 
13752
 
13753
 
13754
 
13755
// INLINE EDITING
13756
 
13757
/**
13758
 * Assigns CellEditor instance to existing Column.
13759
 * @method addCellEditor
13760
 * @param oColumn {YAHOO.widget.Column} Column instance.
13761
 * @param oEditor {YAHOO.wdiget.CellEditor} CellEditor instance.
13762
 */
13763
addCellEditor : function(oColumn, oEditor) {
13764
    oColumn.editor = oEditor;
13765
    oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
13766
    oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
13767
    oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
13768
    oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
13769
    oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
13770
    oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
13771
    oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
13772
    oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
13773
},
13774
 
13775
/**
13776
 * Returns current CellEditor instance, or null.
13777
 * @method getCellEditor
13778
 * @return {YAHOO.widget.CellEditor} CellEditor instance.
13779
 */
13780
getCellEditor : function() {
13781
    return this._oCellEditor;
13782
},
13783
 
13784
 
13785
/**
13786
 * Activates and shows CellEditor instance for the given cell while deactivating and
13787
 * canceling previous CellEditor. It is baked into DataTable that only one CellEditor
13788
 * can be active at any given time.
13789
 *
13790
 * @method showCellEditor
13791
 * @param elCell {HTMLElement | String} Cell to edit.
13792
 */
13793
showCellEditor : function(elCell, oRecord, oColumn) {
13794
    // Get a particular CellEditor
13795
    elCell = this.getTdEl(elCell);
13796
    if(elCell) {
13797
        oColumn = this.getColumn(elCell);
13798
        if(oColumn && oColumn.editor) {
13799
            var oCellEditor = this._oCellEditor;
13800
            // Clean up active CellEditor
13801
            if(oCellEditor) {
13802
                if(this._oCellEditor.cancel) {
13803
                    this._oCellEditor.cancel();
13804
                }
13805
                else if(oCellEditor.isActive) {
13806
                    this.cancelCellEditor();
13807
                }
13808
            }
13809
 
13810
            if(oColumn.editor instanceof YAHOO.widget.BaseCellEditor) {
13811
                // Get CellEditor
13812
                oCellEditor = oColumn.editor;
13813
                var ok = oCellEditor.attach(this, elCell);
13814
                if(ok) {
13815
                    oCellEditor.render();
13816
                    oCellEditor.move();
13817
                    ok = this.doBeforeShowCellEditor(oCellEditor);
13818
                    if(ok) {
13819
                        oCellEditor.show();
13820
                        this._oCellEditor = oCellEditor;
13821
                    }
13822
                }
13823
            }
13824
            // Backward compatibility
13825
            else {
13826
                    if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) {
13827
                        oRecord = this.getRecord(elCell);
13828
                    }
13829
                    if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) {
13830
                        oColumn = this.getColumn(elCell);
13831
                    }
13832
                    if(oRecord && oColumn) {
13833
                        if(!this._oCellEditor || this._oCellEditor.container) {
13834
                            this._initCellEditorEl();
13835
                        }
13836
 
13837
                        // Update Editor values
13838
                        oCellEditor = this._oCellEditor;
13839
                        oCellEditor.cell = elCell;
13840
                        oCellEditor.record = oRecord;
13841
                        oCellEditor.column = oColumn;
13842
                        oCellEditor.validator = (oColumn.editorOptions &&
13843
                                lang.isFunction(oColumn.editorOptions.validator)) ?
13844
                                oColumn.editorOptions.validator : null;
13845
                        oCellEditor.value = oRecord.getData(oColumn.key);
13846
                        oCellEditor.defaultValue = null;
13847
 
13848
                        // Move Editor
13849
                        var elContainer = oCellEditor.container;
13850
                        var x = Dom.getX(elCell);
13851
                        var y = Dom.getY(elCell);
13852
 
13853
                        // SF doesn't get xy for cells in scrolling table
13854
                        // when tbody display is set to block
13855
                        if(isNaN(x) || isNaN(y)) {
13856
                            x = elCell.offsetLeft + // cell pos relative to table
13857
                                    Dom.getX(this._elTbody.parentNode) - // plus table pos relative to document
13858
                                    this._elTbody.scrollLeft; // minus tbody scroll
13859
                            y = elCell.offsetTop + // cell pos relative to table
13860
                                    Dom.getY(this._elTbody.parentNode) - // plus table pos relative to document
13861
                                    this._elTbody.scrollTop + // minus tbody scroll
13862
                                    this._elThead.offsetHeight; // account for fixed THEAD cells
13863
                        }
13864
 
13865
                        elContainer.style.left = x + "px";
13866
                        elContainer.style.top = y + "px";
13867
 
13868
                        // Hook to customize the UI
13869
                        this.doBeforeShowCellEditor(this._oCellEditor);
13870
 
13871
                        //TODO: This is temporarily up here due so elements can be focused
13872
                        // Show Editor
13873
                        elContainer.style.display = "";
13874
 
13875
                        // Handle ESC key
13876
                        Ev.addListener(elContainer, "keydown", function(e, oSelf) {
13877
                            // ESC hides Cell Editor
13878
                            if((e.keyCode == 27)) {
13879
                                oSelf.cancelCellEditor();
13880
                                oSelf.focusTbodyEl();
13881
                            }
13882
                            else {
13883
                                oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e});
13884
                            }
13885
                        }, this);
13886
 
13887
                        // Render Editor markup
13888
                        var fnEditor;
13889
                        if(lang.isString(oColumn.editor)) {
13890
                            switch(oColumn.editor) {
13891
                                case "checkbox":
13892
                                    fnEditor = DT.editCheckbox;
13893
                                    break;
13894
                                case "date":
13895
                                    fnEditor = DT.editDate;
13896
                                    break;
13897
                                case "dropdown":
13898
                                    fnEditor = DT.editDropdown;
13899
                                    break;
13900
                                case "radio":
13901
                                    fnEditor = DT.editRadio;
13902
                                    break;
13903
                                case "textarea":
13904
                                    fnEditor = DT.editTextarea;
13905
                                    break;
13906
                                case "textbox":
13907
                                    fnEditor = DT.editTextbox;
13908
                                    break;
13909
                                default:
13910
                                    fnEditor = null;
13911
                            }
13912
                        }
13913
                        else if(lang.isFunction(oColumn.editor)) {
13914
                            fnEditor = oColumn.editor;
13915
                        }
13916
 
13917
                        if(fnEditor) {
13918
                            // Create DOM input elements
13919
                            fnEditor(this._oCellEditor, this);
13920
 
13921
                            // Show Save/Cancel buttons
13922
                            if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) {
13923
                                this.showCellEditorBtns(elContainer);
13924
                            }
13925
 
13926
                            oCellEditor.isActive = true;
13927
 
13928
                            //TODO: verify which args to pass
13929
                            this.fireEvent("editorShowEvent", {editor:oCellEditor});
13930
                            return;
13931
                        }
13932
                    }
13933
 
13934
 
13935
 
13936
 
13937
            }
13938
        }
13939
    }
13940
},
13941
 
13942
/**
13943
 * Backward compatibility.
13944
 *
13945
 * @method _initCellEditorEl
13946
 * @private
13947
 * @deprecated Use BaseCellEditor class.
13948
 */
13949
_initCellEditorEl : function() {
13950
    // Attach Cell Editor container element as first child of body
13951
    var elCellEditor = document.createElement("div");
13952
    elCellEditor.id = this._sId + "-celleditor";
13953
    elCellEditor.style.display = "none";
13954
    elCellEditor.tabIndex = 0;
13955
    Dom.addClass(elCellEditor, DT.CLASS_EDITOR);
13956
    var elFirstChild = Dom.getFirstChild(document.body);
13957
    if(elFirstChild) {
13958
        elCellEditor = Dom.insertBefore(elCellEditor, elFirstChild);
13959
    }
13960
    else {
13961
        elCellEditor = document.body.appendChild(elCellEditor);
13962
    }
13963
 
13964
    // Internal tracker of Cell Editor values
13965
    var oCellEditor = {};
13966
    oCellEditor.container = elCellEditor;
13967
    oCellEditor.value = null;
13968
    oCellEditor.isActive = false;
13969
    this._oCellEditor = oCellEditor;
13970
},
13971
 
13972
/**
13973
 * Overridable abstract method to customize CellEditor before showing.
13974
 *
13975
 * @method doBeforeShowCellEditor
13976
 * @param oCellEditor {YAHOO.widget.CellEditor} The CellEditor instance.
13977
 * @return {Boolean} Return true to continue showing CellEditor.
13978
 */
13979
doBeforeShowCellEditor : function(oCellEditor) {
13980
    return true;
13981
},
13982
 
13983
/**
13984
 * Saves active CellEditor input to Record and upates DOM UI.
13985
 *
13986
 * @method saveCellEditor
13987
 */
13988
saveCellEditor : function() {
13989
    if(this._oCellEditor) {
13990
        if(this._oCellEditor.save) {
13991
            this._oCellEditor.save();
13992
        }
13993
        // Backward compatibility
13994
        else if(this._oCellEditor.isActive) {
13995
            var newData = this._oCellEditor.value;
13996
            // Copy the data to pass to the event
13997
            //var oldData = YAHOO.widget.DataTable._cloneObject(this._oCellEditor.record.getData(this._oCellEditor.column.key));
13998
            var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key);
13999
 
14000
            // Validate input data
14001
            if(this._oCellEditor.validator) {
14002
                newData = this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData, this._oCellEditor);
14003
                if(newData === null ) {
14004
                    this.resetCellEditor();
14005
                    this.fireEvent("editorRevertEvent",
14006
                            {editor:this._oCellEditor, oldData:oldData, newData:newData});
14007
                    return;
14008
                }
14009
            }
14010
            // Update the Record
14011
            this._oRecordSet.updateRecordValue(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
14012
            // Update the UI
14013
            this.formatCell(this._oCellEditor.cell.firstChild, this._oCellEditor.record, this._oCellEditor.column);
14014
 
14015
            // Bug fix 1764044
14016
            this._oChainRender.add({
14017
                method: function() {
14018
                    this.validateColumnWidths();
14019
                },
14020
                scope: this
14021
            });
14022
            this._oChainRender.run();
14023
            // Clear out the Cell Editor
14024
            this.resetCellEditor();
14025
 
14026
            this.fireEvent("editorSaveEvent",
14027
                    {editor:this._oCellEditor, oldData:oldData, newData:newData});
14028
        }
14029
    }
14030
},
14031
 
14032
/**
14033
 * Cancels active CellEditor.
14034
 *
14035
 * @method cancelCellEditor
14036
 */
14037
cancelCellEditor : function() {
14038
    if(this._oCellEditor) {
14039
        if(this._oCellEditor.cancel) {
14040
            this._oCellEditor.cancel();
14041
        }
14042
        // Backward compatibility
14043
        else if(this._oCellEditor.isActive) {
14044
            this.resetCellEditor();
14045
            //TODO: preserve values for the event?
14046
            this.fireEvent("editorCancelEvent", {editor:this._oCellEditor});
14047
        }
14048
 
14049
    }
14050
},
14051
 
14052
/**
14053
 * Destroys active CellEditor instance and UI.
14054
 *
14055
 * @method destroyCellEditor
14056
 */
14057
destroyCellEditor : function() {
14058
    if(this._oCellEditor) {
14059
        this._oCellEditor.destroy();
14060
        this._oCellEditor = null;
14061
    }
14062
},
14063
 
14064
/**
14065
 * Passes through showEvent of the active CellEditor.
14066
 *
14067
 * @method _onEditorShowEvent
14068
 * @param oArgs {Object}  Custom Event args.
14069
 * @private
14070
 */
14071
_onEditorShowEvent : function(oArgs) {
14072
    this.fireEvent("editorShowEvent", oArgs);
14073
},
14074
 
14075
/**
14076
 * Passes through keydownEvent of the active CellEditor.
14077
 * @param oArgs {Object}  Custom Event args.
14078
 *
14079
 * @method _onEditorKeydownEvent
14080
 * @private
14081
 */
14082
_onEditorKeydownEvent : function(oArgs) {
14083
    this.fireEvent("editorKeydownEvent", oArgs);
14084
},
14085
 
14086
/**
14087
 * Passes through revertEvent of the active CellEditor.
14088
 *
14089
 * @method _onEditorRevertEvent
14090
 * @param oArgs {Object}  Custom Event args.
14091
 * @private
14092
 */
14093
_onEditorRevertEvent : function(oArgs) {
14094
    this.fireEvent("editorRevertEvent", oArgs);
14095
},
14096
 
14097
/**
14098
 * Passes through saveEvent of the active CellEditor.
14099
 *
14100
 * @method _onEditorSaveEvent
14101
 * @param oArgs {Object}  Custom Event args.
14102
 * @private
14103
 */
14104
_onEditorSaveEvent : function(oArgs) {
14105
    this.fireEvent("editorSaveEvent", oArgs);
14106
},
14107
 
14108
/**
14109
 * Passes through cancelEvent of the active CellEditor.
14110
 *
14111
 * @method _onEditorCancelEvent
14112
 * @param oArgs {Object}  Custom Event args.
14113
 * @private
14114
 */
14115
_onEditorCancelEvent : function(oArgs) {
14116
    this.fireEvent("editorCancelEvent", oArgs);
14117
},
14118
 
14119
/**
14120
 * Passes through blurEvent of the active CellEditor.
14121
 *
14122
 * @method _onEditorBlurEvent
14123
 * @param oArgs {Object}  Custom Event args.
14124
 * @private
14125
 */
14126
_onEditorBlurEvent : function(oArgs) {
14127
    this.fireEvent("editorBlurEvent", oArgs);
14128
},
14129
 
14130
/**
14131
 * Passes through blockEvent of the active CellEditor.
14132
 *
14133
 * @method _onEditorBlockEvent
14134
 * @param oArgs {Object}  Custom Event args.
14135
 * @private
14136
 */
14137
_onEditorBlockEvent : function(oArgs) {
14138
    this.fireEvent("editorBlockEvent", oArgs);
14139
},
14140
 
14141
/**
14142
 * Passes through unblockEvent of the active CellEditor.
14143
 *
14144
 * @method _onEditorUnblockEvent
14145
 * @param oArgs {Object}  Custom Event args.
14146
 * @private
14147
 */
14148
_onEditorUnblockEvent : function(oArgs) {
14149
    this.fireEvent("editorUnblockEvent", oArgs);
14150
},
14151
 
14152
/**
14153
 * Public handler of the editorBlurEvent. By default, saves on blur if
14154
 * disableBtns is true, otherwise cancels on blur.
14155
 *
14156
 * @method onEditorBlurEvent
14157
 * @param oArgs {Object}  Custom Event args.
14158
 */
14159
onEditorBlurEvent : function(oArgs) {
14160
    if(oArgs.editor.disableBtns) {
14161
        // Save on blur
14162
        if(oArgs.editor.save) { // Backward incompatible
14163
            oArgs.editor.save();
14164
        }
14165
    }
14166
    else if(oArgs.editor.cancel) { // Backward incompatible
14167
        // Cancel on blur
14168
        oArgs.editor.cancel();
14169
    }
14170
},
14171
 
14172
/**
14173
 * Public handler of the editorBlockEvent. By default, disables DataTable UI.
14174
 *
14175
 * @method onEditorBlockEvent
14176
 * @param oArgs {Object}  Custom Event args.
14177
 */
14178
onEditorBlockEvent : function(oArgs) {
14179
    this.disable();
14180
},
14181
 
14182
/**
14183
 * Public handler of the editorUnblockEvent. By default, undisables DataTable UI.
14184
 *
14185
 * @method onEditorUnblockEvent
14186
 * @param oArgs {Object}  Custom Event args.
14187
 */
14188
onEditorUnblockEvent : function(oArgs) {
14189
    this.undisable();
14190
},
14191
 
14192
 
14193
 
14194
 
14195
 
14196
 
14197
 
14198
 
14199
 
14200
 
14201
 
14202
 
14203
 
14204
 
14205
 
14206
 
14207
 
14208
 
14209
 
14210
 
14211
 
14212
 
14213
 
14214
 
14215
 
14216
 
14217
 
14218
 
14219
 
14220
 
14221
 
14222
 
14223
 
14224
 
14225
 
14226
 
14227
 
14228
 
14229
// ABSTRACT METHODS
14230
 
14231
/**
14232
 * Overridable method gives implementers a hook to access data before
14233
 * it gets added to RecordSet and rendered to the TBODY.
14234
 *
14235
 * @method doBeforeLoadData
14236
 * @param sRequest {String} Original request.
14237
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14238
 * @param oPayload {MIXED} additional arguments
14239
 * @return {Boolean} Return true to continue loading data into RecordSet and
14240
 * updating DataTable with new Records, false to cancel.
14241
 */
14242
doBeforeLoadData : function(sRequest, oResponse, oPayload) {
14243
    return true;
14244
},
14245
 
14246
 
14247
 
14248
 
14249
 
14250
 
14251
 
14252
 
14253
 
14254
 
14255
 
14256
 
14257
 
14258
 
14259
 
14260
 
14261
 
14262
 
14263
 
14264
 
14265
 
14266
 
14267
 
14268
 
14269
 
14270
 
14271
 
14272
 
14273
 
14274
 
14275
 
14276
 
14277
 
14278
 
14279
 
14280
 
14281
 
14282
 
14283
 
14284
 
14285
 
14286
 
14287
 
14288
 
14289
 
14290
 
14291
 
14292
 
14293
 
14294
 
14295
 
14296
 
14297
 
14298
 
14299
 
14300
 
14301
 
14302
 
14303
 
14304
 
14305
 
14306
 
14307
 
14308
/////////////////////////////////////////////////////////////////////////////
14309
//
14310
// Public Custom Event Handlers
14311
//
14312
/////////////////////////////////////////////////////////////////////////////
14313
 
14314
/**
14315
 * Custom event handler to sort Column.
14316
 *
14317
 * @method onEventSortColumn
14318
 * @param oArgs.event {HTMLEvent} Event object.
14319
 * @param oArgs.target {HTMLElement} Target element.
14320
 */
14321
onEventSortColumn : function(oArgs) {
14322
//TODO: support form elements in sortable columns
14323
    var evt = oArgs.event;
14324
    var target = oArgs.target;
14325
 
14326
    var el = this.getThEl(target) || this.getTdEl(target);
14327
    if(el) {
14328
        var oColumn = this.getColumn(el);
14329
        if(oColumn.sortable) {
14330
            Ev.stopEvent(evt);
14331
            this.sortColumn(oColumn);
14332
        }
14333
    }
14334
    else {
14335
    }
14336
},
14337
 
14338
/**
14339
 * Custom event handler to select Column.
14340
 *
14341
 * @method onEventSelectColumn
14342
 * @param oArgs.event {HTMLEvent} Event object.
14343
 * @param oArgs.target {HTMLElement} Target element.
14344
 */
14345
onEventSelectColumn : function(oArgs) {
14346
    this.selectColumn(oArgs.target);
14347
},
14348
 
14349
/**
14350
 * Custom event handler to highlight Column. Accounts for spurious
14351
 * caused-by-child events.
14352
 *
14353
 * @method onEventHighlightColumn
14354
 * @param oArgs.event {HTMLEvent} Event object.
14355
 * @param oArgs.target {HTMLElement} Target element.
14356
 */
14357
onEventHighlightColumn : function(oArgs) {
14358
    this.highlightColumn(oArgs.target);
14359
},
14360
 
14361
/**
14362
 * Custom event handler to unhighlight Column. Accounts for spurious
14363
 * caused-by-child events.
14364
 *
14365
 * @method onEventUnhighlightColumn
14366
 * @param oArgs.event {HTMLEvent} Event object.
14367
 * @param oArgs.target {HTMLElement} Target element.
14368
 */
14369
onEventUnhighlightColumn : function(oArgs) {
14370
    this.unhighlightColumn(oArgs.target);
14371
},
14372
 
14373
/**
14374
 * Custom event handler to manage selection according to desktop paradigm.
14375
 *
14376
 * @method onEventSelectRow
14377
 * @param oArgs.event {HTMLEvent} Event object.
14378
 * @param oArgs.target {HTMLElement} Target element.
14379
 */
14380
onEventSelectRow : function(oArgs) {
14381
    var sMode = this.get("selectionMode");
14382
    if(sMode == "single") {
14383
        this._handleSingleSelectionByMouse(oArgs);
14384
    }
14385
    else {
14386
        this._handleStandardSelectionByMouse(oArgs);
14387
    }
14388
},
14389
 
14390
/**
14391
 * Custom event handler to select cell.
14392
 *
14393
 * @method onEventSelectCell
14394
 * @param oArgs.event {HTMLEvent} Event object.
14395
 * @param oArgs.target {HTMLElement} Target element.
14396
 */
14397
onEventSelectCell : function(oArgs) {
14398
    var sMode = this.get("selectionMode");
14399
    if(sMode == "cellblock") {
14400
        this._handleCellBlockSelectionByMouse(oArgs);
14401
    }
14402
    else if(sMode == "cellrange") {
14403
        this._handleCellRangeSelectionByMouse(oArgs);
14404
    }
14405
    else {
14406
        this._handleSingleCellSelectionByMouse(oArgs);
14407
    }
14408
},
14409
 
14410
/**
14411
 * Custom event handler to highlight row. Accounts for spurious
14412
 * caused-by-child events.
14413
 *
14414
 * @method onEventHighlightRow
14415
 * @param oArgs.event {HTMLEvent} Event object.
14416
 * @param oArgs.target {HTMLElement} Target element.
14417
 */
14418
onEventHighlightRow : function(oArgs) {
14419
    this.highlightRow(oArgs.target);
14420
},
14421
 
14422
/**
14423
 * Custom event handler to unhighlight row. Accounts for spurious
14424
 * caused-by-child events.
14425
 *
14426
 * @method onEventUnhighlightRow
14427
 * @param oArgs.event {HTMLEvent} Event object.
14428
 * @param oArgs.target {HTMLElement} Target element.
14429
 */
14430
onEventUnhighlightRow : function(oArgs) {
14431
    this.unhighlightRow(oArgs.target);
14432
},
14433
 
14434
/**
14435
 * Custom event handler to highlight cell. Accounts for spurious
14436
 * caused-by-child events.
14437
 *
14438
 * @method onEventHighlightCell
14439
 * @param oArgs.event {HTMLEvent} Event object.
14440
 * @param oArgs.target {HTMLElement} Target element.
14441
 */
14442
onEventHighlightCell : function(oArgs) {
14443
    this.highlightCell(oArgs.target);
14444
},
14445
 
14446
/**
14447
 * Custom event handler to unhighlight cell. Accounts for spurious
14448
 * caused-by-child events.
14449
 *
14450
 * @method onEventUnhighlightCell
14451
 * @param oArgs.event {HTMLEvent} Event object.
14452
 * @param oArgs.target {HTMLElement} Target element.
14453
 */
14454
onEventUnhighlightCell : function(oArgs) {
14455
    this.unhighlightCell(oArgs.target);
14456
},
14457
 
14458
/**
14459
 * Custom event handler to format cell.
14460
 *
14461
 * @method onEventFormatCell
14462
 * @param oArgs.event {HTMLEvent} Event object.
14463
 * @param oArgs.target {HTMLElement} Target element.
14464
 */
14465
onEventFormatCell : function(oArgs) {
14466
    var target = oArgs.target;
14467
 
14468
    var elCell = this.getTdEl(target);
14469
    if(elCell) {
14470
        var oColumn = this.getColumn(this.getCellIndex(elCell));
14471
        this.formatCell(elCell.firstChild, this.getRecord(elCell), oColumn);
14472
    }
14473
    else {
14474
    }
14475
},
14476
 
14477
/**
14478
 * Custom event handler to edit cell.
14479
 *
14480
 * @method onEventShowCellEditor
14481
 * @param oArgs.event {HTMLEvent} Event object.
14482
 * @param oArgs.target {HTMLElement} Target element.
14483
 */
14484
onEventShowCellEditor : function(oArgs) {
14485
    if(!this.isDisabled()) {
14486
        this.showCellEditor(oArgs.target);
14487
    }
14488
},
14489
 
14490
/**
14491
 * Custom event handler to save active CellEditor input.
14492
 *
14493
 * @method onEventSaveCellEditor
14494
 */
14495
onEventSaveCellEditor : function(oArgs) {
14496
    if(this._oCellEditor) {
14497
        if(this._oCellEditor.save) {
14498
            this._oCellEditor.save();
14499
        }
14500
        // Backward compatibility
14501
        else {
14502
            this.saveCellEditor();
14503
        }
14504
    }
14505
},
14506
 
14507
/**
14508
 * Custom event handler to cancel active CellEditor.
14509
 *
14510
 * @method onEventCancelCellEditor
14511
 */
14512
onEventCancelCellEditor : function(oArgs) {
14513
    if(this._oCellEditor) {
14514
        if(this._oCellEditor.cancel) {
14515
            this._oCellEditor.cancel();
14516
        }
14517
        // Backward compatibility
14518
        else {
14519
            this.cancelCellEditor();
14520
        }
14521
    }
14522
},
14523
 
14524
/**
14525
 * Callback function receives data from DataSource and populates an entire
14526
 * DataTable with Records and TR elements, clearing previous Records, if any.
14527
 *
14528
 * @method onDataReturnInitializeTable
14529
 * @param sRequest {String} Original request.
14530
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14531
 * @param oPayload {MIXED} (optional) Additional argument(s)
14532
 */
14533
onDataReturnInitializeTable : function(sRequest, oResponse, oPayload) {
14534
    if((this instanceof DT) && this._sId) {
14535
        this.initializeTable();
14536
 
14537
        this.onDataReturnSetRows(sRequest,oResponse,oPayload);
14538
    }
14539
},
14540
 
14541
/**
14542
 * Callback function receives reponse from DataSource, replaces all existing
14543
 * Records in  RecordSet, updates TR elements with new data, and updates state
14544
 * UI for pagination and sorting from payload data, if necessary.
14545
 *
14546
 * @method onDataReturnReplaceRows
14547
 * @param oRequest {MIXED} Original generated request.
14548
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14549
 * @param oPayload {MIXED} (optional) Additional argument(s)
14550
 */
14551
onDataReturnReplaceRows : function(oRequest, oResponse, oPayload) {
14552
    if((this instanceof DT) && this._sId) {
14553
        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
14554
 
14555
        // Pass data through abstract method for any transformations
14556
        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
14557
            pag   = this.get('paginator'),
14558
            index = 0;
14559
 
14560
        // Data ok to set
14561
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14562
            // Update Records
14563
            this._oRecordSet.reset();
14564
 
14565
            if (this.get('dynamicData')) {
14566
                if (oPayload && oPayload.pagination &&
14567
                    lang.isNumber(oPayload.pagination.recordOffset)) {
14568
                    index = oPayload.pagination.recordOffset;
14569
                } else if (pag) {
14570
                    index = pag.getStartIndex();
14571
                }
14572
            }
14573
 
14574
            this._oRecordSet.setRecords(oResponse.results, index | 0);
14575
 
14576
            // Update state
14577
            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
14578
 
14579
            // Update UI
14580
            this.render();
14581
        }
14582
        // Error
14583
        else if(ok && oResponse.error) {
14584
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14585
        }
14586
    }
14587
},
14588
 
14589
/**
14590
 * Callback function receives data from DataSource and appends to an existing
14591
 * DataTable new Records and, if applicable, creates or updates
14592
 * corresponding TR elements.
14593
 *
14594
 * @method onDataReturnAppendRows
14595
 * @param sRequest {String} Original request.
14596
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14597
 * @param oPayload {MIXED} (optional) Additional argument(s)
14598
 */
14599
onDataReturnAppendRows : function(sRequest, oResponse, oPayload) {
14600
    if((this instanceof DT) && this._sId) {
14601
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
14602
 
14603
        // Pass data through abstract method for any transformations
14604
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
14605
 
14606
        // Data ok to append
14607
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14608
            // Append rows
14609
            this.addRows(oResponse.results);
14610
 
14611
            // Update state
14612
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
14613
        }
14614
        // Error
14615
        else if(ok && oResponse.error) {
14616
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14617
        }
14618
    }
14619
},
14620
 
14621
/**
14622
 * Callback function receives data from DataSource and inserts new records
14623
 * starting at the index specified in oPayload.insertIndex. The value for
14624
 * oPayload.insertIndex can be populated when sending the request to the DataSource,
14625
 * or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime.
14626
 * If applicable, creates or updates corresponding TR elements.
14627
 *
14628
 * @method onDataReturnInsertRows
14629
 * @param sRequest {String} Original request.
14630
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14631
 * @param oPayload {MIXED} Argument payload, looks in oPayload.insertIndex.
14632
 */
14633
onDataReturnInsertRows : function(sRequest, oResponse, oPayload) {
14634
    if((this instanceof DT) && this._sId) {
14635
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
14636
 
14637
        // Pass data through abstract method for any transformations
14638
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
14639
 
14640
        // Data ok to append
14641
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14642
            // Insert rows
14643
            this.addRows(oResponse.results, (oPayload ? oPayload.insertIndex : 0));
14644
 
14645
            // Update state
14646
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
14647
        }
14648
        // Error
14649
        else if(ok && oResponse.error) {
14650
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14651
        }
14652
    }
14653
},
14654
 
14655
/**
14656
 * Callback function receives data from DataSource and incrementally updates Records
14657
 * starting at the index specified in oPayload.updateIndex. The value for
14658
 * oPayload.updateIndex can be populated when sending the request to the DataSource,
14659
 * or by accessing oPayload.updateIndex with the doBeforeLoadData() method at runtime.
14660
 * If applicable, creates or updates corresponding TR elements.
14661
 *
14662
 * @method onDataReturnUpdateRows
14663
 * @param sRequest {String} Original request.
14664
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14665
 * @param oPayload {MIXED} Argument payload, looks in oPayload.updateIndex.
14666
 */
14667
onDataReturnUpdateRows : function(sRequest, oResponse, oPayload) {
14668
    if((this instanceof DT) && this._sId) {
14669
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
14670
 
14671
        // Pass data through abstract method for any transformations
14672
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
14673
 
14674
        // Data ok to append
14675
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14676
            // Insert rows
14677
            this.updateRows((oPayload ? oPayload.updateIndex : 0), oResponse.results);
14678
 
14679
            // Update state
14680
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
14681
        }
14682
        // Error
14683
        else if(ok && oResponse.error) {
14684
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14685
        }
14686
    }
14687
},
14688
 
14689
/**
14690
 * Callback function receives reponse from DataSource and populates the
14691
 * RecordSet with the results.
14692
 *
14693
 * @method onDataReturnSetRows
14694
 * @param oRequest {MIXED} Original generated request.
14695
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14696
 * @param oPayload {MIXED} (optional) Additional argument(s)
14697
 */
14698
onDataReturnSetRows : function(oRequest, oResponse, oPayload) {
14699
    if((this instanceof DT) && this._sId) {
14700
        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
14701
 
14702
        // Pass data through abstract method for any transformations
14703
        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
14704
            pag   = this.get('paginator'),
14705
            index = 0;
14706
 
14707
        // Data ok to set
14708
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
14709
            // Update Records
14710
            if (this.get('dynamicData')) {
14711
                if (oPayload && oPayload.pagination &&
14712
                    lang.isNumber(oPayload.pagination.recordOffset)) {
14713
                    index = oPayload.pagination.recordOffset;
14714
                } else if (pag) {
14715
                    index = pag.getStartIndex();
14716
                }
14717
 
14718
                this._oRecordSet.reset(); // Bug 2290604: dyanmic data shouldn't keep accumulating by default
14719
            }
14720
 
14721
            this._oRecordSet.setRecords(oResponse.results, index | 0);
14722
 
14723
            // Update state
14724
            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
14725
 
14726
            // Update UI
14727
            this.render();
14728
        }
14729
        // Error
14730
        else if(ok && oResponse.error) {
14731
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
14732
        }
14733
    }
14734
    else {
14735
    }
14736
},
14737
 
14738
/**
14739
 * Hook to update oPayload before consumption.
14740
 *
14741
 * @method handleDataReturnPayload
14742
 * @param oRequest {MIXED} Original generated request.
14743
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14744
 * @param oPayload {MIXED} State values.
14745
 * @return oPayload {MIXED} State values.
14746
 */
14747
handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
14748
    return oPayload || {};
14749
},
14750
 
14751
/**
14752
 * Updates the DataTable with state data sent in an onDataReturn* payload.
14753
 *
14754
 * @method _handleDataReturnPayload
14755
 * @param oRequest {MIXED} Original generated request.
14756
 * @param oResponse {Object} <a href="http://developer.yahoo.com/yui/datasource/#ds_oParsedResponse">Response object</a>.
14757
 * @param oPayload {MIXED} State values
14758
 * @private
14759
 */
14760
_handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
14761
    oPayload = this.handleDataReturnPayload(oRequest, oResponse, oPayload);
14762
    if(oPayload) {
14763
        // Update pagination
14764
        var oPaginator = this.get('paginator');
14765
        if (oPaginator) {
14766
            // Update totalRecords
14767
            if(this.get("dynamicData")) {
14768
                if (widget.Paginator.isNumeric(oPayload.totalRecords)) {
14769
                    oPaginator.set('totalRecords',oPayload.totalRecords);
14770
                }
14771
            }
14772
            else {
14773
                oPaginator.set('totalRecords',this._oRecordSet.getLength());
14774
            }
14775
            // Update other paginator values
14776
            if (lang.isObject(oPayload.pagination)) {
14777
                oPaginator.set('rowsPerPage',oPayload.pagination.rowsPerPage);
14778
                oPaginator.set('recordOffset',oPayload.pagination.recordOffset);
14779
            }
14780
        }
14781
 
14782
        // Update sorting
14783
        if (oPayload.sortedBy) {
14784
            // Set the sorting values in preparation for refresh
14785
            this.set('sortedBy', oPayload.sortedBy);
14786
        }
14787
        // Backwards compatibility for sorting
14788
        else if (oPayload.sorting) {
14789
            // Set the sorting values in preparation for refresh
14790
            this.set('sortedBy', oPayload.sorting);
14791
        }
14792
    }
14793
},
14794
 
14795
 
14796
 
14797
 
14798
 
14799
 
14800
 
14801
 
14802
 
14803
 
14804
 
14805
 
14806
 
14807
 
14808
 
14809
 
14810
 
14811
 
14812
 
14813
 
14814
 
14815
 
14816
 
14817
 
14818
 
14819
 
14820
 
14821
 
14822
 
14823
 
14824
 
14825
 
14826
 
14827
    /////////////////////////////////////////////////////////////////////////////
14828
    //
14829
    // Custom Events
14830
    //
14831
    /////////////////////////////////////////////////////////////////////////////
14832
 
14833
    /**
14834
     * Fired when the DataTable's rows are rendered from an initialized state.
14835
     *
14836
     * @event initEvent
14837
     */
14838
 
14839
    /**
14840
     * Fired before the DataTable's DOM is rendered or modified.
14841
     *
14842
     * @event beforeRenderEvent
14843
     */
14844
 
14845
    /**
14846
     * Fired when the DataTable's DOM is rendered or modified.
14847
     *
14848
     * @event renderEvent
14849
     */
14850
 
14851
    /**
14852
     * Fired when the DataTable's post-render routine is complete, including
14853
     * Column width validations.
14854
     *
14855
     * @event postRenderEvent
14856
     */
14857
 
14858
    /**
14859
     * Fired when the DataTable is disabled.
14860
     *
14861
     * @event disableEvent
14862
     */
14863
 
14864
    /**
14865
     * Fired when the DataTable is undisabled.
14866
     *
14867
     * @event undisableEvent
14868
     */
14869
 
14870
    /**
14871
     * Fired when data is returned from DataSource but before it is consumed by
14872
     * DataTable.
14873
     *
14874
     * @event dataReturnEvent
14875
     * @param oArgs.request {String} Original request.
14876
     * @param oArgs.response {Object} Response object.
14877
     */
14878
 
14879
    /**
14880
     * Fired when the DataTable has a focus event.
14881
     *
14882
     * @event tableFocusEvent
14883
     */
14884
 
14885
    /**
14886
     * Fired when the DataTable THEAD element has a focus event.
14887
     *
14888
     * @event theadFocusEvent
14889
     */
14890
 
14891
    /**
14892
     * Fired when the DataTable TBODY element has a focus event.
14893
     *
14894
     * @event tbodyFocusEvent
14895
     */
14896
 
14897
    /**
14898
     * Fired when the DataTable has a blur event.
14899
     *
14900
     * @event tableBlurEvent
14901
     */
14902
 
14903
    /*TODO implement theadBlurEvent
14904
     * Fired when the DataTable THEAD element has a blur event.
14905
     *
14906
     * @event theadBlurEvent
14907
     */
14908
 
14909
    /*TODO: implement tbodyBlurEvent
14910
     * Fired when the DataTable TBODY element has a blur event.
14911
     *
14912
     * @event tbodyBlurEvent
14913
     */
14914
 
14915
    /**
14916
     * Fired when the DataTable has a key event.
14917
     *
14918
     * @event tableKeyEvent
14919
     * @param oArgs.event {HTMLEvent} The event object.
14920
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14921
     */
14922
 
14923
    /**
14924
     * Fired when the DataTable THEAD element has a key event.
14925
     *
14926
     * @event theadKeyEvent
14927
     * @param oArgs.event {HTMLEvent} The event object.
14928
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14929
     */
14930
 
14931
    /**
14932
     * Fired when the DataTable TBODY element has a key event.
14933
     *
14934
     * @event tbodyKeyEvent
14935
     * @param oArgs.event {HTMLEvent} The event object.
14936
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14937
     */
14938
 
14939
    /**
14940
     * Fired when the DataTable has a mouseover.
14941
     *
14942
     * @event tableMouseoverEvent
14943
     * @param oArgs.event {HTMLEvent} The event object.
14944
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14945
     *
14946
     */
14947
 
14948
    /**
14949
     * Fired when the DataTable has a mouseout.
14950
     *
14951
     * @event tableMouseoutEvent
14952
     * @param oArgs.event {HTMLEvent} The event object.
14953
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14954
     *
14955
     */
14956
 
14957
    /**
14958
     * Fired when the DataTable has a mousedown.
14959
     *
14960
     * @event tableMousedownEvent
14961
     * @param oArgs.event {HTMLEvent} The event object.
14962
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14963
     *
14964
     */
14965
 
14966
    /**
14967
     * Fired when the DataTable has a mouseup.
14968
     *
14969
     * @event tableMouseupEvent
14970
     * @param oArgs.event {HTMLEvent} The event object.
14971
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14972
     *
14973
     */
14974
 
14975
    /**
14976
     * Fired when the DataTable has a click.
14977
     *
14978
     * @event tableClickEvent
14979
     * @param oArgs.event {HTMLEvent} The event object.
14980
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14981
     *
14982
     */
14983
 
14984
    /**
14985
     * Fired when the DataTable has a dblclick.
14986
     *
14987
     * @event tableDblclickEvent
14988
     * @param oArgs.event {HTMLEvent} The event object.
14989
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
14990
     *
14991
     */
14992
 
14993
    /**
14994
     * Fired when a message is shown in the DataTable's message element.
14995
     *
14996
     * @event tableMsgShowEvent
14997
     * @param oArgs.html {HTML} The HTML displayed.
14998
     * @param oArgs.className {String} The className assigned.
14999
     *
15000
     */
15001
 
15002
    /**
15003
     * Fired when the DataTable's message element is hidden.
15004
     *
15005
     * @event tableMsgHideEvent
15006
     */
15007
 
15008
    /**
15009
     * Fired when a THEAD row has a mouseover.
15010
     *
15011
     * @event theadRowMouseoverEvent
15012
     * @param oArgs.event {HTMLEvent} The event object.
15013
     * @param oArgs.target {HTMLElement} The TR element.
15014
     */
15015
 
15016
    /**
15017
     * Fired when a THEAD row has a mouseout.
15018
     *
15019
     * @event theadRowMouseoutEvent
15020
     * @param oArgs.event {HTMLEvent} The event object.
15021
     * @param oArgs.target {HTMLElement} The TR element.
15022
     */
15023
 
15024
    /**
15025
     * Fired when a THEAD row has a mousedown.
15026
     *
15027
     * @event theadRowMousedownEvent
15028
     * @param oArgs.event {HTMLEvent} The event object.
15029
     * @param oArgs.target {HTMLElement} The TR element.
15030
     */
15031
 
15032
    /**
15033
     * Fired when a THEAD row has a mouseup.
15034
     *
15035
     * @event theadRowMouseupEvent
15036
     * @param oArgs.event {HTMLEvent} The event object.
15037
     * @param oArgs.target {HTMLElement} The TR element.
15038
     */
15039
 
15040
    /**
15041
     * Fired when a THEAD row has a click.
15042
     *
15043
     * @event theadRowClickEvent
15044
     * @param oArgs.event {HTMLEvent} The event object.
15045
     * @param oArgs.target {HTMLElement} The TR element.
15046
     */
15047
 
15048
    /**
15049
     * Fired when a THEAD row has a dblclick.
15050
     *
15051
     * @event theadRowDblclickEvent
15052
     * @param oArgs.event {HTMLEvent} The event object.
15053
     * @param oArgs.target {HTMLElement} The TR element.
15054
     */
15055
 
15056
    /**
15057
     * Fired when a THEAD cell has a mouseover.
15058
     *
15059
     * @event theadCellMouseoverEvent
15060
     * @param oArgs.event {HTMLEvent} The event object.
15061
     * @param oArgs.target {HTMLElement} The TH element.
15062
     *
15063
     */
15064
 
15065
    /**
15066
     * Fired when a THEAD cell has a mouseout.
15067
     *
15068
     * @event theadCellMouseoutEvent
15069
     * @param oArgs.event {HTMLEvent} The event object.
15070
     * @param oArgs.target {HTMLElement} The TH element.
15071
     *
15072
     */
15073
 
15074
    /**
15075
     * Fired when a THEAD cell has a mousedown.
15076
     *
15077
     * @event theadCellMousedownEvent
15078
     * @param oArgs.event {HTMLEvent} The event object.
15079
     * @param oArgs.target {HTMLElement} The TH element.
15080
     */
15081
 
15082
    /**
15083
     * Fired when a THEAD cell has a mouseup.
15084
     *
15085
     * @event theadCellMouseupEvent
15086
     * @param oArgs.event {HTMLEvent} The event object.
15087
     * @param oArgs.target {HTMLElement} The TH element.
15088
     */
15089
 
15090
    /**
15091
     * Fired when a THEAD cell has a click.
15092
     *
15093
     * @event theadCellClickEvent
15094
     * @param oArgs.event {HTMLEvent} The event object.
15095
     * @param oArgs.target {HTMLElement} The TH element.
15096
     */
15097
 
15098
    /**
15099
     * Fired when a THEAD cell has a dblclick.
15100
     *
15101
     * @event theadCellDblclickEvent
15102
     * @param oArgs.event {HTMLEvent} The event object.
15103
     * @param oArgs.target {HTMLElement} The TH element.
15104
     */
15105
 
15106
    /**
15107
     * Fired when a THEAD label has a mouseover.
15108
     *
15109
     * @event theadLabelMouseoverEvent
15110
     * @param oArgs.event {HTMLEvent} The event object.
15111
     * @param oArgs.target {HTMLElement} The SPAN element.
15112
     *
15113
     */
15114
 
15115
    /**
15116
     * Fired when a THEAD label has a mouseout.
15117
     *
15118
     * @event theadLabelMouseoutEvent
15119
     * @param oArgs.event {HTMLEvent} The event object.
15120
     * @param oArgs.target {HTMLElement} The SPAN element.
15121
     *
15122
     */
15123
 
15124
    /**
15125
     * Fired when a THEAD label has a mousedown.
15126
     *
15127
     * @event theadLabelMousedownEvent
15128
     * @param oArgs.event {HTMLEvent} The event object.
15129
     * @param oArgs.target {HTMLElement} The SPAN element.
15130
     */
15131
 
15132
    /**
15133
     * Fired when a THEAD label has a mouseup.
15134
     *
15135
     * @event theadLabelMouseupEvent
15136
     * @param oArgs.event {HTMLEvent} The event object.
15137
     * @param oArgs.target {HTMLElement} The SPAN element.
15138
     */
15139
 
15140
    /**
15141
     * Fired when a THEAD label has a click.
15142
     *
15143
     * @event theadLabelClickEvent
15144
     * @param oArgs.event {HTMLEvent} The event object.
15145
     * @param oArgs.target {HTMLElement} The SPAN element.
15146
     */
15147
 
15148
    /**
15149
     * Fired when a THEAD label has a dblclick.
15150
     *
15151
     * @event theadLabelDblclickEvent
15152
     * @param oArgs.event {HTMLEvent} The event object.
15153
     * @param oArgs.target {HTMLElement} The SPAN element.
15154
     */
15155
 
15156
    /**
15157
     * Fired when a column is sorted.
15158
     *
15159
     * @event columnSortEvent
15160
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15161
     * @param oArgs.dir {String} Sort direction: YAHOO.widget.DataTable.CLASS_ASC
15162
     * or YAHOO.widget.DataTable.CLASS_DESC.
15163
     */
15164
 
15165
    /**
15166
     * Fired when a column width is set.
15167
     *
15168
     * @event columnSetWidthEvent
15169
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15170
     * @param oArgs.width {Number} The width in pixels.
15171
     */
15172
 
15173
    /**
15174
     * Fired when a column width is unset.
15175
     *
15176
     * @event columnUnsetWidthEvent
15177
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15178
     */
15179
 
15180
    /**
15181
     * Fired when a column is drag-resized.
15182
     *
15183
     * @event columnResizeEvent
15184
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15185
     * @param oArgs.target {HTMLElement} The TH element.
15186
     * @param oArgs.width {Number} Width in pixels.
15187
     */
15188
 
15189
    /**
15190
     * Fired when a Column is moved to a new index.
15191
     *
15192
     * @event columnReorderEvent
15193
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15194
     * @param oArgs.oldIndex {Number} The previous tree index position.
15195
     */
15196
 
15197
    /**
15198
     * Fired when a column is hidden.
15199
     *
15200
     * @event columnHideEvent
15201
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15202
     */
15203
 
15204
    /**
15205
     * Fired when a column is shown.
15206
     *
15207
     * @event columnShowEvent
15208
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15209
     */
15210
 
15211
    /**
15212
     * Fired when a column is selected.
15213
     *
15214
     * @event columnSelectEvent
15215
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15216
     */
15217
 
15218
    /**
15219
     * Fired when a column is unselected.
15220
     *
15221
     * @event columnUnselectEvent
15222
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15223
     */
15224
    /**
15225
     * Fired when a column is removed.
15226
     *
15227
     * @event columnRemoveEvent
15228
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15229
     */
15230
 
15231
    /**
15232
     * Fired when a column is inserted.
15233
     *
15234
     * @event columnInsertEvent
15235
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
15236
     * @param oArgs.index {Number} The index position.
15237
     */
15238
 
15239
    /**
15240
     * Fired when a column is highlighted.
15241
     *
15242
     * @event columnHighlightEvent
15243
     * @param oArgs.column {YAHOO.widget.Column} The highlighted Column.
15244
     */
15245
 
15246
    /**
15247
     * Fired when a column is unhighlighted.
15248
     *
15249
     * @event columnUnhighlightEvent
15250
     * @param oArgs.column {YAHOO.widget.Column} The unhighlighted Column.
15251
     */
15252
 
15253
 
15254
    /**
15255
     * Fired when a row has a mouseover.
15256
     *
15257
     * @event rowMouseoverEvent
15258
     * @param oArgs.event {HTMLEvent} The event object.
15259
     * @param oArgs.target {HTMLElement} The TR element.
15260
     */
15261
 
15262
    /**
15263
     * Fired when a row has a mouseout.
15264
     *
15265
     * @event rowMouseoutEvent
15266
     * @param oArgs.event {HTMLEvent} The event object.
15267
     * @param oArgs.target {HTMLElement} The TR element.
15268
     */
15269
 
15270
    /**
15271
     * Fired when a row has a mousedown.
15272
     *
15273
     * @event rowMousedownEvent
15274
     * @param oArgs.event {HTMLEvent} The event object.
15275
     * @param oArgs.target {HTMLElement} The TR element.
15276
     */
15277
 
15278
    /**
15279
     * Fired when a row has a mouseup.
15280
     *
15281
     * @event rowMouseupEvent
15282
     * @param oArgs.event {HTMLEvent} The event object.
15283
     * @param oArgs.target {HTMLElement} The TR element.
15284
     */
15285
 
15286
    /**
15287
     * Fired when a row has a click.
15288
     *
15289
     * @event rowClickEvent
15290
     * @param oArgs.event {HTMLEvent} The event object.
15291
     * @param oArgs.target {HTMLElement} The TR element.
15292
     */
15293
 
15294
    /**
15295
     * Fired when a row has a dblclick.
15296
     *
15297
     * @event rowDblclickEvent
15298
     * @param oArgs.event {HTMLEvent} The event object.
15299
     * @param oArgs.target {HTMLElement} The TR element.
15300
     */
15301
 
15302
    /**
15303
     * Fired when a row is added.
15304
     *
15305
     * @event rowAddEvent
15306
     * @param oArgs.record {YAHOO.widget.Record} The added Record.
15307
     */
15308
 
15309
    /**
15310
     * Fired when rows are added.
15311
     *
15312
     * @event rowsAddEvent
15313
     * @param oArgs.record {YAHOO.widget.Record[]} The added Records.
15314
     */
15315
 
15316
    /**
15317
     * Fired when a row is updated.
15318
     *
15319
     * @event rowUpdateEvent
15320
     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
15321
     * @param oArgs.oldData {Object} Object literal of the old data.
15322
     */
15323
 
15324
    /**
15325
     * Fired when a row is deleted.
15326
     *
15327
     * @event rowDeleteEvent
15328
     * @param oArgs.oldData {Object} Object literal of the deleted data.
15329
     * @param oArgs.recordIndex {Number} Index of the deleted Record.
15330
     * @param oArgs.trElIndex {Number} Index of the deleted TR element, if on current page.
15331
     */
15332
 
15333
    /**
15334
     * Fired when rows are deleted.
15335
     *
15336
     * @event rowsDeleteEvent
15337
     * @param oArgs.oldData {Object[]} Array of object literals of the deleted data.
15338
     * @param oArgs.recordIndex {Number} Index of the first deleted Record.
15339
     * @param oArgs.count {Number} Number of deleted Records.
15340
     */
15341
 
15342
    /**
15343
     * Fired when a row is selected.
15344
     *
15345
     * @event rowSelectEvent
15346
     * @param oArgs.el {HTMLElement} The selected TR element, if applicable.
15347
     * @param oArgs.record {YAHOO.widget.Record} The selected Record.
15348
     */
15349
 
15350
    /**
15351
     * Fired when a row is unselected.
15352
     *
15353
     * @event rowUnselectEvent
15354
     * @param oArgs.el {HTMLElement} The unselected TR element, if applicable.
15355
     * @param oArgs.record {YAHOO.widget.Record} The unselected Record.
15356
     */
15357
 
15358
    /**
15359
     * Fired when all row selections are cleared.
15360
     *
15361
     * @event unselectAllRowsEvent
15362
     */
15363
 
15364
    /**
15365
     * Fired when a row is highlighted.
15366
     *
15367
     * @event rowHighlightEvent
15368
     * @param oArgs.el {HTMLElement} The highlighted TR element.
15369
     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
15370
     */
15371
 
15372
    /**
15373
     * Fired when a row is unhighlighted.
15374
     *
15375
     * @event rowUnhighlightEvent
15376
     * @param oArgs.el {HTMLElement} The highlighted TR element.
15377
     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
15378
     */
15379
 
15380
    /**
15381
     * Fired when a cell is updated.
15382
     *
15383
     * @event cellUpdateEvent
15384
     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
15385
     * @param oArgs.column {YAHOO.widget.Column} The updated Column.
15386
     * @param oArgs.oldData {Object} Original data value of the updated cell.
15387
     */
15388
 
15389
    /**
15390
     * Fired when a cell has a mouseover.
15391
     *
15392
     * @event cellMouseoverEvent
15393
     * @param oArgs.event {HTMLEvent} The event object.
15394
     * @param oArgs.target {HTMLElement} The TD element.
15395
     */
15396
 
15397
    /**
15398
     * Fired when a cell has a mouseout.
15399
     *
15400
     * @event cellMouseoutEvent
15401
     * @param oArgs.event {HTMLEvent} The event object.
15402
     * @param oArgs.target {HTMLElement} The TD element.
15403
     */
15404
 
15405
    /**
15406
     * Fired when a cell has a mousedown.
15407
     *
15408
     * @event cellMousedownEvent
15409
     * @param oArgs.event {HTMLEvent} The event object.
15410
     * @param oArgs.target {HTMLElement} The TD element.
15411
     */
15412
 
15413
    /**
15414
     * Fired when a cell has a mouseup.
15415
     *
15416
     * @event cellMouseupEvent
15417
     * @param oArgs.event {HTMLEvent} The event object.
15418
     * @param oArgs.target {HTMLElement} The TD element.
15419
     */
15420
 
15421
    /**
15422
     * Fired when a cell has a click.
15423
     *
15424
     * @event cellClickEvent
15425
     * @param oArgs.event {HTMLEvent} The event object.
15426
     * @param oArgs.target {HTMLElement} The TD element.
15427
     */
15428
 
15429
    /**
15430
     * Fired when a cell has a dblclick.
15431
     *
15432
     * @event cellDblclickEvent
15433
     * @param oArgs.event {HTMLEvent} The event object.
15434
     * @param oArgs.target {HTMLElement} The TD element.
15435
     */
15436
 
15437
    /**
15438
     * Fired when a cell is formatted.
15439
     *
15440
     * @event cellFormatEvent
15441
     * @param oArgs.el {HTMLElement} The formatted TD element.
15442
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15443
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15444
     * @param oArgs.key {String} (deprecated) The key of the formatted cell.
15445
     */
15446
 
15447
    /**
15448
     * Fired when a cell is selected.
15449
     *
15450
     * @event cellSelectEvent
15451
     * @param oArgs.el {HTMLElement} The selected TD element.
15452
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15453
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15454
     * @param oArgs.key {String} (deprecated) The key of the selected cell.
15455
     */
15456
 
15457
    /**
15458
     * Fired when a cell is unselected.
15459
     *
15460
     * @event cellUnselectEvent
15461
     * @param oArgs.el {HTMLElement} The unselected TD element.
15462
     * @param oArgs.record {YAHOO.widget.Record} The associated Record.
15463
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15464
     * @param oArgs.key {String} (deprecated) The key of the unselected cell.
15465
 
15466
     */
15467
 
15468
    /**
15469
     * Fired when a cell is highlighted.
15470
     *
15471
     * @event cellHighlightEvent
15472
     * @param oArgs.el {HTMLElement} The highlighted TD element.
15473
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15474
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15475
     * @param oArgs.key {String} (deprecated) The key of the highlighted cell.
15476
 
15477
     */
15478
 
15479
    /**
15480
     * Fired when a cell is unhighlighted.
15481
     *
15482
     * @event cellUnhighlightEvent
15483
     * @param oArgs.el {HTMLElement} The unhighlighted TD element.
15484
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
15485
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
15486
     * @param oArgs.key {String} (deprecated) The key of the unhighlighted cell.
15487
 
15488
     */
15489
 
15490
    /**
15491
     * Fired when all cell selections are cleared.
15492
     *
15493
     * @event unselectAllCellsEvent
15494
     */
15495
 
15496
    /**
15497
     * Fired when a CellEditor is shown.
15498
     *
15499
     * @event editorShowEvent
15500
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15501
     */
15502
 
15503
    /**
15504
     * Fired when a CellEditor has a keydown.
15505
     *
15506
     * @event editorKeydownEvent
15507
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15508
     * @param oArgs.event {HTMLEvent} The event object.
15509
     */
15510
 
15511
    /**
15512
     * Fired when a CellEditor input is reverted.
15513
     *
15514
     * @event editorRevertEvent
15515
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15516
     * @param oArgs.newData {Object} New data value from form input field.
15517
     * @param oArgs.oldData {Object} Old data value.
15518
     */
15519
 
15520
    /**
15521
     * Fired when a CellEditor input is saved.
15522
     *
15523
     * @event editorSaveEvent
15524
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15525
     * @param oArgs.newData {Object} New data value from form input field.
15526
     * @param oArgs.oldData {Object} Old data value.
15527
     */
15528
 
15529
    /**
15530
     * Fired when a CellEditor input is canceled.
15531
     *
15532
     * @event editorCancelEvent
15533
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15534
     */
15535
 
15536
    /**
15537
     * Fired when a CellEditor has a blur event.
15538
     *
15539
     * @event editorBlurEvent
15540
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15541
     */
15542
 
15543
    /**
15544
     * Fired when a CellEditor is blocked.
15545
     *
15546
     * @event editorBlockEvent
15547
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15548
     */
15549
 
15550
    /**
15551
     * Fired when a CellEditor is unblocked.
15552
     *
15553
     * @event editorUnblockEvent
15554
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
15555
     */
15556
 
15557
 
15558
 
15559
 
15560
 
15561
    /**
15562
     * Fired when a link is clicked.
15563
     *
15564
     * @event linkClickEvent
15565
     * @param oArgs.event {HTMLEvent} The event object.
15566
     * @param oArgs.target {HTMLElement} The A element.
15567
     */
15568
 
15569
    /**
15570
     * Fired when a BUTTON element or INPUT element of type "button", "image",
15571
     * "submit", "reset" is clicked.
15572
     *
15573
     * @event buttonClickEvent
15574
     * @param oArgs.event {HTMLEvent} The event object.
15575
     * @param oArgs.target {HTMLElement} The BUTTON element.
15576
     */
15577
 
15578
    /**
15579
     * Fired when a CHECKBOX element is clicked.
15580
     *
15581
     * @event checkboxClickEvent
15582
     * @param oArgs.event {HTMLEvent} The event object.
15583
     * @param oArgs.target {HTMLElement} The CHECKBOX element.
15584
     */
15585
 
15586
    /**
15587
     * Fired when a SELECT element is changed.
15588
     *
15589
     * @event dropdownChangeEvent
15590
     * @param oArgs.event {HTMLEvent} The event object.
15591
     * @param oArgs.target {HTMLElement} The SELECT element.
15592
     */
15593
 
15594
    /**
15595
     * Fired when a RADIO element is clicked.
15596
     *
15597
     * @event radioClickEvent
15598
     * @param oArgs.event {HTMLEvent} The event object.
15599
     * @param oArgs.target {HTMLElement} The RADIO element.
15600
     */
15601
 
15602
 
15603
 
15604
 
15605
 
15606
 
15607
 
15608
 
15609
 
15610
 
15611
 
15612
 
15613
 
15614
 
15615
 
15616
 
15617
 
15618
 
15619
 
15620
 
15621
 
15622
 
15623
 
15624
 
15625
 
15626
 
15627
/////////////////////////////////////////////////////////////////////////////
15628
//
15629
// Deprecated APIs
15630
//
15631
/////////////////////////////////////////////////////////////////////////////
15632
 
15633
/*
15634
 * @method showCellEditorBtns
15635
 * @deprecated Use CellEditor.renderBtns()
15636
 */
15637
showCellEditorBtns : function(elContainer) {
15638
    // Buttons
15639
    var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
15640
    Dom.addClass(elBtnsDiv, DT.CLASS_BUTTON);
15641
 
15642
    // Save button
15643
    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
15644
    Dom.addClass(elSaveBtn, DT.CLASS_DEFAULT);
15645
    elSaveBtn.innerHTML = "OK";
15646
    Ev.addListener(elSaveBtn, "click", function(oArgs, oSelf) {
15647
        oSelf.onEventSaveCellEditor(oArgs, oSelf);
15648
        oSelf.focusTbodyEl();
15649
    }, this, true);
15650
 
15651
    // Cancel button
15652
    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
15653
    elCancelBtn.innerHTML = "Cancel";
15654
    Ev.addListener(elCancelBtn, "click", function(oArgs, oSelf) {
15655
        oSelf.onEventCancelCellEditor(oArgs, oSelf);
15656
        oSelf.focusTbodyEl();
15657
    }, this, true);
15658
 
15659
},
15660
 
15661
/**
15662
 * @method resetCellEditor
15663
 * @deprecated Use destroyCellEditor
15664
 */
15665
resetCellEditor : function() {
15666
    var elContainer = this._oCellEditor.container;
15667
    elContainer.style.display = "none";
15668
    Ev.purgeElement(elContainer, true);
15669
    elContainer.innerHTML = "";
15670
    this._oCellEditor.value = null;
15671
    this._oCellEditor.isActive = false;
15672
 
15673
},
15674
 
15675
/**
15676
 * @event editorUpdateEvent
15677
 * @deprecated Use CellEditor class.
15678
 */
15679
 
15680
/**
15681
 * @method getBody
15682
 * @deprecated Use getTbodyEl().
15683
 */
15684
getBody : function() {
15685
    // Backward compatibility
15686
    return this.getTbodyEl();
15687
},
15688
 
15689
/**
15690
 * @method getCell
15691
 * @deprecated Use getTdEl().
15692
 */
15693
getCell : function(index) {
15694
    // Backward compatibility
15695
    return this.getTdEl(index);
15696
},
15697
 
15698
/**
15699
 * @method getRow
15700
 * @deprecated Use getTrEl().
15701
 */
15702
getRow : function(index) {
15703
    // Backward compatibility
15704
    return this.getTrEl(index);
15705
},
15706
 
15707
/**
15708
 * @method refreshView
15709
 * @deprecated Use render.
15710
 */
15711
refreshView : function() {
15712
    // Backward compatibility
15713
    this.render();
15714
},
15715
 
15716
/**
15717
 * @method select
15718
 * @deprecated Use selectRow.
15719
 */
15720
select : function(els) {
15721
    // Backward compatibility
15722
    if(!lang.isArray(els)) {
15723
        els = [els];
15724
    }
15725
    for(var i=0; i<els.length; i++) {
15726
        this.selectRow(els[i]);
15727
    }
15728
},
15729
 
15730
/**
15731
 * @method onEventEditCell
15732
 * @deprecated Use onEventShowCellEditor.
15733
 */
15734
onEventEditCell : function(oArgs) {
15735
    // Backward compatibility
15736
    this.onEventShowCellEditor(oArgs);
15737
},
15738
 
15739
/**
15740
 * @method _syncColWidths
15741
 * @deprecated Use validateColumnWidths.
15742
 */
15743
_syncColWidths : function() {
15744
    // Backward compatibility
15745
    this.validateColumnWidths();
15746
}
15747
 
15748
/**
15749
 * @event headerRowMouseoverEvent
15750
 * @deprecated Use theadRowMouseoverEvent.
15751
 */
15752
 
15753
/**
15754
 * @event headerRowMouseoutEvent
15755
 * @deprecated Use theadRowMouseoutEvent.
15756
 */
15757
 
15758
/**
15759
 * @event headerRowMousedownEvent
15760
 * @deprecated Use theadRowMousedownEvent.
15761
 */
15762
 
15763
/**
15764
 * @event headerRowClickEvent
15765
 * @deprecated Use theadRowClickEvent.
15766
 */
15767
 
15768
/**
15769
 * @event headerRowDblclickEvent
15770
 * @deprecated Use theadRowDblclickEvent.
15771
 */
15772
 
15773
/**
15774
 * @event headerCellMouseoverEvent
15775
 * @deprecated Use theadCellMouseoverEvent.
15776
 */
15777
 
15778
/**
15779
 * @event headerCellMouseoutEvent
15780
 * @deprecated Use theadCellMouseoutEvent.
15781
 */
15782
 
15783
/**
15784
 * @event headerCellMousedownEvent
15785
 * @deprecated Use theadCellMousedownEvent.
15786
 */
15787
 
15788
/**
15789
 * @event headerCellClickEvent
15790
 * @deprecated Use theadCellClickEvent.
15791
 */
15792
 
15793
/**
15794
 * @event headerCellDblclickEvent
15795
 * @deprecated Use theadCellDblclickEvent.
15796
 */
15797
 
15798
/**
15799
 * @event headerLabelMouseoverEvent
15800
 * @deprecated Use theadLabelMouseoverEvent.
15801
 */
15802
 
15803
/**
15804
 * @event headerLabelMouseoutEvent
15805
 * @deprecated Use theadLabelMouseoutEvent.
15806
 */
15807
 
15808
/**
15809
 * @event headerLabelMousedownEvent
15810
 * @deprecated Use theadLabelMousedownEvent.
15811
 */
15812
 
15813
/**
15814
 * @event headerLabelClickEvent
15815
 * @deprecated Use theadLabelClickEvent.
15816
 */
15817
 
15818
/**
15819
 * @event headerLabelDbllickEvent
15820
 * @deprecated Use theadLabelDblclickEvent.
15821
 */
15822
 
15823
});
15824
 
15825
/**
15826
 * Alias for onDataReturnSetRows for backward compatibility
15827
 * @method onDataReturnSetRecords
15828
 * @deprecated Use onDataReturnSetRows
15829
 */
15830
DT.prototype.onDataReturnSetRecords = DT.prototype.onDataReturnSetRows;
15831
 
15832
/**
15833
 * Alias for onPaginatorChange for backward compatibility
15834
 * @method onPaginatorChange
15835
 * @deprecated Use onPaginatorChangeRequest
15836
 */
15837
DT.prototype.onPaginatorChange = DT.prototype.onPaginatorChangeRequest;
15838
 
15839
/////////////////////////////////////////////////////////////////////////////
15840
//
15841
// Deprecated static APIs
15842
//
15843
/////////////////////////////////////////////////////////////////////////////
15844
/**
15845
 * @method DataTable.editCheckbox
15846
 * @deprecated  Use YAHOO.widget.CheckboxCellEditor.
15847
 */
15848
DT.editCheckbox = function() {};
15849
 
15850
/**
15851
 * @method DataTable.editDate
15852
 * @deprecated Use YAHOO.widget.DateCellEditor.
15853
 */
15854
DT.editDate = function() {};
15855
 
15856
/**
15857
 * @method DataTable.editDropdown
15858
 * @deprecated Use YAHOO.widget.DropdownCellEditor.
15859
 */
15860
DT.editDropdown = function() {};
15861
 
15862
/**
15863
 * @method DataTable.editRadio
15864
 * @deprecated Use YAHOO.widget.RadioCellEditor.
15865
 */
15866
DT.editRadio = function() {};
15867
 
15868
/**
15869
 * @method DataTable.editTextarea
15870
 * @deprecated Use YAHOO.widget.TextareaCellEditor
15871
 */
15872
DT.editTextarea = function() {};
15873
 
15874
/**
15875
 * @method DataTable.editTextbox
15876
 * @deprecated Use YAHOO.widget.TextboxCellEditor
15877
 */
15878
DT.editTextbox= function() {};
15879
 
15880
})();
15881
 
15882
(function () {
15883
 
15884
var lang   = YAHOO.lang,
15885
    util   = YAHOO.util,
15886
    widget = YAHOO.widget,
15887
    ua     = YAHOO.env.ua,
15888
 
15889
    Dom    = util.Dom,
15890
    Ev     = util.Event,
15891
    DS     = util.DataSourceBase,
15892
    DT     = widget.DataTable,
15893
    Pag    = widget.Paginator;
15894
 
15895
/**
15896
 * The ScrollingDataTable class extends the DataTable class to provide
15897
 * functionality for x-scrolling, y-scrolling, and xy-scrolling.
15898
 *
15899
 * @namespace YAHOO.widget
15900
 * @class ScrollingDataTable
15901
 * @extends YAHOO.widget.DataTable
15902
 * @constructor
15903
 * @param elContainer {HTMLElement} Container element for the TABLE.
15904
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
15905
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
15906
 * @param oConfigs {object} (optional) Object literal of configuration values.
15907
 */
15908
widget.ScrollingDataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
15909
    oConfigs = oConfigs || {};
15910
 
15911
    // Prevent infinite loop
15912
    if(oConfigs.scrollable) {
15913
        oConfigs.scrollable = false;
15914
    }
15915
 
15916
    this._init();
15917
 
15918
    widget.ScrollingDataTable.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs);
15919
 
15920
    // Once per instance
15921
    this.subscribe("columnShowEvent", this._onColumnChange);
15922
};
15923
 
15924
var SDT = widget.ScrollingDataTable;
15925
 
15926
/////////////////////////////////////////////////////////////////////////////
15927
//
15928
// Public constants
15929
//
15930
/////////////////////////////////////////////////////////////////////////////
15931
lang.augmentObject(SDT, {
15932
 
15933
    /**
15934
     * Class name assigned to inner DataTable header container.
15935
     *
15936
     * @property DataTable.CLASS_HEADER
15937
     * @type String
15938
     * @static
15939
     * @final
15940
     * @default "yui-dt-hd"
15941
     */
15942
    CLASS_HEADER : "yui-dt-hd",
15943
 
15944
    /**
15945
     * Class name assigned to inner DataTable body container.
15946
     *
15947
     * @property DataTable.CLASS_BODY
15948
     * @type String
15949
     * @static
15950
     * @final
15951
     * @default "yui-dt-bd"
15952
     */
15953
    CLASS_BODY : "yui-dt-bd"
15954
});
15955
 
15956
lang.extend(SDT, DT, {
15957
 
15958
/**
15959
 * Container for fixed header TABLE element.
15960
 *
15961
 * @property _elHdContainer
15962
 * @type HTMLElement
15963
 * @private
15964
 */
15965
_elHdContainer : null,
15966
 
15967
/**
15968
 * Fixed header TABLE element.
15969
 *
15970
 * @property _elHdTable
15971
 * @type HTMLElement
15972
 * @private
15973
 */
15974
_elHdTable : null,
15975
 
15976
/**
15977
 * Container for scrolling body TABLE element.
15978
 *
15979
 * @property _elBdContainer
15980
 * @type HTMLElement
15981
 * @private
15982
 */
15983
_elBdContainer : null,
15984
 
15985
/**
15986
 * Body THEAD element.
15987
 *
15988
 * @property _elBdThead
15989
 * @type HTMLElement
15990
 * @private
15991
 */
15992
_elBdThead : null,
15993
 
15994
/**
15995
 * Offscreen container to temporarily clone SDT for auto-width calculation.
15996
 *
15997
 * @property _elTmpContainer
15998
 * @type HTMLElement
15999
 * @private
16000
 */
16001
_elTmpContainer : null,
16002
 
16003
/**
16004
 * Offscreen TABLE element for auto-width calculation.
16005
 *
16006
 * @property _elTmpTable
16007
 * @type HTMLElement
16008
 * @private
16009
 */
16010
_elTmpTable : null,
16011
 
16012
/**
16013
 * True if x-scrollbar is currently visible.
16014
 * @property _bScrollbarX
16015
 * @type Boolean
16016
 * @private
16017
 */
16018
_bScrollbarX : null,
16019
 
16020
 
16021
 
16022
 
16023
 
16024
 
16025
 
16026
 
16027
 
16028
 
16029
 
16030
 
16031
 
16032
 
16033
 
16034
/////////////////////////////////////////////////////////////////////////////
16035
//
16036
// Superclass methods
16037
//
16038
/////////////////////////////////////////////////////////////////////////////
16039
 
16040
/**
16041
 * Implementation of Element's abstract method. Sets up config values.
16042
 *
16043
 * @method initAttributes
16044
 * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
16045
 * @private
16046
 */
16047
 
16048
initAttributes : function(oConfigs) {
16049
    oConfigs = oConfigs || {};
16050
    SDT.superclass.initAttributes.call(this, oConfigs);
16051
 
16052
    /**
16053
    * @attribute width
16054
    * @description Table width for scrollable tables (e.g., "40em").
16055
    * @type String
16056
    */
16057
    this.setAttributeConfig("width", {
16058
        value: null,
16059
        validator: lang.isString,
16060
        method: function(oParam) {
16061
            if(this._elHdContainer && this._elBdContainer) {
16062
                this._elHdContainer.style.width = oParam;
16063
                this._elBdContainer.style.width = oParam;
16064
                this._syncScrollX();
16065
                this._syncScrollOverhang();
16066
            }
16067
        }
16068
    });
16069
 
16070
    /**
16071
    * @attribute height
16072
    * @description Table body height for scrollable tables, not including headers (e.g., "40em").
16073
    * @type String
16074
    */
16075
    this.setAttributeConfig("height", {
16076
        value: null,
16077
        validator: lang.isString,
16078
        method: function(oParam) {
16079
            if(this._elHdContainer && this._elBdContainer) {
16080
                this._elBdContainer.style.height = oParam;
16081
                this._syncScrollX();
16082
                this._syncScrollY();
16083
                this._syncScrollOverhang();
16084
            }
16085
        }
16086
    });
16087
 
16088
    /**
16089
    * @attribute COLOR_COLUMNFILLER
16090
    * @description CSS color value assigned to header filler on scrollable tables.
16091
    * @type String
16092
    * @default "#F2F2F2"
16093
    */
16094
    this.setAttributeConfig("COLOR_COLUMNFILLER", {
16095
        value: "#F2F2F2",
16096
        validator: lang.isString,
16097
        method: function(oParam) {
16098
            if(this._elHdContainer) {
16099
                this._elHdContainer.style.backgroundColor = oParam;
16100
            }
16101
        }
16102
    });
16103
},
16104
 
16105
/**
16106
 * Initializes internal variables.
16107
 *
16108
 * @method _init
16109
 * @private
16110
 */
16111
_init : function() {
16112
    this._elHdContainer = null;
16113
    this._elHdTable = null;
16114
    this._elBdContainer = null;
16115
    this._elBdThead = null;
16116
    this._elTmpContainer = null;
16117
    this._elTmpTable = null;
16118
},
16119
 
16120
/**
16121
 * Initializes DOM elements for a ScrollingDataTable, including creation of
16122
 * two separate TABLE elements.
16123
 *
16124
 * @method _initDomElements
16125
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
16126
 * return {Boolean} False in case of error, otherwise true
16127
 * @private
16128
 */
16129
_initDomElements : function(elContainer) {
16130
    // Outer and inner containers
16131
    this._initContainerEl(elContainer);
16132
    if(this._elContainer && this._elHdContainer && this._elBdContainer) {
16133
        // TABLEs
16134
        this._initTableEl();
16135
 
16136
        if(this._elHdTable && this._elTable) {
16137
            // COLGROUPs
16138
            ///this._initColgroupEl(this._elHdTable, this._elTable);
16139
            this._initColgroupEl(this._elHdTable);
16140
 
16141
            // THEADs
16142
            this._initTheadEl(this._elHdTable, this._elTable);
16143
 
16144
            // Primary TBODY
16145
            this._initTbodyEl(this._elTable);
16146
            // Message TBODY
16147
            this._initMsgTbodyEl(this._elTable);
16148
        }
16149
    }
16150
    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody ||
16151
            !this._elHdTable || !this._elBdThead) {
16152
        return false;
16153
    }
16154
    else {
16155
        return true;
16156
    }
16157
},
16158
 
16159
/**
16160
 * Destroy's the DataTable outer and inner container elements, if available.
16161
 *
16162
 * @method _destroyContainerEl
16163
 * @param elContainer {HTMLElement} Reference to the container element.
16164
 * @private
16165
 */
16166
_destroyContainerEl : function(elContainer) {
16167
    Dom.removeClass(elContainer, DT.CLASS_SCROLLABLE);
16168
    SDT.superclass._destroyContainerEl.call(this, elContainer);
16169
    this._elHdContainer = null;
16170
    this._elBdContainer = null;
16171
},
16172
 
16173
/**
16174
 * Initializes the DataTable outer container element and creates inner header
16175
 * and body container elements.
16176
 *
16177
 * @method _initContainerEl
16178
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
16179
 * @private
16180
 */
16181
_initContainerEl : function(elContainer) {
16182
    SDT.superclass._initContainerEl.call(this, elContainer);
16183
 
16184
    if(this._elContainer) {
16185
        elContainer = this._elContainer; // was constructor input, now is DOM ref
16186
        Dom.addClass(elContainer, DT.CLASS_SCROLLABLE);
16187
 
16188
        // Container for header TABLE
16189
        var elHdContainer = document.createElement("div");
16190
        elHdContainer.style.width = this.get("width") || "";
16191
        elHdContainer.style.backgroundColor = this.get("COLOR_COLUMNFILLER");
16192
        Dom.addClass(elHdContainer, SDT.CLASS_HEADER);
16193
        this._elHdContainer = elHdContainer;
16194
        elContainer.appendChild(elHdContainer);
16195
 
16196
        // Container for body TABLE
16197
        var elBdContainer = document.createElement("div");
16198
        elBdContainer.style.width = this.get("width") || "";
16199
        elBdContainer.style.height = this.get("height") || "";
16200
        Dom.addClass(elBdContainer, SDT.CLASS_BODY);
16201
        Ev.addListener(elBdContainer, "scroll", this._onScroll, this); // to sync horiz scroll headers
16202
        this._elBdContainer = elBdContainer;
16203
        elContainer.appendChild(elBdContainer);
16204
    }
16205
},
16206
 
16207
/**
16208
 * Creates HTML markup CAPTION element.
16209
 *
16210
 * @method _initCaptionEl
16211
 * @param sCaption {String} Text for caption.
16212
 * @private
16213
 */
16214
_initCaptionEl : function(sCaption) {
16215
    // Not yet supported
16216
    /*if(this._elHdTable && sCaption) {
16217
        // Create CAPTION element
16218
        if(!this._elCaption) {
16219
            this._elCaption = this._elHdTable.createCaption();
16220
        }
16221
        // Set CAPTION value
16222
        this._elCaption.innerHTML = sCaption;
16223
    }
16224
    else if(this._elCaption) {
16225
        this._elCaption.parentNode.removeChild(this._elCaption);
16226
    }*/
16227
},
16228
 
16229
/**
16230
 * Destroy's the DataTable head TABLE element, if available.
16231
 *
16232
 * @method _destroyHdTableEl
16233
 * @private
16234
 */
16235
_destroyHdTableEl : function() {
16236
    var elTable = this._elHdTable;
16237
    if(elTable) {
16238
        Ev.purgeElement(elTable, true);
16239
        elTable.parentNode.removeChild(elTable);
16240
 
16241
        // A little out of place, but where else can we null out these extra elements?
16242
        ///this._elBdColgroup = null;
16243
        this._elBdThead = null;
16244
    }
16245
},
16246
 
16247
/**
16248
 * Initializes ScrollingDataTable TABLE elements into the two inner containers.
16249
 *
16250
 * @method _initTableEl
16251
 * @private
16252
 */
16253
_initTableEl : function() {
16254
    // Head TABLE
16255
    if(this._elHdContainer) {
16256
        this._destroyHdTableEl();
16257
 
16258
        // Create TABLE
16259
        this._elHdTable = this._elHdContainer.appendChild(document.createElement("table"));
16260
 
16261
        // Set up mouseover/mouseout events via mouseenter/mouseleave delegation
16262
        Ev.delegate(this._elHdTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this);
16263
        Ev.delegate(this._elHdTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this);
16264
    }
16265
    // Body TABLE
16266
    SDT.superclass._initTableEl.call(this, this._elBdContainer);
16267
},
16268
 
16269
/**
16270
 * Initializes ScrollingDataTable THEAD elements into the two inner containers.
16271
 *
16272
 * @method _initTheadEl
16273
 * @param elHdTable {HTMLElement} (optional) Fixed header TABLE element reference.
16274
 * @param elTable {HTMLElement} (optional) TABLE element reference.
16275
 * @private
16276
 */
16277
_initTheadEl : function(elHdTable, elTable) {
16278
    elHdTable = elHdTable || this._elHdTable;
16279
    elTable = elTable || this._elTable;
16280
 
16281
    // Scrolling body's THEAD
16282
    this._initBdTheadEl(elTable);
16283
    // Standard fixed head THEAD
16284
    SDT.superclass._initTheadEl.call(this, elHdTable);
16285
},
16286
 
16287
/**
16288
 * SDT changes ID so as not to duplicate the accessibility TH IDs.
16289
 *
16290
 * @method _initThEl
16291
 * @param elTh {HTMLElement} TH element reference.
16292
 * @param oColumn {YAHOO.widget.Column} Column object.
16293
 * @private
16294
 */
16295
_initThEl : function(elTh, oColumn) {
16296
    SDT.superclass._initThEl.call(this, elTh, oColumn);
16297
    elTh.id = this.getId() +"-fixedth-" + oColumn.getSanitizedKey(); // Needed for getColumn by TH and ColumnDD
16298
},
16299
 
16300
/**
16301
 * Destroy's the DataTable body THEAD element, if available.
16302
 *
16303
 * @method _destroyBdTheadEl
16304
 * @private
16305
 */
16306
_destroyBdTheadEl : function() {
16307
    var elBdThead = this._elBdThead;
16308
    if(elBdThead) {
16309
        var elTable = elBdThead.parentNode;
16310
        Ev.purgeElement(elBdThead, true);
16311
        elTable.removeChild(elBdThead);
16312
        this._elBdThead = null;
16313
 
16314
        this._destroyColumnHelpers();
16315
    }
16316
},
16317
 
16318
/**
16319
 * Initializes body THEAD element.
16320
 *
16321
 * @method _initBdTheadEl
16322
 * @param elTable {HTMLElement} TABLE element into which to create THEAD.
16323
 * @return {HTMLElement} Initialized THEAD element.
16324
 * @private
16325
 */
16326
_initBdTheadEl : function(elTable) {
16327
    if(elTable) {
16328
        // Destroy previous
16329
        this._destroyBdTheadEl();
16330
 
16331
        var elThead = elTable.insertBefore(document.createElement("thead"), elTable.firstChild);
16332
 
16333
        // Add TRs to the THEAD;
16334
        var oColumnSet = this._oColumnSet,
16335
            colTree = oColumnSet.tree,
16336
            elTh, elTheadTr, oColumn, i, j, k, len;
16337
 
16338
        for(i=0, k=colTree.length; i<k; i++) {
16339
            elTheadTr = elThead.appendChild(document.createElement("tr"));
16340
 
16341
            // ...and create TH cells
16342
            for(j=0, len=colTree[i].length; j<len; j++) {
16343
                oColumn = colTree[i][j];
16344
                elTh = elTheadTr.appendChild(document.createElement("th"));
16345
                this._initBdThEl(elTh,oColumn,i,j);
16346
            }
16347
        }
16348
        this._elBdThead = elThead;
16349
    }
16350
},
16351
 
16352
/**
16353
 * Populates TH element for the body THEAD element.
16354
 *
16355
 * @method _initBdThEl
16356
 * @param elTh {HTMLElement} TH element reference.
16357
 * @param oColumn {YAHOO.widget.Column} Column object.
16358
 * @private
16359
 */
16360
_initBdThEl : function(elTh, oColumn) {
16361
    elTh.id = this.getId()+"-th-" + oColumn.getSanitizedKey(); // Needed for accessibility
16362
    elTh.rowSpan = oColumn.getRowspan();
16363
    elTh.colSpan = oColumn.getColspan();
16364
    // Assign abbr attribute
16365
    if(oColumn.abbr) {
16366
        elTh.abbr = oColumn.abbr;
16367
    }
16368
 
16369
    // TODO: strip links and form elements
16370
    var sKey = oColumn.getKey();
16371
    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
16372
    elTh.innerHTML = sLabel;
16373
},
16374
 
16375
/**
16376
 * Initializes ScrollingDataTable TBODY element for data
16377
 *
16378
 * @method _initTbodyEl
16379
 * @param elTable {HTMLElement} TABLE element into which to create TBODY .
16380
 * @private
16381
 */
16382
_initTbodyEl : function(elTable) {
16383
    SDT.superclass._initTbodyEl.call(this, elTable);
16384
 
16385
    // Bug 2105534 - Safari 3 gap
16386
    // Bug 2492591 - IE8 offsetTop
16387
    elTable.style.marginTop = (this._elTbody.offsetTop > 0) ?
16388
            "-"+this._elTbody.offsetTop+"px" : 0;
16389
},
16390
 
16391
 
16392
 
16393
 
16394
 
16395
 
16396
 
16397
 
16398
 
16399
 
16400
 
16401
 
16402
 
16403
 
16404
 
16405
 
16406
 
16407
 
16408
 
16409
 
16410
 
16411
 
16412
 
16413
 
16414
 
16415
 
16416
 
16417
 
16418
 
16419
/**
16420
 * Sets focus on the given element.
16421
 *
16422
 * @method _focusEl
16423
 * @param el {HTMLElement} Element.
16424
 * @private
16425
 */
16426
_focusEl : function(el) {
16427
    el = el || this._elTbody;
16428
    var oSelf = this;
16429
    this._storeScrollPositions();
16430
    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
16431
    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
16432
    // strange unexpected things as the user clicks on buttons and other controls.
16433
 
16434
    // Bug 1921135: Wrap the whole thing in a setTimeout
16435
    setTimeout(function() {
16436
        setTimeout(function() {
16437
            try {
16438
                el.focus();
16439
                oSelf._restoreScrollPositions();
16440
            }
16441
            catch(e) {
16442
            }
16443
        },0);
16444
    }, 0);
16445
},
16446
 
16447
 
16448
 
16449
 
16450
 
16451
 
16452
 
16453
 
16454
 
16455
 
16456
 
16457
 
16458
 
16459
 
16460
 
16461
 
16462
 
16463
 
16464
 
16465
/**
16466
 * Internal wrapper calls run() on render Chain instance.
16467
 *
16468
 * @method _runRenderChain
16469
 * @private
16470
 */
16471
_runRenderChain : function() {
16472
    this._storeScrollPositions();
16473
    this._oChainRender.run();
16474
},
16475
 
16476
/**
16477
 * Stores scroll positions so they can be restored after a render.
16478
 *
16479
 * @method _storeScrollPositions
16480
 * @private
16481
 */
16482
 _storeScrollPositions : function() {
16483
    this._nScrollTop = this._elBdContainer.scrollTop;
16484
    this._nScrollLeft = this._elBdContainer.scrollLeft;
16485
},
16486
 
16487
/**
16488
 * Clears stored scroll positions to interrupt the automatic restore mechanism.
16489
 * Useful for setting scroll positions programmatically rather than as part of
16490
 * the post-render cleanup process.
16491
 *
16492
 * @method clearScrollPositions
16493
 * @private
16494
 */
16495
 clearScrollPositions : function() {
16496
    this._nScrollTop = 0;
16497
    this._nScrollLeft = 0;
16498
},
16499
 
16500
/**
16501
 * Restores scroll positions to stored value.
16502
 *
16503
 * @method _retoreScrollPositions
16504
 * @private
16505
 */
16506
 _restoreScrollPositions : function() {
16507
    // Reset scroll positions
16508
    if(this._nScrollTop) {
16509
        this._elBdContainer.scrollTop = this._nScrollTop;
16510
        this._nScrollTop = null;
16511
    }
16512
    if(this._nScrollLeft) {
16513
        this._elBdContainer.scrollLeft = this._nScrollLeft;
16514
        // Bug 2529024
16515
        this._elHdContainer.scrollLeft = this._nScrollLeft;
16516
        this._nScrollLeft = null;
16517
    }
16518
},
16519
 
16520
/**
16521
 * Helper function calculates and sets a validated width for a Column in a ScrollingDataTable.
16522
 *
16523
 * @method _validateColumnWidth
16524
 * @param oColumn {YAHOO.widget.Column} Column instance.
16525
 * @param elTd {HTMLElement} TD element to validate against.
16526
 * @private
16527
 */
16528
_validateColumnWidth : function(oColumn, elTd) {
16529
    // Only Columns without widths that are not hidden
16530
    if(!oColumn.width && !oColumn.hidden) {
16531
        var elTh = oColumn.getThEl();
16532
        // Unset a calculated auto-width
16533
        if(oColumn._calculatedWidth) {
16534
            this._setColumnWidth(oColumn, "auto", "visible");
16535
        }
16536
        // Compare auto-widths
16537
        if(elTh.offsetWidth !== elTd.offsetWidth) {
16538
            var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
16539
                    oColumn.getThLinerEl() : elTd.firstChild;
16540
 
16541
            // Grab the wider liner width, unless the minWidth is wider
16542
            var newWidth = Math.max(0,
16543
                (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
16544
                oColumn.minWidth);
16545
 
16546
            var sOverflow = 'visible';
16547
 
16548
            // Now validate against maxAutoWidth
16549
            if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
16550
                newWidth = oColumn.maxAutoWidth;
16551
                sOverflow = "hidden";
16552
            }
16553
 
16554
            // Set to the wider auto-width
16555
            this._elTbody.style.display = "none";
16556
            this._setColumnWidth(oColumn, newWidth+'px', sOverflow);
16557
            oColumn._calculatedWidth = newWidth;
16558
            this._elTbody.style.display = "";
16559
        }
16560
    }
16561
},
16562
 
16563
/**
16564
 * For one or all Columns of a ScrollingDataTable, when Column is not hidden,
16565
 * and width is not set, syncs widths of header and body cells and
16566
 * validates that width against minWidth and/or maxAutoWidth as necessary.
16567
 *
16568
 * @method validateColumnWidths
16569
 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
16570
 */
16571
validateColumnWidths : function(oColumn) {
16572
    // Validate there is at least one TR with proper TDs
16573
    var allKeys   = this._oColumnSet.keys,
16574
        allKeysLength = allKeys.length,
16575
        elRow     = this.getFirstTrEl();
16576
 
16577
    // Reset overhang for IE
16578
    if(ua.ie) {
16579
        this._setOverhangValue(1);
16580
    }
16581
 
16582
    if(allKeys && elRow && (elRow.childNodes.length === allKeysLength)) {
16583
        // Temporarily unsnap container since it causes inaccurate calculations
16584
        var sWidth = this.get("width");
16585
        if(sWidth) {
16586
            this._elHdContainer.style.width = "";
16587
            this._elBdContainer.style.width = "";
16588
        }
16589
        this._elContainer.style.width = "";
16590
 
16591
        //Validate just one Column
16592
        if(oColumn && lang.isNumber(oColumn.getKeyIndex())) {
16593
            this._validateColumnWidth(oColumn, elRow.childNodes[oColumn.getKeyIndex()]);
16594
        }
16595
        // Iterate through all Columns to unset calculated widths in one pass
16596
        else {
16597
            var elTd, todos = [], thisTodo, i, len;
16598
            for(i=0; i<allKeysLength; i++) {
16599
                oColumn = allKeys[i];
16600
                // Only Columns without widths that are not hidden, unset a calculated auto-width
16601
                if(!oColumn.width && !oColumn.hidden && oColumn._calculatedWidth) {
16602
                    todos[todos.length] = oColumn;
16603
                }
16604
            }
16605
 
16606
            this._elTbody.style.display = "none";
16607
            for(i=0, len=todos.length; i<len; i++) {
16608
                this._setColumnWidth(todos[i], "auto", "visible");
16609
            }
16610
            this._elTbody.style.display = "";
16611
 
16612
            todos = [];
16613
 
16614
            // Iterate through all Columns and make the store the adjustments to make in one pass
16615
            for(i=0; i<allKeysLength; i++) {
16616
                oColumn = allKeys[i];
16617
                elTd = elRow.childNodes[i];
16618
                // Only Columns without widths that are not hidden
16619
                if(!oColumn.width && !oColumn.hidden) {
16620
                    var elTh = oColumn.getThEl();
16621
 
16622
                    // Compare auto-widths
16623
                    if(elTh.offsetWidth !== elTd.offsetWidth) {
16624
                        var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
16625
                                oColumn.getThLinerEl() : elTd.firstChild;
16626
 
16627
                        // Grab the wider liner width, unless the minWidth is wider
16628
                        var newWidth = Math.max(0,
16629
                            (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
16630
                            oColumn.minWidth);
16631
 
16632
                        var sOverflow = 'visible';
16633
 
16634
                        // Now validate against maxAutoWidth
16635
                        if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
16636
                            newWidth = oColumn.maxAutoWidth;
16637
                            sOverflow = "hidden";
16638
                        }
16639
 
16640
                        todos[todos.length] = [oColumn, newWidth, sOverflow];
16641
                    }
16642
                }
16643
            }
16644
 
16645
            this._elTbody.style.display = "none";
16646
            for(i=0, len=todos.length; i<len; i++) {
16647
                thisTodo = todos[i];
16648
                // Set to the wider auto-width
16649
                this._setColumnWidth(thisTodo[0], thisTodo[1]+"px", thisTodo[2]);
16650
                thisTodo[0]._calculatedWidth = thisTodo[1];
16651
            }
16652
            this._elTbody.style.display = "";
16653
        }
16654
 
16655
        // Resnap unsnapped containers
16656
        if(sWidth) {
16657
            this._elHdContainer.style.width = sWidth;
16658
            this._elBdContainer.style.width = sWidth;
16659
        }
16660
    }
16661
 
16662
    this._syncScroll();
16663
    this._restoreScrollPositions();
16664
},
16665
 
16666
/**
16667
 * Syncs padding around scrollable tables, including Column header right-padding
16668
 * and container width and height.
16669
 *
16670
 * @method _syncScroll
16671
 * @private
16672
 */
16673
_syncScroll : function() {
16674
    this._syncScrollX();
16675
    this._syncScrollY();
16676
    this._syncScrollOverhang();
16677
    if(ua.opera) {
16678
        // Bug 1925874
16679
        this._elHdContainer.scrollLeft = this._elBdContainer.scrollLeft;
16680
        if(!this.get("width")) {
16681
            // Bug 1926125
16682
            document.body.style += '';
16683
        }
16684
    }
16685
 },
16686
 
16687
/**
16688
 * Snaps container width for y-scrolling tables.
16689
 *
16690
 * @method _syncScrollY
16691
 * @private
16692
 */
16693
_syncScrollY : function() {
16694
    var elTbody = this._elTbody,
16695
        elBdContainer = this._elBdContainer;
16696
 
16697
    // X-scrolling not enabled
16698
    if(!this.get("width")) {
16699
        // Snap outer container width to content
16700
        this._elContainer.style.width =
16701
                (elBdContainer.scrollHeight > elBdContainer.clientHeight) ?
16702
                // but account for y-scrollbar since it is visible
16703
                (elTbody.parentNode.clientWidth + 19) + "px" :
16704
                // no y-scrollbar, just borders
16705
                (elTbody.parentNode.clientWidth + 2) + "px";
16706
    }
16707
},
16708
 
16709
/**
16710
 * Snaps container height for x-scrolling tables in IE. Syncs message TBODY width.
16711
 *
16712
 * @method _syncScrollX
16713
 * @private
16714
 */
16715
_syncScrollX : function() {
16716
    var elTbody = this._elTbody,
16717
        elBdContainer = this._elBdContainer;
16718
 
16719
    // IE 6 and 7 only when y-scrolling not enabled
16720
    if(!this.get("height") && (ua.ie)) {
16721
        // Snap outer container height to content
16722
        elBdContainer.style.height =
16723
                // but account for x-scrollbar if it is visible
16724
                (elBdContainer.scrollWidth > elBdContainer.offsetWidth ) ?
16725
                (elTbody.parentNode.offsetHeight + 18) + "px" :
16726
                elTbody.parentNode.offsetHeight + "px";
16727
    }
16728
 
16729
    // Sync message tbody
16730
    if(this._elTbody.rows.length === 0) {
16731
        this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
16732
    }
16733
    else {
16734
        this._elMsgTbody.parentNode.style.width = "";
16735
    }
16736
},
16737
 
16738
/**
16739
 * Adds/removes Column header overhang as necesary.
16740
 *
16741
 * @method _syncScrollOverhang
16742
 * @private
16743
 */
16744
_syncScrollOverhang : function() {
16745
    var elBdContainer = this._elBdContainer,
16746
        // Overhang should be either 1 (default) or 18px, depending on the location of the right edge of the table
16747
        nPadding = 1;
16748
 
16749
    // Y-scrollbar is visible, which is when the overhang needs to jut out
16750
    if((elBdContainer.scrollHeight > elBdContainer.clientHeight) &&
16751
        // X-scrollbar is also visible, which means the right is jagged, not flush with the Column
16752
        (elBdContainer.scrollWidth > elBdContainer.clientWidth)) {
16753
        nPadding = 18;
16754
    }
16755
 
16756
    this._setOverhangValue(nPadding);
16757
 
16758
},
16759
 
16760
/**
16761
 * Sets Column header overhang to given width.
16762
 *
16763
 * @method _setOverhangValue
16764
 * @param nBorderWidth {Number} Value of new border for overhang.
16765
 * @private
16766
 */
16767
_setOverhangValue : function(nBorderWidth) {
16768
    var aLastHeaders = this._oColumnSet.headers[this._oColumnSet.headers.length-1] || [],
16769
        len = aLastHeaders.length,
16770
        sPrefix = this._sId+"-fixedth-",
16771
        sValue = nBorderWidth + "px solid " + this.get("COLOR_COLUMNFILLER");
16772
 
16773
    this._elThead.style.display = "none";
16774
    for(var i=0; i<len; i++) {
16775
        Dom.get(sPrefix+aLastHeaders[i]).style.borderRight = sValue;
16776
    }
16777
    this._elThead.style.display = "";
16778
},
16779
 
16780
 
16781
 
16782
 
16783
 
16784
 
16785
 
16786
 
16787
 
16788
 
16789
 
16790
 
16791
 
16792
 
16793
 
16794
 
16795
 
16796
 
16797
 
16798
 
16799
 
16800
 
16801
 
16802
 
16803
 
16804
 
16805
 
16806
 
16807
 
16808
 
16809
 
16810
 
16811
 
16812
 
16813
 
16814
 
16815
 
16816
 
16817
/**
16818
 * Returns DOM reference to the DataTable's fixed header container element.
16819
 *
16820
 * @method getHdContainerEl
16821
 * @return {HTMLElement} Reference to DIV element.
16822
 */
16823
getHdContainerEl : function() {
16824
    return this._elHdContainer;
16825
},
16826
 
16827
/**
16828
 * Returns DOM reference to the DataTable's scrolling body container element.
16829
 *
16830
 * @method getBdContainerEl
16831
 * @return {HTMLElement} Reference to DIV element.
16832
 */
16833
getBdContainerEl : function() {
16834
    return this._elBdContainer;
16835
},
16836
 
16837
/**
16838
 * Returns DOM reference to the DataTable's fixed header TABLE element.
16839
 *
16840
 * @method getHdTableEl
16841
 * @return {HTMLElement} Reference to TABLE element.
16842
 */
16843
getHdTableEl : function() {
16844
    return this._elHdTable;
16845
},
16846
 
16847
/**
16848
 * Returns DOM reference to the DataTable's scrolling body TABLE element.
16849
 *
16850
 * @method getBdTableEl
16851
 * @return {HTMLElement} Reference to TABLE element.
16852
 */
16853
getBdTableEl : function() {
16854
    return this._elTable;
16855
},
16856
 
16857
/**
16858
 * Disables ScrollingDataTable UI.
16859
 *
16860
 * @method disable
16861
 */
16862
disable : function() {
16863
    var elMask = this._elMask;
16864
    elMask.style.width = this._elBdContainer.offsetWidth + "px";
16865
    elMask.style.height = this._elHdContainer.offsetHeight + this._elBdContainer.offsetHeight + "px";
16866
    elMask.style.display = "";
16867
    this.fireEvent("disableEvent");
16868
},
16869
 
16870
/**
16871
 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
16872
 * non-nested Columns, and top-level parent Columns (which will remove all
16873
 * children Columns).
16874
 *
16875
 * @method removeColumn
16876
 * @param oColumn {YAHOO.widget.Column} Column instance.
16877
 * @return oColumn {YAHOO.widget.Column} Removed Column instance.
16878
 */
16879
removeColumn : function(oColumn) {
16880
    // Store scroll pos
16881
    var hdPos = this._elHdContainer.scrollLeft;
16882
    var bdPos = this._elBdContainer.scrollLeft;
16883
 
16884
    // Call superclass method
16885
    oColumn = SDT.superclass.removeColumn.call(this, oColumn);
16886
 
16887
    // Restore scroll pos
16888
    this._elHdContainer.scrollLeft = hdPos;
16889
    this._elBdContainer.scrollLeft = bdPos;
16890
 
16891
    return oColumn;
16892
},
16893
 
16894
/**
16895
 * Inserts given Column at the index if given, otherwise at the end. NOTE: You
16896
 * can only add non-nested Columns and top-level parent Columns. You cannot add
16897
 * a nested Column to an existing parent.
16898
 *
16899
 * @method insertColumn
16900
 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
16901
 * definition or a Column instance.
16902
 * @param index {Number} (optional) New tree index.
16903
 * @return oColumn {YAHOO.widget.Column} Inserted Column instance.
16904
 */
16905
insertColumn : function(oColumn, index) {
16906
    // Store scroll pos
16907
    var hdPos = this._elHdContainer.scrollLeft;
16908
    var bdPos = this._elBdContainer.scrollLeft;
16909
 
16910
    // Call superclass method
16911
    var oNewColumn = SDT.superclass.insertColumn.call(this, oColumn, index);
16912
 
16913
    // Restore scroll pos
16914
    this._elHdContainer.scrollLeft = hdPos;
16915
    this._elBdContainer.scrollLeft = bdPos;
16916
 
16917
    return oNewColumn;
16918
},
16919
 
16920
/**
16921
 * Removes given Column and inserts into given tree index. NOTE: You
16922
 * can only reorder non-nested Columns and top-level parent Columns. You cannot
16923
 * reorder a nested Column to an existing parent.
16924
 *
16925
 * @method reorderColumn
16926
 * @param oColumn {YAHOO.widget.Column} Column instance.
16927
 * @param index {Number} New tree index.
16928
 */
16929
reorderColumn : function(oColumn, index) {
16930
    // Store scroll pos
16931
    var hdPos = this._elHdContainer.scrollLeft;
16932
    var bdPos = this._elBdContainer.scrollLeft;
16933
 
16934
    // Call superclass method
16935
    var oNewColumn = SDT.superclass.reorderColumn.call(this, oColumn, index);
16936
 
16937
    // Restore scroll pos
16938
    this._elHdContainer.scrollLeft = hdPos;
16939
    this._elBdContainer.scrollLeft = bdPos;
16940
 
16941
    return oNewColumn;
16942
},
16943
 
16944
/**
16945
 * Sets given Column to given pixel width. If new width is less than minWidth
16946
 * width, sets to minWidth. Updates oColumn.width value.
16947
 *
16948
 * @method setColumnWidth
16949
 * @param oColumn {YAHOO.widget.Column} Column instance.
16950
 * @param nWidth {Number} New width in pixels.
16951
 */
16952
setColumnWidth : function(oColumn, nWidth) {
16953
    oColumn = this.getColumn(oColumn);
16954
    if(oColumn) {
16955
        this._storeScrollPositions();
16956
 
16957
        // Validate new width against minWidth
16958
        if(lang.isNumber(nWidth)) {
16959
            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
16960
 
16961
            // Save state
16962
            oColumn.width = nWidth;
16963
 
16964
            // Resize the DOM elements
16965
            this._setColumnWidth(oColumn, nWidth+"px");
16966
            this._syncScroll();
16967
 
16968
            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
16969
        }
16970
        // Unsets a width to auto-size
16971
        else if(nWidth === null) {
16972
            // Save state
16973
            oColumn.width = nWidth;
16974
 
16975
            // Resize the DOM elements
16976
            this._setColumnWidth(oColumn, "auto");
16977
            this.validateColumnWidths(oColumn);
16978
            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
16979
        }
16980
 
16981
        // Bug 2339454: resize then sort misaligment
16982
        this._clearTrTemplateEl();
16983
    }
16984
    else {
16985
    }
16986
},
16987
 
16988
/**
16989
 * Scrolls to given row or cell
16990
 *
16991
 * @method scrollTo
16992
 * @param to {YAHOO.widget.Record | HTMLElement } Itme to scroll to.
16993
 */
16994
scrollTo : function(to) {
16995
        var td = this.getTdEl(to);
16996
        if(td) {
16997
            this.clearScrollPositions();
16998
            this.getBdContainerEl().scrollLeft = td.offsetLeft;
16999
            this.getBdContainerEl().scrollTop = td.parentNode.offsetTop;
17000
        }
17001
        else {
17002
            var tr = this.getTrEl(to);
17003
            if(tr) {
17004
                this.clearScrollPositions();
17005
                this.getBdContainerEl().scrollTop = tr.offsetTop;
17006
            }
17007
        }
17008
},
17009
 
17010
/**
17011
 * Displays message within secondary TBODY.
17012
 *
17013
 * @method showTableMessage
17014
 * @param sHTML {String} (optional) Value for innerHTMlang.
17015
 * @param sClassName {String} (optional) Classname.
17016
 */
17017
showTableMessage : function(sHTML, sClassName) {
17018
    var elCell = this._elMsgTd;
17019
    if(lang.isString(sHTML)) {
17020
        elCell.firstChild.innerHTML = sHTML;
17021
    }
17022
    if(lang.isString(sClassName)) {
17023
        Dom.addClass(elCell.firstChild, sClassName);
17024
    }
17025
 
17026
    // Needed for SDT only
17027
    var elThead = this.getTheadEl();
17028
    var elTable = elThead.parentNode;
17029
    var newWidth = elTable.offsetWidth;
17030
    this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
17031
 
17032
    this._elMsgTbody.style.display = "";
17033
 
17034
    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
17035
},
17036
 
17037
 
17038
 
17039
 
17040
 
17041
 
17042
 
17043
 
17044
 
17045
 
17046
 
17047
 
17048
 
17049
/////////////////////////////////////////////////////////////////////////////
17050
//
17051
// Private Custom Event Handlers
17052
//
17053
/////////////////////////////////////////////////////////////////////////////
17054
 
17055
/**
17056
 * Handles Column mutations
17057
 *
17058
 * @method onColumnChange
17059
 * @param oArgs {Object} Custom Event data.
17060
 */
17061
_onColumnChange : function(oArg) {
17062
    // Figure out which Column changed
17063
    var oColumn = (oArg.column) ? oArg.column :
17064
            (oArg.editor) ? oArg.editor.column : null;
17065
    this._storeScrollPositions();
17066
    this.validateColumnWidths(oColumn);
17067
},
17068
 
17069
 
17070
 
17071
 
17072
 
17073
 
17074
 
17075
 
17076
 
17077
 
17078
 
17079
 
17080
 
17081
 
17082
 
17083
/////////////////////////////////////////////////////////////////////////////
17084
//
17085
// Private DOM Event Handlers
17086
//
17087
/////////////////////////////////////////////////////////////////////////////
17088
 
17089
/**
17090
 * Syncs scrolltop and scrollleft of all TABLEs.
17091
 *
17092
 * @method _onScroll
17093
 * @param e {HTMLEvent} The scroll event.
17094
 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
17095
 * @private
17096
 */
17097
_onScroll : function(e, oSelf) {
17098
    oSelf._elHdContainer.scrollLeft = oSelf._elBdContainer.scrollLeft;
17099
 
17100
    if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
17101
        oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
17102
        oSelf.cancelCellEditor();
17103
    }
17104
 
17105
    var elTarget = Ev.getTarget(e);
17106
    var elTag = elTarget.nodeName.toLowerCase();
17107
    oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget});
17108
},
17109
 
17110
/**
17111
 * Handles keydown events on the THEAD element.
17112
 *
17113
 * @method _onTheadKeydown
17114
 * @param e {HTMLEvent} The key event.
17115
 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
17116
 * @private
17117
 */
17118
_onTheadKeydown : function(e, oSelf) {
17119
    // If tabbing to next TH label link causes THEAD to scroll,
17120
    // need to sync scrollLeft with TBODY
17121
    if(Ev.getCharCode(e) === 9) {
17122
        setTimeout(function() {
17123
            if((oSelf instanceof SDT) && oSelf._sId) {
17124
                oSelf._elBdContainer.scrollLeft = oSelf._elHdContainer.scrollLeft;
17125
            }
17126
        },0);
17127
    }
17128
 
17129
    var elTarget = Ev.getTarget(e);
17130
    var elTag = elTarget.nodeName.toLowerCase();
17131
    var bKeepBubbling = true;
17132
    while(elTarget && (elTag != "table")) {
17133
        switch(elTag) {
17134
            case "body":
17135
                return;
17136
            case "input":
17137
            case "textarea":
17138
                // TODO: implement textareaKeyEvent
17139
                break;
17140
            case "thead":
17141
                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
17142
                break;
17143
            default:
17144
                break;
17145
        }
17146
        if(bKeepBubbling === false) {
17147
            return;
17148
        }
17149
        else {
17150
            elTarget = elTarget.parentNode;
17151
            if(elTarget) {
17152
                elTag = elTarget.nodeName.toLowerCase();
17153
            }
17154
        }
17155
    }
17156
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
17157
}
17158
 
17159
 
17160
 
17161
 
17162
/**
17163
 * Fired when a fixed scrolling DataTable has a scroll.
17164
 *
17165
 * @event tableScrollEvent
17166
 * @param oArgs.event {HTMLEvent} The event object.
17167
 * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE)
17168
 * or the DataTable's TBODY element (everyone else).
17169
 *
17170
 */
17171
 
17172
 
17173
 
17174
 
17175
});
17176
 
17177
})();
17178
 
17179
(function () {
17180
 
17181
var lang   = YAHOO.lang,
17182
    util   = YAHOO.util,
17183
    widget = YAHOO.widget,
17184
    ua     = YAHOO.env.ua,
17185
 
17186
    Dom    = util.Dom,
17187
    Ev     = util.Event,
17188
 
17189
    DT     = widget.DataTable;
17190
/****************************************************************************/
17191
/****************************************************************************/
17192
/****************************************************************************/
17193
 
17194
/**
17195
 * The BaseCellEditor class provides base functionality common to all inline cell
17196
 * editors for a DataTable widget.
17197
 *
17198
 * @namespace YAHOO.widget
17199
 * @class BaseCellEditor
17200
 * @uses YAHOO.util.EventProvider
17201
 * @constructor
17202
 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
17203
 * @param oConfigs {Object} (Optional) Object literal of configs.
17204
 */
17205
widget.BaseCellEditor = function(sType, oConfigs) {
17206
    this._sId = this._sId || Dom.generateId(null, "yui-ceditor"); // "yui-ceditor" + YAHOO.widget.BaseCellEditor._nCount++;
17207
    YAHOO.widget.BaseCellEditor._nCount++;
17208
    this._sType = sType;
17209
 
17210
    // Validate inputs
17211
    this._initConfigs(oConfigs);
17212
 
17213
    // Create Custom Events
17214
    this._initEvents();
17215
 
17216
    // UI needs to be drawn
17217
    this._needsRender = true;
17218
};
17219
 
17220
var BCE = widget.BaseCellEditor;
17221
 
17222
/////////////////////////////////////////////////////////////////////////////
17223
//
17224
// Static members
17225
//
17226
/////////////////////////////////////////////////////////////////////////////
17227
lang.augmentObject(BCE, {
17228
 
17229
/**
17230
 * Global instance counter.
17231
 *
17232
 * @property CellEditor._nCount
17233
 * @type Number
17234
 * @static
17235
 * @default 0
17236
 * @private
17237
 */
17238
_nCount : 0,
17239
 
17240
/**
17241
 * Class applied to CellEditor container.
17242
 *
17243
 * @property CellEditor.CLASS_CELLEDITOR
17244
 * @type String
17245
 * @static
17246
 * @default "yui-ceditor"
17247
 */
17248
CLASS_CELLEDITOR : "yui-ceditor"
17249
 
17250
});
17251
 
17252
BCE.prototype = {
17253
/////////////////////////////////////////////////////////////////////////////
17254
//
17255
// Private members
17256
//
17257
/////////////////////////////////////////////////////////////////////////////
17258
/**
17259
 * Unique id assigned to instance "yui-ceditorN", useful prefix for generating unique
17260
 * DOM ID strings and log messages.
17261
 *
17262
 * @property _sId
17263
 * @type String
17264
 * @private
17265
 */
17266
_sId : null,
17267
 
17268
/**
17269
 * Editor type.
17270
 *
17271
 * @property _sType
17272
 * @type String
17273
 * @private
17274
 */
17275
_sType : null,
17276
 
17277
/**
17278
 * DataTable instance.
17279
 *
17280
 * @property _oDataTable
17281
 * @type YAHOO.widget.DataTable
17282
 * @private
17283
 */
17284
_oDataTable : null,
17285
 
17286
/**
17287
 * Column instance.
17288
 *
17289
 * @property _oColumn
17290
 * @type YAHOO.widget.Column
17291
 * @default null
17292
 * @private
17293
 */
17294
_oColumn : null,
17295
 
17296
/**
17297
 * Record instance.
17298
 *
17299
 * @property _oRecord
17300
 * @type YAHOO.widget.Record
17301
 * @default null
17302
 * @private
17303
 */
17304
_oRecord : null,
17305
 
17306
/**
17307
 * TD element.
17308
 *
17309
 * @property _elTd
17310
 * @type HTMLElement
17311
 * @default null
17312
 * @private
17313
 */
17314
_elTd : null,
17315
 
17316
/**
17317
 * Container for inline editor.
17318
 *
17319
 * @property _elContainer
17320
 * @type HTMLElement
17321
 * @private
17322
 */
17323
_elContainer : null,
17324
 
17325
/**
17326
 * Reference to Cancel button, if available.
17327
 *
17328
 * @property _elCancelBtn
17329
 * @type HTMLElement
17330
 * @default null
17331
 * @private
17332
 */
17333
_elCancelBtn : null,
17334
 
17335
/**
17336
 * Reference to Save button, if available.
17337
 *
17338
 * @property _elSaveBtn
17339
 * @type HTMLElement
17340
 * @default null
17341
 * @private
17342
 */
17343
_elSaveBtn : null,
17344
 
17345
 
17346
 
17347
 
17348
 
17349
 
17350
 
17351
 
17352
/////////////////////////////////////////////////////////////////////////////
17353
//
17354
// Private methods
17355
//
17356
/////////////////////////////////////////////////////////////////////////////
17357
 
17358
/**
17359
 * Initialize configs.
17360
 *
17361
 * @method _initConfigs
17362
 * @private
17363
 */
17364
_initConfigs : function(oConfigs) {
17365
    // Object literal defines CellEditor configs
17366
    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
17367
        for(var sConfig in oConfigs) {
17368
            if(sConfig) {
17369
                this[sConfig] = oConfigs[sConfig];
17370
            }
17371
        }
17372
    }
17373
},
17374
 
17375
/**
17376
 * Initialize Custom Events.
17377
 *
17378
 * @method _initEvents
17379
 * @private
17380
 */
17381
_initEvents : function() {
17382
    this.createEvent("showEvent");
17383
    this.createEvent("keydownEvent");
17384
    this.createEvent("invalidDataEvent");
17385
    this.createEvent("revertEvent");
17386
    this.createEvent("saveEvent");
17387
    this.createEvent("cancelEvent");
17388
    this.createEvent("blurEvent");
17389
    this.createEvent("blockEvent");
17390
    this.createEvent("unblockEvent");
17391
},
17392
 
17393
/**
17394
 * Initialize container element.
17395
 *
17396
 * @method _initContainerEl
17397
 * @private
17398
 */
17399
_initContainerEl : function() {
17400
    if(this._elContainer) {
17401
        YAHOO.util.Event.purgeElement(this._elContainer, true);
17402
        this._elContainer.innerHTML = "";
17403
    }
17404
 
17405
    var elContainer = document.createElement("div");
17406
    elContainer.id = this.getId() + "-container"; // Needed for tracking blur event
17407
    elContainer.style.display = "none";
17408
    elContainer.tabIndex = 0;
17409
 
17410
    this.className = lang.isArray(this.className) ? this.className : this.className ? [this.className] : [];
17411
    this.className[this.className.length] = DT.CLASS_EDITOR;
17412
    elContainer.className = this.className.join(" ");
17413
 
17414
    document.body.insertBefore(elContainer, document.body.firstChild);
17415
    this._elContainer = elContainer;
17416
},
17417
 
17418
/**
17419
 * Initialize container shim element.
17420
 *
17421
 * @method _initShimEl
17422
 * @private
17423
 */
17424
_initShimEl : function() {
17425
    // Iframe shim
17426
    if(this.useIFrame) {
17427
        if(!this._elIFrame) {
17428
            var elIFrame = document.createElement("iframe");
17429
            elIFrame.src = "javascript:false";
17430
            elIFrame.frameBorder = 0;
17431
            elIFrame.scrolling = "no";
17432
            elIFrame.style.display = "none";
17433
            elIFrame.className = DT.CLASS_EDITOR_SHIM;
17434
            elIFrame.tabIndex = -1;
17435
            elIFrame.role = "presentation";
17436
            elIFrame.title = "Presentational iframe shim";
17437
            document.body.insertBefore(elIFrame, document.body.firstChild);
17438
            this._elIFrame = elIFrame;
17439
        }
17440
    }
17441
},
17442
 
17443
/**
17444
 * Hides CellEditor UI at end of interaction.
17445
 *
17446
 * @method _hide
17447
 */
17448
_hide : function() {
17449
    this.getContainerEl().style.display = "none";
17450
    if(this._elIFrame) {
17451
        this._elIFrame.style.display = "none";
17452
    }
17453
    this.isActive = false;
17454
    this.getDataTable()._oCellEditor =  null;
17455
},
17456
 
17457
 
17458
 
17459
 
17460
 
17461
 
17462
 
17463
 
17464
 
17465
 
17466
 
17467
/////////////////////////////////////////////////////////////////////////////
17468
//
17469
// Public properties
17470
//
17471
/////////////////////////////////////////////////////////////////////////////
17472
/**
17473
 * Implementer defined function that can submit the input value to a server. This
17474
 * function must accept the arguments fnCallback and oNewValue. When the submission
17475
 * is complete, the function must also call fnCallback(bSuccess, oNewValue) to
17476
 * finish the save routine in the CellEditor. This function can also be used to
17477
 * perform extra validation or input value manipulation.
17478
 *
17479
 * @property asyncSubmitter
17480
 * @type HTMLFunction
17481
 */
17482
asyncSubmitter : null,
17483
 
17484
/**
17485
 * Current value.
17486
 *
17487
 * @property value
17488
 * @type MIXED
17489
 */
17490
value : null,
17491
 
17492
/**
17493
 * Default value in case Record data is undefined. NB: Null values will not trigger
17494
 * the default value.
17495
 *
17496
 * @property defaultValue
17497
 * @type MIXED
17498
 * @default null
17499
 */
17500
defaultValue : null,
17501
 
17502
/**
17503
 * Validator function for input data, called from the DataTable instance scope,
17504
 * receives the arguments (inputValue, currentValue, editorInstance) and returns
17505
 * either the validated (or type-converted) value or undefined.
17506
 *
17507
 * @property validator
17508
 * @type HTMLFunction
17509
 * @default null
17510
 */
17511
validator : null,
17512
 
17513
/**
17514
 * If validation is enabled, resets input field of invalid data.
17515
 *
17516
 * @property resetInvalidData
17517
 * @type Boolean
17518
 * @default true
17519
 */
17520
resetInvalidData : true,
17521
 
17522
/**
17523
 * True if currently active.
17524
 *
17525
 * @property isActive
17526
 * @type Boolean
17527
 */
17528
isActive : false,
17529
 
17530
/**
17531
 * Text to display on Save button.
17532
 *
17533
 * @property LABEL_SAVE
17534
 * @type HTML
17535
 * @default "Save"
17536
 */
17537
LABEL_SAVE : "Save",
17538
 
17539
/**
17540
 * Text to display on Cancel button.
17541
 *
17542
 * @property LABEL_CANCEL
17543
 * @type HTML
17544
 * @default "Cancel"
17545
 */
17546
LABEL_CANCEL : "Cancel",
17547
 
17548
/**
17549
 * True if Save/Cancel buttons should not be displayed in the CellEditor.
17550
 *
17551
 * @property disableBtns
17552
 * @type Boolean
17553
 * @default false
17554
 */
17555
disableBtns : false,
17556
 
17557
/**
17558
 * True if iframe shim for container element should be enabled.
17559
 *
17560
 * @property useIFrame
17561
 * @type Boolean
17562
 * @default false
17563
 */
17564
useIFrame : false,
17565
 
17566
/**
17567
 * Custom CSS class or array of classes applied to the container element.
17568
 *
17569
 * @property className
17570
 * @type String || String[]
17571
 */
17572
className : null,
17573
 
17574
 
17575
 
17576
 
17577
 
17578
/////////////////////////////////////////////////////////////////////////////
17579
//
17580
// Public methods
17581
//
17582
/////////////////////////////////////////////////////////////////////////////
17583
/**
17584
 * CellEditor instance name, for logging.
17585
 *
17586
 * @method toString
17587
 * @return {String} Unique name of the CellEditor instance.
17588
 */
17589
 
17590
toString : function() {
17591
    return "CellEditor instance " + this._sId;
17592
},
17593
 
17594
/**
17595
 * CellEditor unique ID.
17596
 *
17597
 * @method getId
17598
 * @return {String} Unique ID of the CellEditor instance.
17599
 */
17600
 
17601
getId : function() {
17602
    return this._sId;
17603
},
17604
 
17605
/**
17606
 * Returns reference to associated DataTable instance.
17607
 *
17608
 * @method getDataTable
17609
 * @return {YAHOO.widget.DataTable} DataTable instance.
17610
 */
17611
 
17612
getDataTable : function() {
17613
    return this._oDataTable;
17614
},
17615
 
17616
/**
17617
 * Returns reference to associated Column instance.
17618
 *
17619
 * @method getColumn
17620
 * @return {YAHOO.widget.Column} Column instance.
17621
 */
17622
 
17623
getColumn : function() {
17624
    return this._oColumn;
17625
},
17626
 
17627
/**
17628
 * Returns reference to associated Record instance.
17629
 *
17630
 * @method getRecord
17631
 * @return {YAHOO.widget.Record} Record instance.
17632
 */
17633
 
17634
getRecord : function() {
17635
    return this._oRecord;
17636
},
17637
 
17638
 
17639
 
17640
/**
17641
 * Returns reference to associated TD element.
17642
 *
17643
 * @method getTdEl
17644
 * @return {HTMLElement} TD element.
17645
 */
17646
 
17647
getTdEl : function() {
17648
    return this._elTd;
17649
},
17650
 
17651
/**
17652
 * Returns container element.
17653
 *
17654
 * @method getContainerEl
17655
 * @return {HTMLElement} Reference to container element.
17656
 */
17657
 
17658
getContainerEl : function() {
17659
    return this._elContainer;
17660
},
17661
 
17662
/**
17663
 * Nulls out the entire CellEditor instance and related objects, removes attached
17664
 * event listeners, and clears out DOM elements inside the container, removes
17665
 * container from the DOM.
17666
 *
17667
 * @method destroy
17668
 */
17669
destroy : function() {
17670
    this.unsubscribeAll();
17671
 
17672
    // Column is late-binding in attach()
17673
    var oColumn = this.getColumn();
17674
    if(oColumn) {
17675
        oColumn.editor = null;
17676
    }
17677
 
17678
    var elContainer = this.getContainerEl();
17679
    if (elContainer) {
17680
        Ev.purgeElement(elContainer, true);
17681
        elContainer.parentNode.removeChild(elContainer);
17682
    }
17683
},
17684
 
17685
/**
17686
 * Renders DOM elements and attaches event listeners.
17687
 *
17688
 * @method render
17689
 */
17690
render : function() {
17691
    if (!this._needsRender) {
17692
        return;
17693
    }
17694
 
17695
    this._initContainerEl();
17696
    this._initShimEl();
17697
 
17698
    // Handle ESC key
17699
    Ev.addListener(this.getContainerEl(), "keydown", function(e, oSelf) {
17700
        // ESC cancels Cell Editor
17701
        if((e.keyCode == 27)) {
17702
            var target = Ev.getTarget(e);
17703
            // workaround for Mac FF3 bug that disabled clicks when ESC hit when
17704
            // select is open. [bug 2273056]
17705
            if (target.nodeName && target.nodeName.toLowerCase() === 'select') {
17706
                target.blur();
17707
            }
17708
            oSelf.cancel();
17709
        }
17710
        // Pass through event
17711
        oSelf.fireEvent("keydownEvent", {editor:oSelf, event:e});
17712
    }, this);
17713
 
17714
    this.renderForm();
17715
 
17716
    // Show Save/Cancel buttons
17717
    if(!this.disableBtns) {
17718
        this.renderBtns();
17719
    }
17720
 
17721
    this.doAfterRender();
17722
    this._needsRender = false;
17723
},
17724
 
17725
/**
17726
 * Renders Save/Cancel buttons.
17727
 *
17728
 * @method renderBtns
17729
 */
17730
renderBtns : function() {
17731
    // Buttons
17732
    var elBtnsDiv = this.getContainerEl().appendChild(document.createElement("div"));
17733
    elBtnsDiv.className = DT.CLASS_BUTTON;
17734
 
17735
    // Save button
17736
    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
17737
    elSaveBtn.className = DT.CLASS_DEFAULT;
17738
    elSaveBtn.innerHTML = this.LABEL_SAVE;
17739
    Ev.addListener(elSaveBtn, "click", function(oArgs) {
17740
        this.save();
17741
    }, this, true);
17742
    this._elSaveBtn = elSaveBtn;
17743
 
17744
    // Cancel button
17745
    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
17746
    elCancelBtn.innerHTML = this.LABEL_CANCEL;
17747
    Ev.addListener(elCancelBtn, "click", function(oArgs) {
17748
        this.cancel();
17749
    }, this, true);
17750
    this._elCancelBtn = elCancelBtn;
17751
},
17752
 
17753
/**
17754
 * Attach CellEditor for a new interaction.
17755
 *
17756
 * @method attach
17757
 * @param oDataTable {YAHOO.widget.DataTable} Associated DataTable instance.
17758
 * @param elCell {HTMLElement} Cell to edit.
17759
 */
17760
attach : function(oDataTable, elCell) {
17761
    // Validate
17762
    if(oDataTable instanceof YAHOO.widget.DataTable) {
17763
        this._oDataTable = oDataTable;
17764
 
17765
        // Validate cell
17766
        elCell = oDataTable.getTdEl(elCell);
17767
        if(elCell) {
17768
            this._elTd = elCell;
17769
 
17770
            // Validate Column
17771
            var oColumn = oDataTable.getColumn(elCell);
17772
            if(oColumn) {
17773
                this._oColumn = oColumn;
17774
 
17775
                // Validate Record
17776
                var oRecord = oDataTable.getRecord(elCell);
17777
                if(oRecord) {
17778
                    this._oRecord = oRecord;
17779
                    var value = oRecord.getData(this.getColumn().getField());
17780
                    this.value = (value !== undefined) ? value : this.defaultValue;
17781
                    return true;
17782
                }
17783
            }
17784
        }
17785
    }
17786
    return false;
17787
},
17788
 
17789
/**
17790
 * Moves container into position for display.
17791
 *
17792
 * @method move
17793
 */
17794
move : function() {
17795
    // Move Editor
17796
    var elContainer = this.getContainerEl(),
17797
        elTd = this.getTdEl(),
17798
        x = Dom.getX(elTd),
17799
        y = Dom.getY(elTd);
17800
 
17801
    //TODO: remove scrolling logic
17802
    // SF doesn't get xy for cells in scrolling table
17803
    // when tbody display is set to block
17804
    if(isNaN(x) || isNaN(y)) {
17805
        var elTbody = this.getDataTable().getTbodyEl();
17806
        x = elTd.offsetLeft + // cell pos relative to table
17807
                Dom.getX(elTbody.parentNode) - // plus table pos relative to document
17808
                elTbody.scrollLeft; // minus tbody scroll
17809
        y = elTd.offsetTop + // cell pos relative to table
17810
                Dom.getY(elTbody.parentNode) - // plus table pos relative to document
17811
                elTbody.scrollTop + // minus tbody scroll
17812
                this.getDataTable().getTheadEl().offsetHeight; // account for fixed THEAD cells
17813
    }
17814
 
17815
    elContainer.style.left = x + "px";
17816
    elContainer.style.top = y + "px";
17817
 
17818
    if(this._elIFrame) {
17819
        this._elIFrame.style.left = x + "px";
17820
        this._elIFrame.style.top = y + "px";
17821
    }
17822
},
17823
 
17824
/**
17825
 * Displays CellEditor UI in the correct position.
17826
 *
17827
 * @method show
17828
 */
17829
show : function() {
17830
    var elContainer = this.getContainerEl(),
17831
        elIFrame = this._elIFrame;
17832
    this.resetForm();
17833
    this.isActive = true;
17834
    elContainer.style.display = "";
17835
    if(elIFrame) {
17836
        elIFrame.style.width = elContainer.offsetWidth + "px";
17837
        elIFrame.style.height = elContainer.offsetHeight + "px";
17838
        elIFrame.style.display = "";
17839
    }
17840
    this.focus();
17841
    this.fireEvent("showEvent", {editor:this});
17842
},
17843
 
17844
/**
17845
 * Fires blockEvent
17846
 *
17847
 * @method block
17848
 */
17849
block : function() {
17850
    this.fireEvent("blockEvent", {editor:this});
17851
},
17852
 
17853
/**
17854
 * Fires unblockEvent
17855
 *
17856
 * @method unblock
17857
 */
17858
unblock : function() {
17859
    this.fireEvent("unblockEvent", {editor:this});
17860
},
17861
 
17862
/**
17863
 * Saves value of CellEditor and hides UI.
17864
 *
17865
 * @method save
17866
 */
17867
save : function() {
17868
    // Get new value
17869
    var inputValue = this.getInputValue();
17870
    var validValue = inputValue;
17871
 
17872
    // Validate new value
17873
    if(this.validator) {
17874
        validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this);
17875
        if(validValue === undefined ) {
17876
            if(this.resetInvalidData) {
17877
                this.resetForm();
17878
            }
17879
            this.fireEvent("invalidDataEvent",
17880
                    {editor:this, oldData:this.value, newData:inputValue});
17881
            return;
17882
        }
17883
    }
17884
 
17885
    var oSelf = this;
17886
    var finishSave = function(bSuccess, oNewValue) {
17887
        var oOrigValue = oSelf.value;
17888
        if(bSuccess) {
17889
            // Update new value
17890
            oSelf.value = oNewValue;
17891
            oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue);
17892
 
17893
            // Hide CellEditor
17894
            oSelf._hide();
17895
 
17896
            oSelf.fireEvent("saveEvent",
17897
                    {editor:oSelf, oldData:oOrigValue, newData:oSelf.value});
17898
        }
17899
        else {
17900
            oSelf.resetForm();
17901
            oSelf.fireEvent("revertEvent",
17902
                    {editor:oSelf, oldData:oOrigValue, newData:oNewValue});
17903
        }
17904
        oSelf.unblock();
17905
    };
17906
 
17907
    this.block();
17908
    if(lang.isFunction(this.asyncSubmitter)) {
17909
        this.asyncSubmitter.call(this, finishSave, validValue);
17910
    }
17911
    else {
17912
        finishSave(true, validValue);
17913
    }
17914
},
17915
 
17916
/**
17917
 * Cancels CellEditor input and hides UI.
17918
 *
17919
 * @method cancel
17920
 */
17921
cancel : function() {
17922
    if(this.isActive) {
17923
        this._hide();
17924
        this.fireEvent("cancelEvent", {editor:this});
17925
    }
17926
    else {
17927
    }
17928
},
17929
 
17930
/**
17931
 * Renders form elements.
17932
 *
17933
 * @method renderForm
17934
 */
17935
renderForm : function() {
17936
    // To be implemented by subclass
17937
},
17938
 
17939
/**
17940
 * Access to add additional event listeners.
17941
 *
17942
 * @method doAfterRender
17943
 */
17944
doAfterRender : function() {
17945
    // To be implemented by subclass
17946
},
17947
 
17948
 
17949
/**
17950
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
17951
 * to save input without them.
17952
 *
17953
 * @method handleDisabledBtns
17954
 */
17955
handleDisabledBtns : function() {
17956
    // To be implemented by subclass
17957
},
17958
 
17959
/**
17960
 * Resets CellEditor UI to initial state.
17961
 *
17962
 * @method resetForm
17963
 */
17964
resetForm : function() {
17965
    // To be implemented by subclass
17966
},
17967
 
17968
/**
17969
 * Sets focus in CellEditor.
17970
 *
17971
 * @method focus
17972
 */
17973
focus : function() {
17974
    // To be implemented by subclass
17975
},
17976
 
17977
/**
17978
 * Retrieves input value from CellEditor.
17979
 *
17980
 * @method getInputValue
17981
 */
17982
getInputValue : function() {
17983
    // To be implemented by subclass
17984
}
17985
 
17986
};
17987
 
17988
lang.augmentProto(BCE, util.EventProvider);
17989
 
17990
 
17991
/////////////////////////////////////////////////////////////////////////////
17992
//
17993
// Custom Events
17994
//
17995
/////////////////////////////////////////////////////////////////////////////
17996
 
17997
/**
17998
 * Fired when a CellEditor is shown.
17999
 *
18000
 * @event showEvent
18001
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18002
 */
18003
 
18004
/**
18005
 * Fired when a CellEditor has a keydown.
18006
 *
18007
 * @event keydownEvent
18008
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18009
 * @param oArgs.event {HTMLEvent} The event object.
18010
 */
18011
 
18012
/**
18013
 * Fired when a CellEditor input is reverted due to invalid data.
18014
 *
18015
 * @event invalidDataEvent
18016
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18017
 * @param oArgs.newData {Object} New data value from form input field.
18018
 * @param oArgs.oldData {Object} Old data value.
18019
 */
18020
 
18021
/**
18022
 * Fired when a CellEditor input is reverted due to asyncSubmitter failure.
18023
 *
18024
 * @event revertEvent
18025
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18026
 * @param oArgs.newData {Object} New data value from form input field.
18027
 * @param oArgs.oldData {Object} Old data value.
18028
 */
18029
 
18030
/**
18031
 * Fired when a CellEditor input is saved.
18032
 *
18033
 * @event saveEvent
18034
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18035
 * @param oArgs.newData {Object} New data value from form input field.
18036
 * @param oArgs.oldData {Object} Old data value.
18037
 */
18038
 
18039
/**
18040
 * Fired when a CellEditor input is canceled.
18041
 *
18042
 * @event cancelEvent
18043
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18044
 */
18045
 
18046
/**
18047
 * Fired when a CellEditor has a blur event.
18048
 *
18049
 * @event blurEvent
18050
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
18051
 */
18052
 
18053
 
18054
 
18055
 
18056
 
18057
 
18058
 
18059
 
18060
 
18061
 
18062
 
18063
 
18064
 
18065
 
18066
/****************************************************************************/
18067
/****************************************************************************/
18068
/****************************************************************************/
18069
 
18070
/**
18071
 * The CheckboxCellEditor class provides functionality for inline editing
18072
 * DataTable cell data with checkboxes.
18073
 *
18074
 * @namespace YAHOO.widget
18075
 * @class CheckboxCellEditor
18076
 * @extends YAHOO.widget.BaseCellEditor
18077
 * @constructor
18078
 * @param oConfigs {Object} (Optional) Object literal of configs.
18079
 */
18080
widget.CheckboxCellEditor = function(oConfigs) {
18081
    oConfigs = oConfigs || {};
18082
    this._sId = this._sId || Dom.generateId(null, "yui-checkboxceditor"); // "yui-checkboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18083
    YAHOO.widget.BaseCellEditor._nCount++;
18084
    widget.CheckboxCellEditor.superclass.constructor.call(this, oConfigs.type || "checkbox", oConfigs);
18085
};
18086
 
18087
// CheckboxCellEditor extends BaseCellEditor
18088
lang.extend(widget.CheckboxCellEditor, BCE, {
18089
 
18090
/////////////////////////////////////////////////////////////////////////////
18091
//
18092
// CheckboxCellEditor public properties
18093
//
18094
/////////////////////////////////////////////////////////////////////////////
18095
/**
18096
 * Array of checkbox values. Can either be a simple array (e.g., ["red","green","blue"])
18097
 * or a an array of objects (e.g., [{label:"red", value:"#FF0000"},
18098
 * {label:"green", value:"#00FF00"}, {label:"blue", value:"#0000FF"}]). String
18099
 * values are treated as markup and inserted into the DOM as innerHTML.
18100
 *
18101
 * @property checkboxOptions
18102
 * @type HTML[] | Object[]
18103
 */
18104
checkboxOptions : null,
18105
 
18106
/**
18107
 * Reference to the checkbox elements.
18108
 *
18109
 * @property checkboxes
18110
 * @type HTMLElement[]
18111
 */
18112
checkboxes : null,
18113
 
18114
/**
18115
 * Array of checked values
18116
 *
18117
 * @property value
18118
 * @type String[]
18119
 */
18120
value : null,
18121
 
18122
/////////////////////////////////////////////////////////////////////////////
18123
//
18124
// CheckboxCellEditor public methods
18125
//
18126
/////////////////////////////////////////////////////////////////////////////
18127
 
18128
/**
18129
 * Render a form with input(s) type=checkbox.
18130
 *
18131
 * @method renderForm
18132
 */
18133
renderForm : function() {
18134
    if(lang.isArray(this.checkboxOptions)) {
18135
        var checkboxOption, checkboxValue, checkboxId, elLabel, j, len;
18136
 
18137
        // Create the checkbox buttons in an IE-friendly way...
18138
        for(j=0,len=this.checkboxOptions.length; j<len; j++) {
18139
            checkboxOption = this.checkboxOptions[j];
18140
            checkboxValue = lang.isValue(checkboxOption.value) ?
18141
                    checkboxOption.value : checkboxOption;
18142
 
18143
            checkboxId = this.getId() + "-chk" + j;
18144
            this.getContainerEl().innerHTML += "<input type=\"checkbox\"" +
18145
                    " id=\"" + checkboxId + "\"" + // Needed for label
18146
                    " value=\"" + checkboxValue + "\" />";
18147
 
18148
            // Create the labels in an IE-friendly way
18149
            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
18150
            elLabel.htmlFor = checkboxId;
18151
            elLabel.innerHTML = lang.isValue(checkboxOption.label) ?
18152
                    checkboxOption.label : checkboxOption;
18153
        }
18154
 
18155
        // Store the reference to the checkbox elements
18156
        var allCheckboxes = [];
18157
        for(j=0; j<len; j++) {
18158
            allCheckboxes[allCheckboxes.length] = this.getContainerEl().childNodes[j*2];
18159
        }
18160
        this.checkboxes = allCheckboxes;
18161
 
18162
        if(this.disableBtns) {
18163
            this.handleDisabledBtns();
18164
        }
18165
    }
18166
    else {
18167
    }
18168
},
18169
 
18170
/**
18171
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18172
 * to save input without them.
18173
 *
18174
 * @method handleDisabledBtns
18175
 */
18176
handleDisabledBtns : function() {
18177
    Ev.addListener(this.getContainerEl(), "click", function(v){
18178
        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
18179
            // Save on blur
18180
            this.save();
18181
        }
18182
    }, this, true);
18183
},
18184
 
18185
/**
18186
 * Resets CheckboxCellEditor UI to initial state.
18187
 *
18188
 * @method resetForm
18189
 */
18190
resetForm : function() {
18191
    // Normalize to array
18192
    var originalValues = lang.isArray(this.value) ? this.value : [this.value];
18193
 
18194
    // Match checks to value
18195
    for(var i=0, j=this.checkboxes.length; i<j; i++) {
18196
        this.checkboxes[i].checked = false;
18197
        for(var k=0, len=originalValues.length; k<len; k++) {
18198
            if(this.checkboxes[i].value == originalValues[k]) {
18199
                this.checkboxes[i].checked = true;
18200
            }
18201
        }
18202
    }
18203
},
18204
 
18205
/**
18206
 * Sets focus in CheckboxCellEditor.
18207
 *
18208
 * @method focus
18209
 */
18210
focus : function() {
18211
    this.checkboxes[0].focus();
18212
},
18213
 
18214
/**
18215
 * Retrieves input value from CheckboxCellEditor.
18216
 *
18217
 * @method getInputValue
18218
 */
18219
getInputValue : function() {
18220
    var checkedValues = [];
18221
    for(var i=0, j=this.checkboxes.length; i<j; i++) {
18222
        if(this.checkboxes[i].checked) {
18223
            checkedValues[checkedValues.length] = this.checkboxes[i].value;
18224
        }
18225
    }
18226
    return checkedValues;
18227
}
18228
 
18229
});
18230
 
18231
// Copy static members to CheckboxCellEditor class
18232
lang.augmentObject(widget.CheckboxCellEditor, BCE);
18233
 
18234
 
18235
 
18236
 
18237
 
18238
 
18239
 
18240
 
18241
/****************************************************************************/
18242
/****************************************************************************/
18243
/****************************************************************************/
18244
 
18245
/**
18246
 * The DataCellEditor class provides functionality for inline editing
18247
 * DataTable cell data with a YUI Calendar.
18248
 *
18249
 * @namespace YAHOO.widget
18250
 * @class DateCellEditor
18251
 * @extends YAHOO.widget.BaseCellEditor
18252
 * @constructor
18253
 * @param oConfigs {Object} (Optional) Object literal of configs.
18254
 */
18255
widget.DateCellEditor = function(oConfigs) {
18256
    oConfigs = oConfigs || {};
18257
    this._sId = this._sId || Dom.generateId(null, "yui-dateceditor"); // "yui-dateceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18258
    YAHOO.widget.BaseCellEditor._nCount++;
18259
    widget.DateCellEditor.superclass.constructor.call(this, oConfigs.type || "date", oConfigs);
18260
};
18261
 
18262
// CheckboxCellEditor extends BaseCellEditor
18263
lang.extend(widget.DateCellEditor, BCE, {
18264
 
18265
/////////////////////////////////////////////////////////////////////////////
18266
//
18267
// DateCellEditor public properties
18268
//
18269
/////////////////////////////////////////////////////////////////////////////
18270
/**
18271
 * Reference to Calendar instance.
18272
 *
18273
 * @property calendar
18274
 * @type YAHOO.widget.Calendar
18275
 */
18276
calendar : null,
18277
 
18278
/**
18279
 * Configs for the calendar instance, to be passed to Calendar constructor.
18280
 *
18281
 * @property calendarOptions
18282
 * @type Object
18283
 */
18284
calendarOptions : null,
18285
 
18286
/**
18287
 * Default value.
18288
 *
18289
 * @property defaultValue
18290
 * @type Date
18291
 * @default new Date()
18292
 */
18293
defaultValue : new Date(),
18294
 
18295
 
18296
/////////////////////////////////////////////////////////////////////////////
18297
//
18298
// DateCellEditor public methods
18299
//
18300
/////////////////////////////////////////////////////////////////////////////
18301
 
18302
/**
18303
 * Render a Calendar.
18304
 *
18305
 * @method renderForm
18306
 */
18307
renderForm : function() {
18308
    // Calendar widget
18309
    if(YAHOO.widget.Calendar) {
18310
        var calContainer = this.getContainerEl().appendChild(document.createElement("div"));
18311
        calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor
18312
        var calendar =
18313
                new YAHOO.widget.Calendar(this.getId() + "-date",
18314
                calContainer.id, this.calendarOptions);
18315
        calendar.render();
18316
        calContainer.style.cssFloat = "none";
18317
 
18318
        // Bug 2528576
18319
        calendar.hideEvent.subscribe(function() {this.cancel();}, this, true);
18320
 
18321
        if(ua.ie) {
18322
            var calFloatClearer = this.getContainerEl().appendChild(document.createElement("div"));
18323
            calFloatClearer.style.clear = "both";
18324
        }
18325
 
18326
        this.calendar = calendar;
18327
 
18328
        if(this.disableBtns) {
18329
            this.handleDisabledBtns();
18330
        }
18331
    }
18332
    else {
18333
    }
18334
 
18335
},
18336
 
18337
/**
18338
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18339
 * to save input without them.
18340
 *
18341
 * @method handleDisabledBtns
18342
 */
18343
handleDisabledBtns : function() {
18344
    this.calendar.selectEvent.subscribe(function(v){
18345
        // Save on select
18346
        this.save();
18347
    }, this, true);
18348
},
18349
 
18350
/**
18351
 * Resets DateCellEditor UI to initial state.
18352
 *
18353
 * @method resetForm
18354
 */
18355
resetForm : function() {
18356
    var value = this.value || (new Date());
18357
    this.calendar.select(value);
18358
    this.calendar.cfg.setProperty("pagedate",value,false);
18359
	this.calendar.render();
18360
	// Bug 2528576
18361
	this.calendar.show();
18362
},
18363
 
18364
/**
18365
 * Sets focus in DateCellEditor.
18366
 *
18367
 * @method focus
18368
 */
18369
focus : function() {
18370
    // To be impmlemented by subclass
18371
},
18372
 
18373
/**
18374
 * Retrieves input value from DateCellEditor.
18375
 *
18376
 * @method getInputValue
18377
 */
18378
getInputValue : function() {
18379
    return this.calendar.getSelectedDates()[0];
18380
}
18381
 
18382
});
18383
 
18384
// Copy static members to DateCellEditor class
18385
lang.augmentObject(widget.DateCellEditor, BCE);
18386
 
18387
 
18388
 
18389
 
18390
 
18391
 
18392
 
18393
 
18394
 
18395
/****************************************************************************/
18396
/****************************************************************************/
18397
/****************************************************************************/
18398
 
18399
/**
18400
 * The DropdownCellEditor class provides functionality for inline editing
18401
 * DataTable cell data a SELECT element.
18402
 *
18403
 * @namespace YAHOO.widget
18404
 * @class DropdownCellEditor
18405
 * @extends YAHOO.widget.BaseCellEditor
18406
 * @constructor
18407
 * @param oConfigs {Object} (Optional) Object literal of configs.
18408
 */
18409
widget.DropdownCellEditor = function(oConfigs) {
18410
    oConfigs = oConfigs || {};
18411
    this._sId = this._sId || Dom.generateId(null, "yui-dropdownceditor"); // "yui-dropdownceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18412
    YAHOO.widget.BaseCellEditor._nCount++;
18413
    widget.DropdownCellEditor.superclass.constructor.call(this, oConfigs.type || "dropdown", oConfigs);
18414
};
18415
 
18416
// DropdownCellEditor extends BaseCellEditor
18417
lang.extend(widget.DropdownCellEditor, BCE, {
18418
 
18419
/////////////////////////////////////////////////////////////////////////////
18420
//
18421
// DropdownCellEditor public properties
18422
//
18423
/////////////////////////////////////////////////////////////////////////////
18424
/**
18425
 * Array of dropdown values. Can either be a simple array (e.g.,
18426
 * ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g.,
18427
 * [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"},
18428
 * {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]). String
18429
 * values are treated as markup and inserted into the DOM as innerHTML.
18430
 *
18431
 * @property dropdownOptions
18432
 * @type HTML[] | Object[]
18433
 */
18434
dropdownOptions : null,
18435
 
18436
/**
18437
 * Reference to Dropdown element.
18438
 *
18439
 * @property dropdown
18440
 * @type HTMLElement
18441
 */
18442
dropdown : null,
18443
 
18444
/**
18445
 * Enables multi-select.
18446
 *
18447
 * @property multiple
18448
 * @type Boolean
18449
 */
18450
multiple : false,
18451
 
18452
/**
18453
 * Specifies number of visible options.
18454
 *
18455
 * @property size
18456
 * @type Number
18457
 */
18458
size : null,
18459
 
18460
/////////////////////////////////////////////////////////////////////////////
18461
//
18462
// DropdownCellEditor public methods
18463
//
18464
/////////////////////////////////////////////////////////////////////////////
18465
 
18466
/**
18467
 * Render a form with select element.
18468
 *
18469
 * @method renderForm
18470
 */
18471
renderForm : function() {
18472
    var elDropdown = this.getContainerEl().appendChild(document.createElement("select"));
18473
    elDropdown.style.zoom = 1;
18474
    if(this.multiple) {
18475
        elDropdown.multiple = "multiple";
18476
    }
18477
    if(lang.isNumber(this.size)) {
18478
        elDropdown.size = this.size;
18479
    }
18480
    this.dropdown = elDropdown;
18481
 
18482
    if(lang.isArray(this.dropdownOptions)) {
18483
        var dropdownOption, elOption;
18484
        for(var i=0, j=this.dropdownOptions.length; i<j; i++) {
18485
            dropdownOption = this.dropdownOptions[i];
18486
            elOption = document.createElement("option");
18487
            elOption.value = (lang.isValue(dropdownOption.value)) ?
18488
                    dropdownOption.value : dropdownOption;
18489
            elOption.innerHTML = (lang.isValue(dropdownOption.label)) ?
18490
                    dropdownOption.label : dropdownOption;
18491
            elOption = elDropdown.appendChild(elOption);
18492
        }
18493
 
18494
        if(this.disableBtns) {
18495
            this.handleDisabledBtns();
18496
        }
18497
    }
18498
},
18499
 
18500
/**
18501
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18502
 * to save input without them.
18503
 *
18504
 * @method handleDisabledBtns
18505
 */
18506
handleDisabledBtns : function() {
18507
    // Save on blur for multi-select
18508
    if(this.multiple) {
18509
        Ev.addListener(this.dropdown, "blur", function(v){
18510
            // Save on change
18511
            this.save();
18512
        }, this, true);
18513
    }
18514
    // Save on change for single-select
18515
    else {
18516
        if(!ua.ie) {
18517
            Ev.addListener(this.dropdown, "change", function(v){
18518
                // Save on change
18519
                this.save();
18520
            }, this, true);
18521
        }
18522
        else {
18523
            // Bug 2529274: "change" event is not keyboard accessible in IE6
18524
            Ev.addListener(this.dropdown, "blur", function(v){
18525
                this.save();
18526
            }, this, true);
18527
            Ev.addListener(this.dropdown, "click", function(v){
18528
                this.save();
18529
            }, this, true);
18530
        }
18531
    }
18532
},
18533
 
18534
/**
18535
 * Resets DropdownCellEditor UI to initial state.
18536
 *
18537
 * @method resetForm
18538
 */
18539
resetForm : function() {
18540
    var allOptions = this.dropdown.options,
18541
        i=0, j=allOptions.length;
18542
 
18543
    // Look for multi-select selections
18544
    if(lang.isArray(this.value)) {
18545
        var allValues = this.value,
18546
            m=0, n=allValues.length,
18547
            hash = {};
18548
        // Reset all selections and stash options in a value hash
18549
        for(; i<j; i++) {
18550
            allOptions[i].selected = false;
18551
            hash[allOptions[i].value] = allOptions[i];
18552
        }
18553
        for(; m<n; m++) {
18554
            if(hash[allValues[m]]) {
18555
                hash[allValues[m]].selected = true;
18556
            }
18557
        }
18558
    }
18559
    // Only need to look for a single selection
18560
    else {
18561
        for(; i<j; i++) {
18562
            if(this.value == allOptions[i].value) {
18563
                allOptions[i].selected = true;
18564
            }
18565
        }
18566
    }
18567
},
18568
 
18569
/**
18570
 * Sets focus in DropdownCellEditor.
18571
 *
18572
 * @method focus
18573
 */
18574
focus : function() {
18575
    this.getDataTable()._focusEl(this.dropdown);
18576
},
18577
 
18578
/**
18579
 * Retrieves input value from DropdownCellEditor.
18580
 *
18581
 * @method getInputValue
18582
 */
18583
getInputValue : function() {
18584
    var allOptions = this.dropdown.options;
18585
 
18586
    // Look for multiple selections
18587
    if(this.multiple) {
18588
        var values = [],
18589
            i=0, j=allOptions.length;
18590
        for(; i<j; i++) {
18591
            if(allOptions[i].selected) {
18592
                values.push(allOptions[i].value);
18593
            }
18594
        }
18595
        return values;
18596
    }
18597
    // Only need to look for single selection
18598
    else {
18599
        return allOptions[allOptions.selectedIndex].value;
18600
    }
18601
}
18602
 
18603
});
18604
 
18605
// Copy static members to DropdownCellEditor class
18606
lang.augmentObject(widget.DropdownCellEditor, BCE);
18607
 
18608
 
18609
 
18610
 
18611
 
18612
 
18613
/****************************************************************************/
18614
/****************************************************************************/
18615
/****************************************************************************/
18616
 
18617
/**
18618
 * The RadioCellEditor class provides functionality for inline editing
18619
 * DataTable cell data with radio buttons.
18620
 *
18621
 * @namespace YAHOO.widget
18622
 * @class RadioCellEditor
18623
 * @extends YAHOO.widget.BaseCellEditor
18624
 * @constructor
18625
 * @param oConfigs {Object} (Optional) Object literal of configs.
18626
 */
18627
widget.RadioCellEditor = function(oConfigs) {
18628
    oConfigs = oConfigs || {};
18629
    this._sId = this._sId || Dom.generateId(null, "yui-radioceditor"); // "yui-radioceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18630
    YAHOO.widget.BaseCellEditor._nCount++;
18631
    widget.RadioCellEditor.superclass.constructor.call(this, oConfigs.type || "radio", oConfigs);
18632
};
18633
 
18634
// RadioCellEditor extends BaseCellEditor
18635
lang.extend(widget.RadioCellEditor, BCE, {
18636
 
18637
/////////////////////////////////////////////////////////////////////////////
18638
//
18639
// RadioCellEditor public properties
18640
//
18641
/////////////////////////////////////////////////////////////////////////////
18642
/**
18643
 * Reference to radio elements.
18644
 *
18645
 * @property radios
18646
 * @type HTMLElement[]
18647
 */
18648
radios : null,
18649
 
18650
/**
18651
 * Array of radio values. Can either be a simple array (e.g., ["yes","no","maybe"])
18652
 * or a an array of objects (e.g., [{label:"yes", value:1}, {label:"no", value:-1},
18653
 * {label:"maybe", value:0}]). String values are treated as markup and inserted
18654
 * into the DOM as innerHTML.
18655
 *
18656
 * @property radioOptions
18657
 * @type HTML[] | Object[]
18658
 */
18659
radioOptions : null,
18660
 
18661
/////////////////////////////////////////////////////////////////////////////
18662
//
18663
// RadioCellEditor public methods
18664
//
18665
/////////////////////////////////////////////////////////////////////////////
18666
 
18667
/**
18668
 * Render a form with input(s) type=radio.
18669
 *
18670
 * @method renderForm
18671
 */
18672
renderForm : function() {
18673
    if(lang.isArray(this.radioOptions)) {
18674
        var radioOption, radioValue, radioId, elLabel;
18675
 
18676
        // Create the radio buttons in an IE-friendly way
18677
        for(var i=0, len=this.radioOptions.length; i<len; i++) {
18678
            radioOption = this.radioOptions[i];
18679
            radioValue = lang.isValue(radioOption.value) ?
18680
                    radioOption.value : radioOption;
18681
            radioId = this.getId() + "-radio" + i;
18682
            this.getContainerEl().innerHTML += "<input type=\"radio\"" +
18683
                    " name=\"" + this.getId() + "\"" +
18684
                    " value=\"" + radioValue + "\"" +
18685
                    " id=\"" +  radioId + "\" />"; // Needed for label
18686
 
18687
            // Create the labels in an IE-friendly way
18688
            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
18689
            elLabel.htmlFor = radioId;
18690
            elLabel.innerHTML = (lang.isValue(radioOption.label)) ?
18691
                    radioOption.label : radioOption;
18692
        }
18693
 
18694
        // Store the reference to the checkbox elements
18695
        var allRadios = [],
18696
            elRadio;
18697
        for(var j=0; j<len; j++) {
18698
            elRadio = this.getContainerEl().childNodes[j*2];
18699
            allRadios[allRadios.length] = elRadio;
18700
        }
18701
        this.radios = allRadios;
18702
 
18703
        if(this.disableBtns) {
18704
            this.handleDisabledBtns();
18705
        }
18706
    }
18707
    else {
18708
    }
18709
},
18710
 
18711
/**
18712
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18713
 * to save input without them.
18714
 *
18715
 * @method handleDisabledBtns
18716
 */
18717
handleDisabledBtns : function() {
18718
    Ev.addListener(this.getContainerEl(), "click", function(v){
18719
        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
18720
            // Save on blur
18721
            this.save();
18722
        }
18723
    }, this, true);
18724
},
18725
 
18726
/**
18727
 * Resets RadioCellEditor UI to initial state.
18728
 *
18729
 * @method resetForm
18730
 */
18731
resetForm : function() {
18732
    for(var i=0, j=this.radios.length; i<j; i++) {
18733
        var elRadio = this.radios[i];
18734
        if(this.value == elRadio.value) {
18735
            elRadio.checked = true;
18736
            return;
18737
        }
18738
    }
18739
},
18740
 
18741
/**
18742
 * Sets focus in RadioCellEditor.
18743
 *
18744
 * @method focus
18745
 */
18746
focus : function() {
18747
    for(var i=0, j=this.radios.length; i<j; i++) {
18748
        if(this.radios[i].checked) {
18749
            this.radios[i].focus();
18750
            return;
18751
        }
18752
    }
18753
},
18754
 
18755
/**
18756
 * Retrieves input value from RadioCellEditor.
18757
 *
18758
 * @method getInputValue
18759
 */
18760
getInputValue : function() {
18761
    for(var i=0, j=this.radios.length; i<j; i++) {
18762
        if(this.radios[i].checked) {
18763
            return this.radios[i].value;
18764
        }
18765
    }
18766
}
18767
 
18768
});
18769
 
18770
// Copy static members to RadioCellEditor class
18771
lang.augmentObject(widget.RadioCellEditor, BCE);
18772
 
18773
 
18774
 
18775
 
18776
 
18777
 
18778
/****************************************************************************/
18779
/****************************************************************************/
18780
/****************************************************************************/
18781
 
18782
/**
18783
 * The TextareaCellEditor class provides functionality for inline editing
18784
 * DataTable cell data with a TEXTAREA element.
18785
 *
18786
 * @namespace YAHOO.widget
18787
 * @class TextareaCellEditor
18788
 * @extends YAHOO.widget.BaseCellEditor
18789
 * @constructor
18790
 * @param oConfigs {Object} (Optional) Object literal of configs.
18791
 */
18792
widget.TextareaCellEditor = function(oConfigs) {
18793
    oConfigs = oConfigs || {};
18794
    this._sId = this._sId || Dom.generateId(null, "yui-textareaceditor");// "yui-textareaceditor" + ;
18795
    YAHOO.widget.BaseCellEditor._nCount++;
18796
    widget.TextareaCellEditor.superclass.constructor.call(this, oConfigs.type || "textarea", oConfigs);
18797
};
18798
 
18799
// TextareaCellEditor extends BaseCellEditor
18800
lang.extend(widget.TextareaCellEditor, BCE, {
18801
 
18802
/////////////////////////////////////////////////////////////////////////////
18803
//
18804
// TextareaCellEditor public properties
18805
//
18806
/////////////////////////////////////////////////////////////////////////////
18807
/**
18808
 * Reference to textarea element.
18809
 *
18810
 * @property textarea
18811
 * @type HTMLElement
18812
 */
18813
textarea : null,
18814
 
18815
 
18816
/////////////////////////////////////////////////////////////////////////////
18817
//
18818
// TextareaCellEditor public methods
18819
//
18820
/////////////////////////////////////////////////////////////////////////////
18821
 
18822
/**
18823
 * Render a form with textarea.
18824
 *
18825
 * @method renderForm
18826
 */
18827
renderForm : function() {
18828
    var elTextarea = this.getContainerEl().appendChild(document.createElement("textarea"));
18829
    this.textarea = elTextarea;
18830
 
18831
    if(this.disableBtns) {
18832
        this.handleDisabledBtns();
18833
    }
18834
},
18835
 
18836
/**
18837
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
18838
 * to save input without them.
18839
 *
18840
 * @method handleDisabledBtns
18841
 */
18842
handleDisabledBtns : function() {
18843
    Ev.addListener(this.textarea, "blur", function(v){
18844
        // Save on blur
18845
        this.save();
18846
    }, this, true);
18847
},
18848
 
18849
/**
18850
 * Moves TextareaCellEditor UI to a cell.
18851
 *
18852
 * @method move
18853
 */
18854
move : function() {
18855
    this.textarea.style.width = this.getTdEl().offsetWidth + "px";
18856
    this.textarea.style.height = "3em";
18857
    YAHOO.widget.TextareaCellEditor.superclass.move.call(this);
18858
},
18859
 
18860
/**
18861
 * Resets TextareaCellEditor UI to initial state.
18862
 *
18863
 * @method resetForm
18864
 */
18865
resetForm : function() {
18866
    this.textarea.value = this.value;
18867
},
18868
 
18869
/**
18870
 * Sets focus in TextareaCellEditor.
18871
 *
18872
 * @method focus
18873
 */
18874
focus : function() {
18875
    // Bug 2303181, Bug 2263600
18876
    this.getDataTable()._focusEl(this.textarea);
18877
    this.textarea.select();
18878
},
18879
 
18880
/**
18881
 * Retrieves input value from TextareaCellEditor.
18882
 *
18883
 * @method getInputValue
18884
 */
18885
getInputValue : function() {
18886
    return this.textarea.value;
18887
}
18888
 
18889
});
18890
 
18891
// Copy static members to TextareaCellEditor class
18892
lang.augmentObject(widget.TextareaCellEditor, BCE);
18893
 
18894
 
18895
 
18896
 
18897
 
18898
 
18899
 
18900
 
18901
 
18902
/****************************************************************************/
18903
/****************************************************************************/
18904
/****************************************************************************/
18905
 
18906
/**
18907
 * The TextboxCellEditor class provides functionality for inline editing
18908
 * DataTable cell data with an INPUT TYPE=TEXT element.
18909
 *
18910
 * @namespace YAHOO.widget
18911
 * @class TextboxCellEditor
18912
 * @extends YAHOO.widget.BaseCellEditor
18913
 * @constructor
18914
 * @param oConfigs {Object} (Optional) Object literal of configs.
18915
 */
18916
widget.TextboxCellEditor = function(oConfigs) {
18917
    oConfigs = oConfigs || {};
18918
    this._sId = this._sId || Dom.generateId(null, "yui-textboxceditor");// "yui-textboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
18919
    YAHOO.widget.BaseCellEditor._nCount++;
18920
    widget.TextboxCellEditor.superclass.constructor.call(this, oConfigs.type || "textbox", oConfigs);
18921
};
18922
 
18923
// TextboxCellEditor extends BaseCellEditor
18924
lang.extend(widget.TextboxCellEditor, BCE, {
18925
 
18926
/////////////////////////////////////////////////////////////////////////////
18927
//
18928
// TextboxCellEditor public properties
18929
//
18930
/////////////////////////////////////////////////////////////////////////////
18931
/**
18932
 * Reference to the textbox element.
18933
 *
18934
 * @property textbox
18935
 */
18936
textbox : null,
18937
 
18938
/////////////////////////////////////////////////////////////////////////////
18939
//
18940
// TextboxCellEditor public methods
18941
//
18942
/////////////////////////////////////////////////////////////////////////////
18943
 
18944
/**
18945
 * Render a form with input type=text.
18946
 *
18947
 * @method renderForm
18948
 */
18949
renderForm : function() {
18950
    var elTextbox;
18951
    // Bug 1802582: SF3/Mac needs a form element wrapping the input
18952
    if(ua.webkit>420) {
18953
        elTextbox = this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input"));
18954
    }
18955
    else {
18956
        elTextbox = this.getContainerEl().appendChild(document.createElement("input"));
18957
    }
18958
    elTextbox.type = "text";
18959
    this.textbox = elTextbox;
18960
 
18961
    // Save on enter by default
18962
    // Bug: 1802582 Set up a listener on each textbox to track on keypress
18963
    // since SF/OP can't preventDefault on keydown
18964
    Ev.addListener(elTextbox, "keypress", function(v){
18965
        if((v.keyCode === 13)) {
18966
            // Prevent form submit
18967
            YAHOO.util.Event.preventDefault(v);
18968
            this.save();
18969
        }
18970
    }, this, true);
18971
 
18972
    if(this.disableBtns) {
18973
        // By default this is no-op since enter saves by default
18974
        this.handleDisabledBtns();
18975
    }
18976
},
18977
 
18978
/**
18979
 * Moves TextboxCellEditor UI to a cell.
18980
 *
18981
 * @method move
18982
 */
18983
move : function() {
18984
    this.textbox.style.width = this.getTdEl().offsetWidth + "px";
18985
    widget.TextboxCellEditor.superclass.move.call(this);
18986
},
18987
 
18988
/**
18989
 * Resets TextboxCellEditor UI to initial state.
18990
 *
18991
 * @method resetForm
18992
 */
18993
resetForm : function() {
18994
    this.textbox.value = lang.isValue(this.value) ? this.value.toString() : "";
18995
},
18996
 
18997
/**
18998
 * Sets focus in TextboxCellEditor.
18999
 *
19000
 * @method focus
19001
 */
19002
focus : function() {
19003
    // Bug 2303181, Bug 2263600
19004
    this.getDataTable()._focusEl(this.textbox);
19005
    this.textbox.select();
19006
},
19007
 
19008
/**
19009
 * Returns new value for TextboxCellEditor.
19010
 *
19011
 * @method getInputValue
19012
 */
19013
getInputValue : function() {
19014
    return this.textbox.value;
19015
}
19016
 
19017
});
19018
 
19019
// Copy static members to TextboxCellEditor class
19020
lang.augmentObject(widget.TextboxCellEditor, BCE);
19021
 
19022
 
19023
 
19024
 
19025
 
19026
 
19027
 
19028
/////////////////////////////////////////////////////////////////////////////
19029
//
19030
// DataTable extension
19031
//
19032
/////////////////////////////////////////////////////////////////////////////
19033
 
19034
/**
19035
 * CellEditor subclasses.
19036
 * @property DataTable.Editors
19037
 * @type Object
19038
 * @static
19039
 */
19040
DT.Editors = {
19041
    checkbox : widget.CheckboxCellEditor,
19042
    "date"   : widget.DateCellEditor,
19043
    dropdown : widget.DropdownCellEditor,
19044
    radio    : widget.RadioCellEditor,
19045
    textarea : widget.TextareaCellEditor,
19046
    textbox  : widget.TextboxCellEditor
19047
};
19048
 
19049
/****************************************************************************/
19050
/****************************************************************************/
19051
/****************************************************************************/
19052
 
19053
/**
19054
 * Factory class for instantiating a BaseCellEditor subclass.
19055
 *
19056
 * @namespace YAHOO.widget
19057
 * @class CellEditor
19058
 * @extends YAHOO.widget.BaseCellEditor
19059
 * @constructor
19060
 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
19061
 * @param oConfigs {Object} (Optional) Object literal of configs.
19062
 */
19063
widget.CellEditor = function(sType, oConfigs) {
19064
    // Point to one of the subclasses
19065
    if(sType && DT.Editors[sType]) {
19066
        lang.augmentObject(BCE, DT.Editors[sType]);
19067
        return new DT.Editors[sType](oConfigs);
19068
    }
19069
    else {
19070
        return new BCE(null, oConfigs);
19071
    }
19072
};
19073
 
19074
var CE = widget.CellEditor;
19075
 
19076
// Copy static members to CellEditor class
19077
lang.augmentObject(CE, BCE);
19078
 
19079
 
19080
})();
19081
 
19082
YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.9.0", build: "2800"});
19083
 
19084
}, '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"]});