~ubuntu-branches/ubuntu/natty/otrs2/natty-updates

« back to all changes in this revision

Viewing changes to var/httpd/htdocs/yui/2.7.0/build/datatable/datatable-debug.js

  • Committer: Package Import Robot
  • Author(s): Patrick Matthäi
  • Date: 2010-08-09 19:43:44 UTC
  • mfrom: (1.1.12)
  • Revision ID: package-import@ubuntu.com-20100809194344-absef1ut5mfj3qhv
Tags: 2.4.7+dfsg1-1
* Strip out yui from the source in the dfsg version.
  Closes: #591196
* Depend on libjs-yui and link to this package, instead of using the embedded
  yui version. This changes make the flash ticket statistics unuseable!
  Closes: #592146

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
 
Code licensed under the BSD License:
4
 
http://developer.yahoo.net/yui/license.txt
5
 
version: 2.7.0
6
 
*/
7
 
/**
8
 
 * 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:
9
 
 * <ul>
10
 
 *    <li><code>method</code> - {Function} REQUIRED the callback function.</li>
11
 
 *    <li><code>scope</code> - {Object} the scope from which to execute the callback.  Default is the global window scope.</li>
12
 
 *    <li><code>argument</code> - {Array} parameters to be passed to method as individual arguments.</li>
13
 
 *    <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>
14
 
 *    <li><code>until</code> - {Function} boolean function executed before each iteration.  Return true to indicate completion and proceed to the next callback.</li>
15
 
 *    <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>
16
 
 * </ul>
17
 
 *
18
 
 * @namespace YAHOO.util
19
 
 * @class Chain
20
 
 * @constructor
21
 
 * @param callback* {Function|Object} Any number of callbacks to initialize the queue
22
 
*/
23
 
YAHOO.util.Chain = function () {
24
 
    /**
25
 
     * The callback queue
26
 
     * @property q
27
 
     * @type {Array}
28
 
     * @private
29
 
     */
30
 
    this.q = [].slice.call(arguments);
31
 
 
32
 
    /**
33
 
     * Event fired when the callback queue is emptied via execution (not via
34
 
     * a call to chain.stop().
35
 
     * @event end
36
 
     */
37
 
    this.createEvent('end');
38
 
};
39
 
 
40
 
YAHOO.util.Chain.prototype = {
41
 
    /**
42
 
     * 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.
43
 
     * @property id
44
 
     * @type {number}
45
 
     * @private
46
 
     */
47
 
    id   : 0,
48
 
 
49
 
    /**
50
 
     * Begin executing the chain, or resume execution from the last paused position.
51
 
     * @method run
52
 
     * @return {Chain} the Chain instance
53
 
     */
54
 
    run : function () {
55
 
        // Grab the first callback in the queue
56
 
        var c  = this.q[0],
57
 
            fn;
58
 
 
59
 
        // If there is no callback in the queue or the Chain is currently
60
 
        // in an execution mode, return
61
 
        if (!c) {
62
 
            this.fireEvent('end');
63
 
            return this;
64
 
        } else if (this.id) {
65
 
            return this;
66
 
        }
67
 
 
68
 
        fn = c.method || c;
69
 
 
70
 
        if (typeof fn === 'function') {
71
 
            var o    = c.scope || {},
72
 
                args = c.argument || [],
73
 
                ms   = c.timeout || 0,
74
 
                me   = this;
75
 
                
76
 
            if (!(args instanceof Array)) {
77
 
                args = [args];
78
 
            }
79
 
 
80
 
            // Execute immediately if the callback timeout is negative.
81
 
            if (ms < 0) {
82
 
                this.id = ms;
83
 
                if (c.until) {
84
 
                    for (;!c.until();) {
85
 
                        // Execute the callback from scope, with argument
86
 
                        fn.apply(o,args);
87
 
                    }
88
 
                } else if (c.iterations) {
89
 
                    for (;c.iterations-- > 0;) {
90
 
                        fn.apply(o,args);
91
 
                    }
92
 
                } else {
93
 
                    fn.apply(o,args);
94
 
                }
95
 
                this.q.shift();
96
 
                this.id = 0;
97
 
                return this.run();
98
 
            } else {
99
 
                // If the until condition is set, check if we're done
100
 
                if (c.until) {
101
 
                    if (c.until()) {
102
 
                        // Shift this callback from the queue and execute the next
103
 
                        // callback
104
 
                        this.q.shift();
105
 
                        return this.run();
106
 
                    }
107
 
                // Otherwise if either iterations is not set or we're
108
 
                // executing the last iteration, shift callback from the queue
109
 
                } else if (!c.iterations || !--c.iterations) {
110
 
                    this.q.shift();
111
 
                }
112
 
 
113
 
                // Otherwise set to execute after the configured timeout
114
 
                this.id = setTimeout(function () {
115
 
                    // Execute the callback from scope, with argument
116
 
                    fn.apply(o,args);
117
 
                    // Check if the Chain was not paused from inside the callback
118
 
                    if (me.id) {
119
 
                        // Indicate ready to run state
120
 
                        me.id = 0;
121
 
                        // Start the fun all over again
122
 
                        me.run();
123
 
                    }
124
 
                },ms);
125
 
            }
126
 
        }
127
 
 
128
 
        return this;
129
 
    },
130
 
    
131
 
    /**
132
 
     * Add a callback to the end of the queue
133
 
     * @method add
134
 
     * @param c {Function|Object} the callback function ref or object literal
135
 
     * @return {Chain} the Chain instance
136
 
     */
137
 
    add  : function (c) {
138
 
        this.q.push(c);
139
 
        return this;
140
 
    },
141
 
 
142
 
    /**
143
 
     * Pause the execution of the Chain after the current execution of the
144
 
     * current callback completes.  If called interstitially, clears the
145
 
     * timeout for the pending callback. Paused Chains can be restarted with
146
 
     * chain.run()
147
 
     * @method pause
148
 
     * @return {Chain} the Chain instance
149
 
     */
150
 
    pause: function () {
151
 
        clearTimeout(this.id);
152
 
        this.id = 0;
153
 
        return this;
154
 
    },
155
 
 
156
 
    /**
157
 
     * Stop and clear the Chain's queue after the current execution of the
158
 
     * current callback completes.
159
 
     * @method stop
160
 
     * @return {Chain} the Chain instance
161
 
     */
162
 
    stop : function () { 
163
 
        this.pause();
164
 
        this.q = [];
165
 
        return this;
166
 
    }
167
 
};
168
 
YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider);
169
 
 
170
 
/****************************************************************************/
171
 
/****************************************************************************/
172
 
/****************************************************************************/
173
 
 
174
 
/**
175
 
 * The ColumnSet class defines and manages a DataTable's Columns,
176
 
 * including nested hierarchies and access to individual Column instances.
177
 
 *
178
 
 * @namespace YAHOO.widget
179
 
 * @class ColumnSet
180
 
 * @uses YAHOO.util.EventProvider
181
 
 * @constructor
182
 
 * @param aDefinitions {Object[]} Array of object literals that define cells in
183
 
 * the THEAD.
184
 
 */
185
 
YAHOO.widget.ColumnSet = function(aDefinitions) {
186
 
    this._sId = "yui-cs" + YAHOO.widget.ColumnSet._nCount;
187
 
 
188
 
    // First clone the defs
189
 
    aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions);
190
 
    this._init(aDefinitions);
191
 
 
192
 
    YAHOO.widget.ColumnSet._nCount++;
193
 
    YAHOO.log("ColumnSet initialized", "info", this.toString());
194
 
};
195
 
 
196
 
/////////////////////////////////////////////////////////////////////////////
197
 
//
198
 
// Private member variables
199
 
//
200
 
/////////////////////////////////////////////////////////////////////////////
201
 
 
202
 
/**
203
 
 * Internal class variable to index multiple ColumnSet instances.
204
 
 *
205
 
 * @property ColumnSet._nCount
206
 
 * @type Number
207
 
 * @private
208
 
 * @static
209
 
 */
210
 
YAHOO.widget.ColumnSet._nCount = 0;
211
 
 
212
 
YAHOO.widget.ColumnSet.prototype = {
213
 
    /**
214
 
     * Unique instance name.
215
 
     *
216
 
     * @property _sId
217
 
     * @type String
218
 
     * @private
219
 
     */
220
 
    _sId : null,
221
 
 
222
 
    /**
223
 
     * Array of object literal Column definitions passed to the constructor.
224
 
     *
225
 
     * @property _aDefinitions
226
 
     * @type Object[]
227
 
     * @private
228
 
     */
229
 
    _aDefinitions : null,
230
 
 
231
 
    /////////////////////////////////////////////////////////////////////////////
232
 
    //
233
 
    // Public member variables
234
 
    //
235
 
    /////////////////////////////////////////////////////////////////////////////
236
 
 
237
 
    /**
238
 
     * Top-down tree representation of Column hierarchy.
239
 
     *
240
 
     * @property tree
241
 
     * @type YAHOO.widget.Column[]
242
 
     */
243
 
    tree : null,
244
 
 
245
 
    /**
246
 
     * Flattened representation of all Columns.
247
 
     *
248
 
     * @property flat
249
 
     * @type YAHOO.widget.Column[]
250
 
     * @default []
251
 
     */
252
 
    flat : null,
253
 
 
254
 
    /**
255
 
     * Array of Columns that map one-to-one to a table column.
256
 
     *
257
 
     * @property keys
258
 
     * @type YAHOO.widget.Column[]
259
 
     * @default []
260
 
     */
261
 
    keys : null,
262
 
 
263
 
    /**
264
 
     * ID index of nested parent hierarchies for HEADERS accessibility attribute.
265
 
     *
266
 
     * @property headers
267
 
     * @type String[]
268
 
     * @default []
269
 
     */
270
 
    headers : null,
271
 
 
272
 
    /////////////////////////////////////////////////////////////////////////////
273
 
    //
274
 
    // Private methods
275
 
    //
276
 
    /////////////////////////////////////////////////////////////////////////////
277
 
 
278
 
    /**
279
 
     * Initializes ColumnSet instance with data from Column definitions.
280
 
     *
281
 
     * @method _init
282
 
     * @param aDefinitions {Object[]} Array of object literals that define cells in
283
 
     * the THEAD .
284
 
     * @private
285
 
     */
286
 
 
287
 
    _init : function(aDefinitions) {        
288
 
        // DOM tree representation of all Columns
289
 
        var tree = [];
290
 
        // Flat representation of all Columns
291
 
        var flat = [];
292
 
        // Flat representation of only Columns that are meant to display data
293
 
        var keys = [];
294
 
        // Array of HEADERS attribute values for all keys in the "keys" array
295
 
        var headers = [];
296
 
 
297
 
        // Tracks current node list depth being tracked
298
 
        var nodeDepth = -1;
299
 
 
300
 
        // Internal recursive function to define Column instances
301
 
        var parseColumns = function(nodeList, parent) {
302
 
            // One level down
303
 
            nodeDepth++;
304
 
 
305
 
            // Create corresponding tree node if not already there for this depth
306
 
            if(!tree[nodeDepth]) {
307
 
                tree[nodeDepth] = [];
308
 
            }
309
 
 
310
 
 
311
 
            // Parse each node at this depth for attributes and any children
312
 
            for(var j=0; j<nodeList.length; j++) {
313
 
                var currentNode = nodeList[j];
314
 
 
315
 
                // Instantiate a new Column for each node
316
 
                var oColumn = new YAHOO.widget.Column(currentNode);
317
 
                
318
 
                // Cross-reference Column ID back to the original object literal definition
319
 
                currentNode.yuiColumnId = oColumn._sId;
320
 
                
321
 
                // Add the new Column to the flat list
322
 
                flat.push(oColumn);
323
 
 
324
 
                // Assign its parent as an attribute, if applicable
325
 
                if(parent) {
326
 
                    oColumn._oParent = parent;
327
 
                }
328
 
 
329
 
                // The Column has descendants
330
 
                if(YAHOO.lang.isArray(currentNode.children)) {
331
 
                    oColumn.children = currentNode.children;
332
 
 
333
 
                    // Determine COLSPAN value for this Column
334
 
                    var terminalChildNodes = 0;
335
 
                    var countTerminalChildNodes = function(ancestor) {
336
 
                        var descendants = ancestor.children;
337
 
                        // Drill down each branch and count terminal nodes
338
 
                        for(var k=0; k<descendants.length; k++) {
339
 
                            // Keep drilling down
340
 
                            if(YAHOO.lang.isArray(descendants[k].children)) {
341
 
                                countTerminalChildNodes(descendants[k]);
342
 
                            }
343
 
                            // Reached branch terminus
344
 
                            else {
345
 
                                terminalChildNodes++;
346
 
                            }
347
 
                        }
348
 
                    };
349
 
                    countTerminalChildNodes(currentNode);
350
 
                    oColumn._nColspan = terminalChildNodes;
351
 
 
352
 
                    // Cascade certain properties to children if not defined on their own
353
 
                    var currentChildren = currentNode.children;
354
 
                    for(var k=0; k<currentChildren.length; k++) {
355
 
                        var child = currentChildren[k];
356
 
                        if(oColumn.className && (child.className === undefined)) {
357
 
                            child.className = oColumn.className;
358
 
                        }
359
 
                        if(oColumn.editor && (child.editor === undefined)) {
360
 
                            child.editor = oColumn.editor;
361
 
                        }
362
 
                        //TODO: Deprecated
363
 
                        if(oColumn.editorOptions && (child.editorOptions === undefined)) {
364
 
                            child.editorOptions = oColumn.editorOptions;
365
 
                        }
366
 
                        if(oColumn.formatter && (child.formatter === undefined)) {
367
 
                            child.formatter = oColumn.formatter;
368
 
                        }
369
 
                        if(oColumn.resizeable && (child.resizeable === undefined)) {
370
 
                            child.resizeable = oColumn.resizeable;
371
 
                        }
372
 
                        if(oColumn.sortable && (child.sortable === undefined)) {
373
 
                            child.sortable = oColumn.sortable;
374
 
                        }
375
 
                        if(oColumn.hidden) {
376
 
                            child.hidden = true;
377
 
                        }
378
 
                        if(oColumn.width && (child.width === undefined)) {
379
 
                            child.width = oColumn.width;
380
 
                        }
381
 
                        if(oColumn.minWidth && (child.minWidth === undefined)) {
382
 
                            child.minWidth = oColumn.minWidth;
383
 
                        }
384
 
                        if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) {
385
 
                            child.maxAutoWidth = oColumn.maxAutoWidth;
386
 
                        }
387
 
                        // Backward compatibility
388
 
                        if(oColumn.type && (child.type === undefined)) {
389
 
                            child.type = oColumn.type;
390
 
                        }
391
 
                        if(oColumn.type && !oColumn.formatter) {
392
 
                            YAHOO.log("The property type has been" +
393
 
                            " deprecated in favor of formatter", "warn", oColumn.toString());
394
 
                            oColumn.formatter = oColumn.type;
395
 
                        }
396
 
                        if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
397
 
                            YAHOO.log("The property text has been" +
398
 
                            " deprecated in favor of label", "warn", oColumn.toString());
399
 
                            oColumn.label = oColumn.text;
400
 
                        }
401
 
                        if(oColumn.parser) {
402
 
                            YAHOO.log("The property parser is no longer supported",
403
 
                            "warn", this.toString());
404
 
                        }
405
 
                        if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
406
 
                                (oColumn.sortOptions.descFunction))) {
407
 
                            YAHOO.log("The properties sortOptions.ascFunction and " +
408
 
                            " sortOptions.descFunction have been deprecated in favor " +
409
 
                            " of sortOptions.sortFunction", "warn", oColumn.toString());
410
 
                        }
411
 
                    }
412
 
 
413
 
                    // The children themselves must also be parsed for Column instances
414
 
                    if(!tree[nodeDepth+1]) {
415
 
                        tree[nodeDepth+1] = [];
416
 
                    }
417
 
                    parseColumns(currentChildren, oColumn);
418
 
                }
419
 
                // This Column does not have any children
420
 
                else {
421
 
                    oColumn._nKeyIndex = keys.length;
422
 
                    oColumn._nColspan = 1;
423
 
                    keys.push(oColumn);
424
 
                }
425
 
 
426
 
                // Add the Column to the top-down tree
427
 
                tree[nodeDepth].push(oColumn);
428
 
            }
429
 
            nodeDepth--;
430
 
        };
431
 
 
432
 
        // Parse out Column instances from the array of object literals
433
 
        if(YAHOO.lang.isArray(aDefinitions)) {
434
 
            parseColumns(aDefinitions);
435
 
 
436
 
            // Store the array
437
 
            this._aDefinitions = aDefinitions;
438
 
        }
439
 
        else {
440
 
            YAHOO.log("Could not initialize ColumnSet due to invalid definitions","error");
441
 
            return null;
442
 
        }
443
 
 
444
 
        var i;
445
 
 
446
 
        // Determine ROWSPAN value for each Column in the tree
447
 
        var parseTreeForRowspan = function(tree) {
448
 
            var maxRowDepth = 1;
449
 
            var currentRow;
450
 
            var currentColumn;
451
 
 
452
 
            // Calculate the max depth of descendants for this row
453
 
            var countMaxRowDepth = function(row, tmpRowDepth) {
454
 
                tmpRowDepth = tmpRowDepth || 1;
455
 
 
456
 
                for(var n=0; n<row.length; n++) {
457
 
                    var col = row[n];
458
 
                    // Column has children, so keep counting
459
 
                    if(YAHOO.lang.isArray(col.children)) {
460
 
                        tmpRowDepth++;
461
 
                        countMaxRowDepth(col.children, tmpRowDepth);
462
 
                        tmpRowDepth--;
463
 
                    }
464
 
                    // No children, is it the max depth?
465
 
                    else {
466
 
                        if(tmpRowDepth > maxRowDepth) {
467
 
                            maxRowDepth = tmpRowDepth;
468
 
                        }
469
 
                    }
470
 
 
471
 
                }
472
 
            };
473
 
 
474
 
            // Count max row depth for each row
475
 
            for(var m=0; m<tree.length; m++) {
476
 
                currentRow = tree[m];
477
 
                countMaxRowDepth(currentRow);
478
 
 
479
 
                // Assign the right ROWSPAN values to each Column in the row
480
 
                for(var p=0; p<currentRow.length; p++) {
481
 
                    currentColumn = currentRow[p];
482
 
                    if(!YAHOO.lang.isArray(currentColumn.children)) {
483
 
                        currentColumn._nRowspan = maxRowDepth;
484
 
                    }
485
 
                    else {
486
 
                        currentColumn._nRowspan = 1;
487
 
                    }
488
 
                }
489
 
 
490
 
                // Reset counter for next row
491
 
                maxRowDepth = 1;
492
 
            }
493
 
        };
494
 
        parseTreeForRowspan(tree);
495
 
 
496
 
        // Store tree index values
497
 
        for(i=0; i<tree[0].length; i++) {
498
 
            tree[0][i]._nTreeIndex = i;
499
 
        }
500
 
 
501
 
        // Store header relationships in an array for HEADERS attribute
502
 
        var recurseAncestorsForHeaders = function(i, oColumn) {
503
 
            headers[i].push(oColumn.getSanitizedKey());
504
 
            if(oColumn._oParent) {
505
 
                recurseAncestorsForHeaders(i, oColumn._oParent);
506
 
            }
507
 
        };
508
 
        for(i=0; i<keys.length; i++) {
509
 
            headers[i] = [];
510
 
            recurseAncestorsForHeaders(i, keys[i]);
511
 
            headers[i] = headers[i].reverse();
512
 
        }
513
 
 
514
 
        // Save to the ColumnSet instance
515
 
        this.tree = tree;
516
 
        this.flat = flat;
517
 
        this.keys = keys;
518
 
        this.headers = headers;
519
 
    },
520
 
 
521
 
    /////////////////////////////////////////////////////////////////////////////
522
 
    //
523
 
    // Public methods
524
 
    //
525
 
    /////////////////////////////////////////////////////////////////////////////
526
 
 
527
 
    /**
528
 
     * Returns unique name of the ColumnSet instance.
529
 
     *
530
 
     * @method getId
531
 
     * @return {String} Unique name of the ColumnSet instance.
532
 
     */
533
 
 
534
 
    getId : function() {
535
 
        return this._sId;
536
 
    },
537
 
 
538
 
    /**
539
 
     * ColumnSet instance name, for logging.
540
 
     *
541
 
     * @method toString
542
 
     * @return {String} Unique name of the ColumnSet instance.
543
 
     */
544
 
 
545
 
    toString : function() {
546
 
        return "ColumnSet instance " + this._sId;
547
 
    },
548
 
 
549
 
    /**
550
 
     * Public accessor to the definitions array.
551
 
     *
552
 
     * @method getDefinitions
553
 
     * @return {Object[]} Array of object literal Column definitions.
554
 
     */
555
 
 
556
 
    getDefinitions : function() {
557
 
        var aDefinitions = this._aDefinitions;
558
 
        
559
 
        // Internal recursive function to define Column instances
560
 
        var parseColumns = function(nodeList, oSelf) {
561
 
            // Parse each node at this depth for attributes and any children
562
 
            for(var j=0; j<nodeList.length; j++) {
563
 
                var currentNode = nodeList[j];
564
 
                
565
 
                // Get the Column for each node
566
 
                var oColumn = oSelf.getColumnById(currentNode.yuiColumnId);
567
 
                
568
 
                if(oColumn) {    
569
 
                    // Update the current values
570
 
                    var oDefinition = oColumn.getDefinition();
571
 
                    for(var name in oDefinition) {
572
 
                        if(YAHOO.lang.hasOwnProperty(oDefinition, name)) {
573
 
                            currentNode[name] = oDefinition[name];
574
 
                        }
575
 
                    }
576
 
                }
577
 
                            
578
 
                // The Column has descendants
579
 
                if(YAHOO.lang.isArray(currentNode.children)) {
580
 
                    // The children themselves must also be parsed for Column instances
581
 
                    parseColumns(currentNode.children, oSelf);
582
 
                }
583
 
            }
584
 
        };
585
 
 
586
 
        parseColumns(aDefinitions, this);
587
 
        this._aDefinitions = aDefinitions;
588
 
        return aDefinitions;
589
 
    },
590
 
 
591
 
    /**
592
 
     * Returns Column instance with given ID.
593
 
     *
594
 
     * @method getColumnById
595
 
     * @param column {String} Column ID.
596
 
     * @return {YAHOO.widget.Column} Column instance.
597
 
     */
598
 
 
599
 
    getColumnById : function(column) {
600
 
        if(YAHOO.lang.isString(column)) {
601
 
            var allColumns = this.flat;
602
 
            for(var i=allColumns.length-1; i>-1; i--) {
603
 
                if(allColumns[i]._sId === column) {
604
 
                    return allColumns[i];
605
 
                }
606
 
            }
607
 
        }
608
 
        return null;
609
 
    },
610
 
 
611
 
    /**
612
 
     * Returns Column instance with given key or ColumnSet key index.
613
 
     *
614
 
     * @method getColumn
615
 
     * @param column {String | Number} Column key or ColumnSet key index.
616
 
     * @return {YAHOO.widget.Column} Column instance.
617
 
     */
618
 
 
619
 
    getColumn : function(column) {
620
 
        if(YAHOO.lang.isNumber(column) && this.keys[column]) {
621
 
            return this.keys[column];
622
 
        }
623
 
        else if(YAHOO.lang.isString(column)) {
624
 
            var allColumns = this.flat;
625
 
            var aColumns = [];
626
 
            for(var i=0; i<allColumns.length; i++) {
627
 
                if(allColumns[i].key === column) {
628
 
                    aColumns.push(allColumns[i]);
629
 
                }
630
 
            }
631
 
            if(aColumns.length === 1) {
632
 
                return aColumns[0];
633
 
            }
634
 
            else if(aColumns.length > 1) {
635
 
                return aColumns;
636
 
            }
637
 
        }
638
 
        return null;
639
 
    },
640
 
 
641
 
    /**
642
 
     * Public accessor returns array of given Column's desendants (if any), including itself.
643
 
     *
644
 
     * @method getDescendants
645
 
     * @parem {YAHOO.widget.Column} Column instance.
646
 
     * @return {Array} Array including the Column itself and all descendants (if any).
647
 
     */
648
 
    getDescendants : function(oColumn) {
649
 
        var oSelf = this;
650
 
        var allDescendants = [];
651
 
        var i;
652
 
 
653
 
        // Recursive function to loop thru all children
654
 
        var parse = function(oParent) {
655
 
            allDescendants.push(oParent);
656
 
            // This Column has children
657
 
            if(oParent.children) {
658
 
                for(i=0; i<oParent.children.length; i++) {
659
 
                    parse(oSelf.getColumn(oParent.children[i].key));
660
 
                }
661
 
            }
662
 
        };
663
 
        parse(oColumn);
664
 
 
665
 
        return allDescendants;
666
 
    }
667
 
};
668
 
 
669
 
/****************************************************************************/
670
 
/****************************************************************************/
671
 
/****************************************************************************/
672
 
 
673
 
/**
674
 
 * The Column class defines and manages attributes of DataTable Columns
675
 
 *
676
 
 * @namespace YAHOO.widget
677
 
 * @class Column
678
 
 * @constructor
679
 
 * @param oConfigs {Object} Object literal of definitions.
680
 
 */
681
 
YAHOO.widget.Column = function(oConfigs) {
682
 
    this._sId = "yui-col" + YAHOO.widget.Column._nCount;
683
 
    
684
 
    // Object literal defines Column attributes
685
 
    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
686
 
        for(var sConfig in oConfigs) {
687
 
            if(sConfig) {
688
 
                this[sConfig] = oConfigs[sConfig];
689
 
            }
690
 
        }
691
 
    }
692
 
 
693
 
    // Assign a key if not found
694
 
    if(!YAHOO.lang.isValue(this.key)) {
695
 
        this.key = "yui-dt-col" + YAHOO.widget.Column._nCount;
696
 
    }
697
 
    
698
 
    // Assign a field if not found, defaults to key
699
 
    if(!YAHOO.lang.isValue(this.field)) {
700
 
        this.field = this.key;
701
 
    }
702
 
 
703
 
    // Increment counter
704
 
    YAHOO.widget.Column._nCount++;
705
 
 
706
 
    // Backward compatibility
707
 
    if(this.width && !YAHOO.lang.isNumber(this.width)) {
708
 
        this.width = null;
709
 
        YAHOO.log("The Column property width must be a number", "warn", this.toString());
710
 
    }
711
 
    if(this.editor && YAHOO.lang.isString(this.editor)) {
712
 
        this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions);
713
 
        YAHOO.log("The Column property editor must be an instance of YAHOO.widget.CellEditor", "warn", this.toString());
714
 
    }
715
 
};
716
 
 
717
 
/////////////////////////////////////////////////////////////////////////////
718
 
//
719
 
// Private member variables
720
 
//
721
 
/////////////////////////////////////////////////////////////////////////////
722
 
 
723
 
YAHOO.lang.augmentObject(YAHOO.widget.Column, {
724
 
    /**
725
 
     * Internal class variable to index multiple Column instances.
726
 
     *
727
 
     * @property Column._nCount
728
 
     * @type Number
729
 
     * @private
730
 
     * @static
731
 
     */
732
 
    _nCount : 0,
733
 
 
734
 
    formatCheckbox : function(elCell, oRecord, oColumn, oData) {
735
 
        YAHOO.log("The method YAHOO.widget.Column.formatCheckbox() has been" +
736
 
        " deprecated in favor of YAHOO.widget.DataTable.formatCheckbox()", "warn",
737
 
        "YAHOO.widget.Column.formatCheckbox");
738
 
        YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
739
 
    },
740
 
 
741
 
    formatCurrency : function(elCell, oRecord, oColumn, oData) {
742
 
        YAHOO.log("The method YAHOO.widget.Column.formatCurrency() has been" +
743
 
        " deprecated in favor of YAHOO.widget.DataTable.formatCurrency()", "warn",
744
 
        "YAHOO.widget.Column.formatCurrency");
745
 
        YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
746
 
    },
747
 
 
748
 
    formatDate : function(elCell, oRecord, oColumn, oData) {
749
 
        YAHOO.log("The method YAHOO.widget.Column.formatDate() has been" +
750
 
        " deprecated in favor of YAHOO.widget.DataTable.formatDate()", "warn",
751
 
        "YAHOO.widget.Column.formatDate");
752
 
        YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
753
 
    },
754
 
 
755
 
    formatEmail : function(elCell, oRecord, oColumn, oData) {
756
 
        YAHOO.log("The method YAHOO.widget.Column.formatEmail() has been" +
757
 
        " deprecated in favor of YAHOO.widget.DataTable.formatEmail()", "warn",
758
 
        "YAHOO.widget.Column.formatEmail");
759
 
        YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
760
 
    },
761
 
 
762
 
    formatLink : function(elCell, oRecord, oColumn, oData) {
763
 
        YAHOO.log("The method YAHOO.widget.Column.formatLink() has been" +
764
 
        " deprecated in favor of YAHOO.widget.DataTable.formatLink()", "warn",
765
 
        "YAHOO.widget.Column.formatLink");
766
 
        YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
767
 
    },
768
 
 
769
 
    formatNumber : function(elCell, oRecord, oColumn, oData) {
770
 
        YAHOO.log("The method YAHOO.widget.Column.formatNumber() has been" +
771
 
        " deprecated in favor of YAHOO.widget.DataTable.formatNumber()", "warn",
772
 
        "YAHOO.widget.Column.formatNumber");
773
 
        YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
774
 
    },
775
 
 
776
 
    formatSelect : function(elCell, oRecord, oColumn, oData) {
777
 
        YAHOO.log("The method YAHOO.widget.Column.formatSelect() has been" +
778
 
        " deprecated in favor of YAHOO.widget.DataTable.formatDropdown()", "warn",
779
 
        "YAHOO.widget.Column.formatSelect");
780
 
        YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
781
 
    }
782
 
});
783
 
 
784
 
YAHOO.widget.Column.prototype = {
785
 
    /**
786
 
     * Unique String identifier assigned at instantiation.
787
 
     *
788
 
     * @property _sId
789
 
     * @type String
790
 
     * @private
791
 
     */
792
 
    _sId : null,
793
 
 
794
 
    /**
795
 
     * Reference to Column's current position index within its ColumnSet's keys
796
 
     * array, if applicable. This property only applies to non-nested and bottom-
797
 
     * level child Columns.
798
 
     *
799
 
     * @property _nKeyIndex
800
 
     * @type Number
801
 
     * @private
802
 
     */
803
 
    _nKeyIndex : null,
804
 
 
805
 
    /**
806
 
     * Reference to Column's current position index within its ColumnSet's tree
807
 
     * array, if applicable. This property only applies to non-nested and top-
808
 
     * level parent Columns.
809
 
     *
810
 
     * @property _nTreeIndex
811
 
     * @type Number
812
 
     * @private
813
 
     */
814
 
    _nTreeIndex : null,
815
 
 
816
 
    /**
817
 
     * Number of table cells the Column spans.
818
 
     *
819
 
     * @property _nColspan
820
 
     * @type Number
821
 
     * @private
822
 
     */
823
 
    _nColspan : 1,
824
 
 
825
 
    /**
826
 
     * Number of table rows the Column spans.
827
 
     *
828
 
     * @property _nRowspan
829
 
     * @type Number
830
 
     * @private
831
 
     */
832
 
    _nRowspan : 1,
833
 
 
834
 
    /**
835
 
     * Column's parent Column instance, or null.
836
 
     *
837
 
     * @property _oParent
838
 
     * @type YAHOO.widget.Column
839
 
     * @private
840
 
     */
841
 
    _oParent : null,
842
 
 
843
 
    /**
844
 
     * The DOM reference to the associated TH element.
845
 
     *
846
 
     * @property _elTh
847
 
     * @type HTMLElement
848
 
     * @private
849
 
     */
850
 
    _elTh : null,
851
 
 
852
 
    /**
853
 
     * The DOM reference to the associated TH element's liner DIV element.
854
 
     *
855
 
     * @property _elThLiner
856
 
     * @type HTMLElement
857
 
     * @private
858
 
     */
859
 
    _elThLiner : null,
860
 
 
861
 
    /**
862
 
     * The DOM reference to the associated TH element's label SPAN element.
863
 
     *
864
 
     * @property _elThLabel
865
 
     * @type HTMLElement
866
 
     * @private
867
 
     */
868
 
    _elThLabel : null,
869
 
 
870
 
    /**
871
 
     * The DOM reference to the associated resizerelement (if any).
872
 
     *
873
 
     * @property _elResizer
874
 
     * @type HTMLElement
875
 
     * @private
876
 
     */
877
 
    _elResizer : null,
878
 
 
879
 
    /**
880
 
     * Internal width tracker.
881
 
     *
882
 
     * @property _nWidth
883
 
     * @type Number
884
 
     * @private
885
 
     */
886
 
    _nWidth : null,
887
 
 
888
 
    /**
889
 
     * For unreg() purposes, a reference to the Column's DragDrop instance.
890
 
     *
891
 
     * @property _dd
892
 
     * @type YAHOO.util.DragDrop
893
 
     * @private
894
 
     */
895
 
    _dd : null,
896
 
 
897
 
    /**
898
 
     * For unreg() purposes, a reference to the Column resizer's DragDrop instance.
899
 
     *
900
 
     * @property _ddResizer
901
 
     * @type YAHOO.util.DragDrop
902
 
     * @private
903
 
     */
904
 
    _ddResizer : null,
905
 
 
906
 
    /////////////////////////////////////////////////////////////////////////////
907
 
    //
908
 
    // Public member variables
909
 
    //
910
 
    /////////////////////////////////////////////////////////////////////////////
911
 
 
912
 
    /**
913
 
     * Unique name, required.
914
 
     *
915
 
     * @property key
916
 
     * @type String
917
 
     */
918
 
    key : null,
919
 
 
920
 
    /**
921
 
     * Associated database field, or null.
922
 
     *
923
 
     * @property field
924
 
     * @type String
925
 
     */
926
 
    field : null,
927
 
 
928
 
    /**
929
 
     * Text or HTML for display as Column's label in the TH element.
930
 
     *
931
 
     * @property label
932
 
     * @type String
933
 
     */
934
 
    label : null,
935
 
 
936
 
    /**
937
 
     * Column head cell ABBR for accessibility.
938
 
     *
939
 
     * @property abbr
940
 
     * @type String
941
 
     */
942
 
    abbr : null,
943
 
 
944
 
    /**
945
 
     * Array of object literals that define children (nested headers) of a Column.
946
 
     *
947
 
     * @property children
948
 
     * @type Object[]
949
 
     */
950
 
    children : null,
951
 
 
952
 
    /**
953
 
     * Column width (in pixels).
954
 
     *
955
 
     * @property width
956
 
     * @type Number
957
 
     */
958
 
    width : null,
959
 
 
960
 
    /**
961
 
     * Minimum Column width (in pixels).
962
 
     *
963
 
     * @property minWidth
964
 
     * @type Number
965
 
     * @default null
966
 
     */
967
 
    minWidth : null,
968
 
 
969
 
    /**
970
 
     * When a width is not defined for a Column, maxAutoWidth defines an upper
971
 
     * limit that the Column should be auto-sized to. If resizeable is enabled, 
972
 
     * users may still resize to a greater width. Most useful for Columns intended
973
 
     * to hold long unbroken, unwrapped Strings, such as URLs, to prevent very
974
 
     * wide Columns from disrupting visual readability by inducing truncation.
975
 
     *
976
 
     * @property maxAutoWidth
977
 
     * @type Number
978
 
     * @default null
979
 
     */
980
 
    maxAutoWidth : null,
981
 
 
982
 
    /**
983
 
     * True if Column is in hidden state.
984
 
     *
985
 
     * @property hidden
986
 
     * @type Boolean
987
 
     * @default false     
988
 
     */
989
 
    hidden : false,
990
 
 
991
 
    /**
992
 
     * True if Column is in selected state.
993
 
     *
994
 
     * @property selected
995
 
     * @type Boolean
996
 
     * @default false     
997
 
     */
998
 
    selected : false,
999
 
 
1000
 
    /**
1001
 
     * Custom CSS class or array of classes to be applied to every cell in the Column.
1002
 
     *
1003
 
     * @property className
1004
 
     * @type String || String[]
1005
 
     */
1006
 
    className : null,
1007
 
 
1008
 
    /**
1009
 
     * Defines a format function.
1010
 
     *
1011
 
     * @property formatter
1012
 
     * @type String || HTMLFunction
1013
 
     */
1014
 
    formatter : null,
1015
 
    
1016
 
    /**
1017
 
     * Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
1018
 
     *
1019
 
     * @property currencyOptions
1020
 
     * @type Object
1021
 
     * @default null
1022
 
     */
1023
 
    currencyOptions : null,
1024
 
 
1025
 
    /**
1026
 
     * Config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
1027
 
     *
1028
 
     * @property dateOptions
1029
 
     * @type Object
1030
 
     * @default null
1031
 
     */
1032
 
    dateOptions : null,
1033
 
 
1034
 
    /**
1035
 
     * A CellEditor instance, otherwise Column is not editable.     
1036
 
     *
1037
 
     * @property editor
1038
 
     * @type YAHOO.widget.CellEditor
1039
 
     */
1040
 
    editor : null,
1041
 
 
1042
 
    /**
1043
 
     * True if Column is resizeable, false otherwise. The Drag & Drop Utility is
1044
 
     * required to enable this feature. Only bottom-level and non-nested Columns are
1045
 
     * resizeble. 
1046
 
     *
1047
 
     * @property resizeable
1048
 
     * @type Boolean
1049
 
     * @default false
1050
 
     */
1051
 
    resizeable : false,
1052
 
 
1053
 
    /**
1054
 
     * True if Column is sortable, false otherwise.
1055
 
     *
1056
 
     * @property sortable
1057
 
     * @type Boolean
1058
 
     * @default false
1059
 
     */
1060
 
    sortable : false,
1061
 
 
1062
 
    /**
1063
 
     * @property sortOptions.defaultOrder
1064
 
     * @deprecated Use sortOptions.defaultDir.
1065
 
     */
1066
 
    /**
1067
 
     * Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC.
1068
 
     *
1069
 
     * @property sortOptions.defaultDir
1070
 
     * @type String
1071
 
     * @default null
1072
 
     */
1073
 
    /**
1074
 
     * Custom field to sort on.
1075
 
     *
1076
 
     * @property sortOptions.field
1077
 
     * @type String
1078
 
     * @default null
1079
 
     */
1080
 
    /**
1081
 
     * Custom sort handler.
1082
 
     *
1083
 
     * @property sortOptions.sortFunction
1084
 
     * @type Function
1085
 
     * @default null
1086
 
     */
1087
 
    sortOptions : null,
1088
 
 
1089
 
 
1090
 
 
1091
 
 
1092
 
 
1093
 
 
1094
 
 
1095
 
 
1096
 
 
1097
 
 
1098
 
 
1099
 
 
1100
 
 
1101
 
 
1102
 
 
1103
 
    /////////////////////////////////////////////////////////////////////////////
1104
 
    //
1105
 
    // Public methods
1106
 
    //
1107
 
    /////////////////////////////////////////////////////////////////////////////
1108
 
 
1109
 
    /**
1110
 
     * Returns unique ID string.
1111
 
     *
1112
 
     * @method getId
1113
 
     * @return {String} Unique ID string.
1114
 
     */
1115
 
    getId : function() {
1116
 
        return this._sId;
1117
 
    },
1118
 
 
1119
 
    /**
1120
 
     * Column instance name, for logging.
1121
 
     *
1122
 
     * @method toString
1123
 
     * @return {String} Column's unique name.
1124
 
     */
1125
 
    toString : function() {
1126
 
        return "Column instance " + this._sId;
1127
 
    },
1128
 
 
1129
 
    /**
1130
 
     * Returns object literal definition.
1131
 
     *
1132
 
     * @method getDefinition
1133
 
     * @return {Object} Object literal definition.
1134
 
     */
1135
 
    getDefinition : function() {
1136
 
        var oDefinition = {};
1137
 
        
1138
 
        // Update the definition
1139
 
        oDefinition.abbr = this.abbr;
1140
 
        oDefinition.className = this.className;
1141
 
        oDefinition.editor = this.editor;
1142
 
        oDefinition.editorOptions = this.editorOptions; //TODO: deprecated
1143
 
        oDefinition.field = this.field;
1144
 
        oDefinition.formatter = this.formatter;
1145
 
        oDefinition.hidden = this.hidden;
1146
 
        oDefinition.key = this.key;
1147
 
        oDefinition.label = this.label;
1148
 
        oDefinition.minWidth = this.minWidth;
1149
 
        oDefinition.maxAutoWidth = this.maxAutoWidth;
1150
 
        oDefinition.resizeable = this.resizeable;
1151
 
        oDefinition.selected = this.selected;
1152
 
        oDefinition.sortable = this.sortable;
1153
 
        oDefinition.sortOptions = this.sortOptions;
1154
 
        oDefinition.width = this.width;
1155
 
 
1156
 
        return oDefinition;
1157
 
    },
1158
 
 
1159
 
    /**
1160
 
     * Returns unique Column key.
1161
 
     *
1162
 
     * @method getKey
1163
 
     * @return {String} Column key.
1164
 
     */
1165
 
    getKey : function() {
1166
 
        return this.key;
1167
 
    },
1168
 
    
1169
 
    /**
1170
 
     * Returns field.
1171
 
     *
1172
 
     * @method getField
1173
 
     * @return {String} Column field.
1174
 
     */
1175
 
    getField : function() {
1176
 
        return this.field;
1177
 
    },
1178
 
    
1179
 
    /**
1180
 
     * Returns Column key which has been sanitized for DOM (class and ID) usage
1181
 
     * starts with letter, contains only letters, numbers, hyphen, or period.
1182
 
     *
1183
 
     * @method getSanitizedKey
1184
 
     * @return {String} Sanitized Column key.
1185
 
     */
1186
 
    getSanitizedKey : function() {
1187
 
        return this.getKey().replace(/[^\w\-]/g,"");
1188
 
    },
1189
 
 
1190
 
    /**
1191
 
     * Public accessor returns Column's current position index within its
1192
 
     * ColumnSet's keys array, if applicable. Only non-nested and bottom-level
1193
 
     * child Columns will return a value.
1194
 
     *
1195
 
     * @method getKeyIndex
1196
 
     * @return {Number} Position index, or null.
1197
 
     */
1198
 
    getKeyIndex : function() {
1199
 
        return this._nKeyIndex;
1200
 
    },
1201
 
 
1202
 
    /**
1203
 
     * Public accessor returns Column's current position index within its
1204
 
     * ColumnSet's tree array, if applicable. Only non-nested and top-level parent
1205
 
     * Columns will return a value;
1206
 
     *
1207
 
     * @method getTreeIndex
1208
 
     * @return {Number} Position index, or null.
1209
 
     */
1210
 
    getTreeIndex : function() {
1211
 
        return this._nTreeIndex;
1212
 
    },
1213
 
 
1214
 
    /**
1215
 
     * Public accessor returns Column's parent instance if any, or null otherwise.
1216
 
     *
1217
 
     * @method getParent
1218
 
     * @return {YAHOO.widget.Column} Column's parent instance.
1219
 
     */
1220
 
    getParent : function() {
1221
 
        return this._oParent;
1222
 
    },
1223
 
 
1224
 
    /**
1225
 
     * Public accessor returns Column's calculated COLSPAN value.
1226
 
     *
1227
 
     * @method getColspan
1228
 
     * @return {Number} Column's COLSPAN value.
1229
 
     */
1230
 
    getColspan : function() {
1231
 
        return this._nColspan;
1232
 
    },
1233
 
    // Backward compatibility
1234
 
    getColSpan : function() {
1235
 
        YAHOO.log("The method getColSpan() has been" +
1236
 
        " deprecated in favor of getColspan()", "warn", this.toString());
1237
 
        return this.getColspan();
1238
 
    },
1239
 
 
1240
 
    /**
1241
 
     * Public accessor returns Column's calculated ROWSPAN value.
1242
 
     *
1243
 
     * @method getRowspan
1244
 
     * @return {Number} Column's ROWSPAN value.
1245
 
     */
1246
 
    getRowspan : function() {
1247
 
        return this._nRowspan;
1248
 
    },
1249
 
 
1250
 
    /**
1251
 
     * Returns DOM reference to the key TH element.
1252
 
     *
1253
 
     * @method getThEl
1254
 
     * @return {HTMLElement} TH element.
1255
 
     */
1256
 
    getThEl : function() {
1257
 
        return this._elTh;
1258
 
    },
1259
 
 
1260
 
    /**
1261
 
     * Returns DOM reference to the TH's liner DIV element. Introduced since
1262
 
     * resizeable Columns may have an extra resizer liner, making the DIV liner
1263
 
     * not reliably the TH element's first child.               
1264
 
     *
1265
 
     * @method getThLInerEl
1266
 
     * @return {HTMLElement} TH element.
1267
 
     */
1268
 
    getThLinerEl : function() {
1269
 
        return this._elThLiner;
1270
 
    },
1271
 
    
1272
 
    /**
1273
 
     * Returns DOM reference to the resizer element, or null.
1274
 
     *
1275
 
     * @method getResizerEl
1276
 
     * @return {HTMLElement} DIV element.
1277
 
     */
1278
 
    getResizerEl : function() {
1279
 
        return this._elResizer;
1280
 
    },
1281
 
 
1282
 
    // Backward compatibility
1283
 
    /**
1284
 
     * @method getColEl
1285
 
     * @deprecated Use getThEl
1286
 
     */
1287
 
    getColEl : function() {
1288
 
        YAHOO.log("The method getColEl() has been" +
1289
 
        " deprecated in favor of getThEl()", "warn",
1290
 
        this.toString());
1291
 
        return this.getThEl();
1292
 
    },
1293
 
    getIndex : function() {
1294
 
        YAHOO.log("The method getIndex() has been" +
1295
 
        " deprecated in favor of getKeyIndex()", "warn",
1296
 
        this.toString());
1297
 
        return this.getKeyIndex();
1298
 
    },
1299
 
    format : function() {
1300
 
        YAHOO.log("The method format() has been deprecated in favor of the " +
1301
 
        "DataTable method formatCell()", "error", this.toString());
1302
 
    }
1303
 
};
1304
 
 
1305
 
/****************************************************************************/
1306
 
/****************************************************************************/
1307
 
/****************************************************************************/
1308
 
 
1309
 
/**
1310
 
 * Sort static utility to support Column sorting.
1311
 
 *
1312
 
 * @namespace YAHOO.util
1313
 
 * @class Sort
1314
 
 * @static
1315
 
 */
1316
 
YAHOO.util.Sort = {
1317
 
    /////////////////////////////////////////////////////////////////////////////
1318
 
    //
1319
 
    // Public methods
1320
 
    //
1321
 
    /////////////////////////////////////////////////////////////////////////////
1322
 
 
1323
 
    /**
1324
 
     * Comparator function for simple case-insensitive string sorting.
1325
 
     *
1326
 
     * @method compare
1327
 
     * @param a {Object} First sort argument.
1328
 
     * @param b {Object} Second sort argument.
1329
 
     * @param desc {Boolean} True if sort direction is descending, false if
1330
 
     * sort direction is ascending.
1331
 
     */
1332
 
    compare: function(a, b, desc) {
1333
 
        if((a === null) || (typeof a == "undefined")) {
1334
 
            if((b === null) || (typeof b == "undefined")) {
1335
 
                return 0;
1336
 
            }
1337
 
            else {
1338
 
                return 1;
1339
 
            }
1340
 
        }
1341
 
        else if((b === null) || (typeof b == "undefined")) {
1342
 
            return -1;
1343
 
        }
1344
 
 
1345
 
        if(a.constructor == String) {
1346
 
            a = a.toLowerCase();
1347
 
        }
1348
 
        if(b.constructor == String) {
1349
 
            b = b.toLowerCase();
1350
 
        }
1351
 
        if(a < b) {
1352
 
            return (desc) ? 1 : -1;
1353
 
        }
1354
 
        else if (a > b) {
1355
 
            return (desc) ? -1 : 1;
1356
 
        }
1357
 
        else {
1358
 
            return 0;
1359
 
        }
1360
 
    }
1361
 
};
1362
 
 
1363
 
/****************************************************************************/
1364
 
/****************************************************************************/
1365
 
/****************************************************************************/
1366
 
 
1367
 
/**
1368
 
 * ColumnDD subclasses DragDrop to support rearrangeable Columns.
1369
 
 *
1370
 
 * @namespace YAHOO.util
1371
 
 * @class ColumnDD
1372
 
 * @extends YAHOO.util.DDProxy
1373
 
 * @constructor
1374
 
 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
1375
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
1376
 
 * @param elTh {HTMLElement} TH element reference.
1377
 
 * @param elTarget {HTMLElement} Drag target element.
1378
 
 */
1379
 
YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) {
1380
 
    if(oDataTable && oColumn && elTh && elTarget) {
1381
 
        this.datatable = oDataTable;
1382
 
        this.table = oDataTable.getTableEl();
1383
 
        this.column = oColumn;
1384
 
        this.headCell = elTh;
1385
 
        this.pointer = elTarget;
1386
 
        this.newIndex = null;
1387
 
        this.init(elTh);
1388
 
        this.initFrame(); // Needed for DDProxy
1389
 
        this.invalidHandleTypes = {};
1390
 
 
1391
 
        // Set top/bottom padding to account for children of nested columns
1392
 
        this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0);
1393
 
 
1394
 
        YAHOO.util.Event.on(window, 'resize', function() {
1395
 
            this.initConstraints();
1396
 
        }, this, true);
1397
 
    }
1398
 
    else {
1399
 
        YAHOO.log("Column dragdrop could not be created","warn",oDataTable.toString());
1400
 
    }
1401
 
};
1402
 
 
1403
 
if(YAHOO.util.DDProxy) {
1404
 
    YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, {
1405
 
        initConstraints: function() {
1406
 
            //Get the top, right, bottom and left positions
1407
 
            var region = YAHOO.util.Dom.getRegion(this.table),
1408
 
                //Get the element we are working on
1409
 
                el = this.getEl(),
1410
 
                //Get the xy position of it
1411
 
                xy = YAHOO.util.Dom.getXY(el),
1412
 
                //Get the width and height
1413
 
                width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10),
1414
 
                height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10),
1415
 
                //Set left to x minus left
1416
 
                left = ((xy[0] - region.left) + 15), //Buffer of 15px
1417
 
                //Set right to right minus x minus width
1418
 
                right = ((region.right - xy[0] - width) + 15);
1419
 
    
1420
 
            //Set the constraints based on the above calculations
1421
 
            this.setXConstraint(left, right);
1422
 
            this.setYConstraint(10, 10);            
1423
 
        },
1424
 
        _resizeProxy: function() {
1425
 
            this.constructor.superclass._resizeProxy.apply(this, arguments);
1426
 
            var dragEl = this.getDragEl(),
1427
 
                el = this.getEl();
1428
 
 
1429
 
            YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px');
1430
 
            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block');
1431
 
            var xy = YAHOO.util.Dom.getXY(el);
1432
 
            YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
1433
 
            
1434
 
            YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px");
1435
 
            YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
1436
 
            YAHOO.util.Dom.setXY(this.dragEl, xy);
1437
 
        },
1438
 
        onMouseDown: function() {
1439
 
                this.initConstraints();
1440
 
                this.resetConstraints();
1441
 
        },
1442
 
        clickValidator: function(e) {
1443
 
            if(!this.column.hidden) {
1444
 
                var target = YAHOO.util.Event.getTarget(e);
1445
 
                return ( this.isValidHandleChild(target) &&
1446
 
                            (this.id == this.handleElId ||
1447
 
                                this.DDM.handleWasClicked(target, this.id)) );
1448
 
            }
1449
 
        },
1450
 
        onDragOver: function(ev, id) {
1451
 
            // Validate target as a Column
1452
 
            var target = this.datatable.getColumn(id);
1453
 
            if(target) {                
1454
 
                // Validate target as a top-level parent
1455
 
                var targetIndex = target.getTreeIndex();
1456
 
                while((targetIndex === null) && target.getParent()) {
1457
 
                    target = target.getParent();
1458
 
                    targetIndex = target.getTreeIndex();
1459
 
                }
1460
 
                if(targetIndex !== null) {
1461
 
                    // Are we placing to left or right of target?
1462
 
                    var elTarget = target.getThEl();
1463
 
                    var newIndex = targetIndex;
1464
 
                    var mouseX = YAHOO.util.Event.getPageX(ev),
1465
 
                        targetX = YAHOO.util.Dom.getX(elTarget),
1466
 
                        midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2),
1467
 
                        currentIndex =  this.column.getTreeIndex();
1468
 
                    
1469
 
                    if (mouseX < midX) {
1470
 
                       YAHOO.util.Dom.setX(this.pointer, targetX);
1471
 
                    } else {
1472
 
                        var targetWidth = parseInt(elTarget.offsetWidth, 10);
1473
 
                        YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth));
1474
 
                        newIndex++;
1475
 
                    }
1476
 
                    if (targetIndex > currentIndex) {
1477
 
                        newIndex--;
1478
 
                    }
1479
 
                    if(newIndex < 0) {
1480
 
                        newIndex = 0;
1481
 
                    }
1482
 
                    else if(newIndex > this.datatable.getColumnSet().tree[0].length) {
1483
 
                        newIndex = this.datatable.getColumnSet().tree[0].length;
1484
 
                    }
1485
 
                    this.newIndex = newIndex;
1486
 
                }
1487
 
            }
1488
 
        },
1489
 
        onDragDrop: function() {
1490
 
            this.datatable.reorderColumn(this.column, this.newIndex);
1491
 
        },
1492
 
        endDrag: function() {
1493
 
            this.newIndex = null;
1494
 
            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none');
1495
 
        }
1496
 
    });
1497
 
}
1498
 
 
1499
 
/****************************************************************************/
1500
 
/****************************************************************************/
1501
 
/****************************************************************************/
1502
 
 
1503
 
/**
1504
 
 * ColumnResizer subclasses DragDrop to support resizeable Columns.
1505
 
 *
1506
 
 * @namespace YAHOO.util
1507
 
 * @class ColumnResizer
1508
 
 * @extends YAHOO.util.DDProxy
1509
 
 * @constructor
1510
 
 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
1511
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
1512
 
 * @param elTh {HTMLElement} TH element reference.
1513
 
 * @param sHandleElId {String} DOM ID of the handle element that causes the resize.
1514
 
 * @param elProxy {HTMLElement} Resizer proxy element.
1515
 
 */
1516
 
YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) {
1517
 
    if(oDataTable && oColumn && elTh && sHandleId) {
1518
 
        this.datatable = oDataTable;
1519
 
        this.column = oColumn;
1520
 
        this.headCell = elTh;
1521
 
        this.headCellLiner = oColumn.getThLinerEl();
1522
 
        this.resizerLiner = elTh.firstChild;
1523
 
        this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id});
1524
 
        this.initFrame(); // Needed for proxy
1525
 
        this.resetResizerEl(); // Needed when rowspan > 0
1526
 
 
1527
 
        // Set right padding for bug 1858462
1528
 
        this.setPadding(0, 1, 0, 0);
1529
 
    }
1530
 
    else {
1531
 
        YAHOO.log("Column resizer could not be created","warn",oDataTable.toString());
1532
 
    }
1533
 
};
1534
 
 
1535
 
if(YAHOO.util.DD) {
1536
 
    YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, {
1537
 
        /////////////////////////////////////////////////////////////////////////////
1538
 
        //
1539
 
        // Public methods
1540
 
        //
1541
 
        /////////////////////////////////////////////////////////////////////////////
1542
 
        /**
1543
 
         * Resets resizer element.
1544
 
         *
1545
 
         * @method resetResizerEl
1546
 
         */
1547
 
        resetResizerEl : function() {
1548
 
            var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style;
1549
 
            resizerStyle.left = "auto";
1550
 
            resizerStyle.right = 0;
1551
 
            resizerStyle.top = "auto";
1552
 
            resizerStyle.bottom = 0;
1553
 
            resizerStyle.height = this.headCell.offsetHeight+"px";
1554
 
        },
1555
 
    
1556
 
        /////////////////////////////////////////////////////////////////////////////
1557
 
        //
1558
 
        // Public DOM event handlers
1559
 
        //
1560
 
        /////////////////////////////////////////////////////////////////////////////
1561
 
    
1562
 
        /**
1563
 
         * Handles mouseup events on the Column resizer.
1564
 
         *
1565
 
         * @method onMouseUp
1566
 
         * @param e {string} The mouseup event
1567
 
         */
1568
 
        onMouseUp : function(e) {
1569
 
            // Reset height of all resizer els in case TH's have changed height
1570
 
            var allKeys = this.datatable.getColumnSet().keys,
1571
 
                col;
1572
 
            for(var i=0, len=allKeys.length; i<len; i++) {
1573
 
                col = allKeys[i];
1574
 
                if(col._ddResizer) {
1575
 
                    col._ddResizer.resetResizerEl();
1576
 
                }
1577
 
            }
1578
 
            this.resetResizerEl();
1579
 
            
1580
 
            var el = this.headCellLiner;
1581
 
            var newWidth = el.offsetWidth -
1582
 
                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) -
1583
 
                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0);
1584
 
 
1585
 
            this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth});
1586
 
        },
1587
 
    
1588
 
        /**
1589
 
         * Handles mousedown events on the Column resizer.
1590
 
         *
1591
 
         * @method onMouseDown
1592
 
         * @param e {string} The mousedown event
1593
 
         */
1594
 
        onMouseDown : function(e) {
1595
 
            this.startWidth = this.headCellLiner.offsetWidth;
1596
 
            this.startX = YAHOO.util.Event.getXY(e)[0];
1597
 
            this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) +
1598
 
                    (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
1599
 
        },
1600
 
    
1601
 
        /**
1602
 
         * Custom clickValidator to ensure Column is not in hidden state.
1603
 
         *
1604
 
         * @method clickValidator
1605
 
         * @param {Event} e
1606
 
         * @private
1607
 
         */
1608
 
        clickValidator : function(e) {
1609
 
            if(!this.column.hidden) {
1610
 
                var target = YAHOO.util.Event.getTarget(e);
1611
 
                return ( this.isValidHandleChild(target) &&
1612
 
                            (this.id == this.handleElId ||
1613
 
                                this.DDM.handleWasClicked(target, this.id)) );
1614
 
            }
1615
 
        },
1616
 
    
1617
 
        /**
1618
 
         * Handles start drag on the Column resizer.
1619
 
         *
1620
 
         * @method startDrag
1621
 
         * @param e {string} The drag event
1622
 
         */
1623
 
        startDrag : function() {
1624
 
            // Shrinks height of all resizer els to not hold open TH els
1625
 
            var allKeys = this.datatable.getColumnSet().keys,
1626
 
                thisKey = this.column.getKeyIndex(),
1627
 
                col;
1628
 
            for(var i=0, len=allKeys.length; i<len; i++) {
1629
 
                col = allKeys[i];
1630
 
                if(col._ddResizer) {
1631
 
                    YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em";
1632
 
                }
1633
 
            }
1634
 
        },
1635
 
 
1636
 
        /**
1637
 
         * Handles drag events on the Column resizer.
1638
 
         *
1639
 
         * @method onDrag
1640
 
         * @param e {string} The drag event
1641
 
         */
1642
 
        onDrag : function(e) {
1643
 
            var newX = YAHOO.util.Event.getXY(e)[0];
1644
 
            if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) {
1645
 
                var offsetX = newX - this.startX;
1646
 
                var newWidth = this.startWidth + offsetX - this.nLinerPadding;
1647
 
                if(newWidth > 0) {
1648
 
                    this.datatable.setColumnWidth(this.column, newWidth);
1649
 
                }
1650
 
            }
1651
 
        }
1652
 
    });
1653
 
}
1654
 
 
1655
 
/////////////////////////////////////////////////////////////////////////////
1656
 
//
1657
 
// Deprecated
1658
 
//
1659
 
/////////////////////////////////////////////////////////////////////////////
1660
 
 
1661
 
/**
1662
 
 * @property editorOptions
1663
 
 * @deprecated Pass configs directly to CellEditor constructor. 
1664
 
 */
1665
 
 
1666
 
 
1667
 
(function () {
1668
 
 
1669
 
var lang   = YAHOO.lang,
1670
 
    util   = YAHOO.util,
1671
 
    widget = YAHOO.widget,
1672
 
    
1673
 
    Dom    = util.Dom,
1674
 
    Ev     = util.Event,
1675
 
    DT     = widget.DataTable;
1676
 
 
1677
 
/****************************************************************************/
1678
 
/****************************************************************************/
1679
 
/****************************************************************************/
1680
 
 
1681
 
/**
1682
 
 * A RecordSet defines and manages a set of Records.
1683
 
 *
1684
 
 * @namespace YAHOO.widget
1685
 
 * @class RecordSet
1686
 
 * @param data {Object || Object[]} An object literal or an array of data.
1687
 
 * @constructor
1688
 
 */
1689
 
YAHOO.widget.RecordSet = function(data) {
1690
 
    // Internal variables
1691
 
    this._sId = "yui-rs" + widget.RecordSet._nCount;
1692
 
    widget.RecordSet._nCount++;
1693
 
    this._records = [];
1694
 
    //this._length = 0;
1695
 
 
1696
 
    if(data) {
1697
 
        if(lang.isArray(data)) {
1698
 
            this.addRecords(data);
1699
 
        }
1700
 
        else if(lang.isObject(data)) {
1701
 
            this.addRecord(data);
1702
 
        }
1703
 
    }
1704
 
 
1705
 
    YAHOO.log("RecordSet initialized", "info", this.toString());
1706
 
};
1707
 
 
1708
 
var RS = widget.RecordSet;
1709
 
 
1710
 
/**
1711
 
 * Internal class variable to name multiple Recordset instances.
1712
 
 *
1713
 
 * @property RecordSet._nCount
1714
 
 * @type Number
1715
 
 * @private
1716
 
 * @static
1717
 
 */
1718
 
RS._nCount = 0;
1719
 
 
1720
 
RS.prototype = {
1721
 
 
1722
 
    /////////////////////////////////////////////////////////////////////////////
1723
 
    //
1724
 
    // Private member variables
1725
 
    //
1726
 
    /////////////////////////////////////////////////////////////////////////////
1727
 
    /**
1728
 
     * Unique String identifier assigned at instantiation.
1729
 
     *
1730
 
     * @property _sId
1731
 
     * @type String
1732
 
     * @private
1733
 
     */
1734
 
    _sId : null,
1735
 
 
1736
 
    /**
1737
 
     * Internal counter of how many Records are in the RecordSet.
1738
 
     *
1739
 
     * @property _length
1740
 
     * @type Number
1741
 
     * @private
1742
 
     * @deprecated No longer used
1743
 
     */
1744
 
    //_length : null,
1745
 
 
1746
 
    /////////////////////////////////////////////////////////////////////////////
1747
 
    //
1748
 
    // Private methods
1749
 
    //
1750
 
    /////////////////////////////////////////////////////////////////////////////
1751
 
 
1752
 
    /**
1753
 
     * Adds one Record to the RecordSet at the given index. If index is null,
1754
 
     * then adds the Record to the end of the RecordSet.
1755
 
     *
1756
 
     * @method _addRecord
1757
 
     * @param oData {Object} An object literal of data.
1758
 
     * @param index {Number} (optional) Position index.
1759
 
     * @return {YAHOO.widget.Record} A Record instance.
1760
 
     * @private
1761
 
     */
1762
 
    _addRecord : function(oData, index) {
1763
 
        var oRecord = new YAHOO.widget.Record(oData);
1764
 
        
1765
 
        if(YAHOO.lang.isNumber(index) && (index > -1)) {
1766
 
            this._records.splice(index,0,oRecord);
1767
 
        }
1768
 
        else {
1769
 
            //index = this.getLength();
1770
 
            //this._records[index] = oRecord;
1771
 
            this._records[this._records.length] = oRecord;
1772
 
        }
1773
 
        //this._length++;
1774
 
        return oRecord;
1775
 
    },
1776
 
 
1777
 
    /**
1778
 
     * Sets/replaces one Record to the RecordSet at the given index.  Existing
1779
 
     * Records with higher indexes are not shifted.  If no index specified, the
1780
 
     * Record is added to the end of the RecordSet.
1781
 
     *
1782
 
     * @method _setRecord
1783
 
     * @param oData {Object} An object literal of data.
1784
 
     * @param index {Number} (optional) Position index.
1785
 
     * @return {YAHOO.widget.Record} A Record instance.
1786
 
     * @private
1787
 
     */
1788
 
    _setRecord : function(oData, index) {
1789
 
        if (!lang.isNumber(index) || index < 0) {
1790
 
            index = this._records.length;
1791
 
        }
1792
 
        return (this._records[index] = new widget.Record(oData));
1793
 
        /*
1794
 
        if(lang.isNumber(index) && (index > -1)) {
1795
 
            this._records[index] = oRecord;
1796
 
            if((index+1) > this.getLength()) {
1797
 
                this._length = index+1;
1798
 
            }
1799
 
        }
1800
 
        else {
1801
 
            this._records[this.getLength()] = oRecord;
1802
 
            this._length++;
1803
 
        }
1804
 
        return oRecord;
1805
 
        */
1806
 
    },
1807
 
 
1808
 
    /**
1809
 
     * Deletes Records from the RecordSet at the given index. If range is null,
1810
 
     * then only one Record is deleted.
1811
 
     *
1812
 
     * @method _deleteRecord
1813
 
     * @param index {Number} Position index.
1814
 
     * @param range {Number} (optional) How many Records to delete
1815
 
     * @private
1816
 
     */
1817
 
    _deleteRecord : function(index, range) {
1818
 
        if(!lang.isNumber(range) || (range < 0)) {
1819
 
            range = 1;
1820
 
        }
1821
 
        this._records.splice(index, range);
1822
 
        //this._length = this._length - range;
1823
 
    },
1824
 
 
1825
 
    /////////////////////////////////////////////////////////////////////////////
1826
 
    //
1827
 
    // Public methods
1828
 
    //
1829
 
    /////////////////////////////////////////////////////////////////////////////
1830
 
 
1831
 
    /**
1832
 
     * Returns unique name of the RecordSet instance.
1833
 
     *
1834
 
     * @method getId
1835
 
     * @return {String} Unique name of the RecordSet instance.
1836
 
     */
1837
 
    getId : function() {
1838
 
        return this._sId;
1839
 
    },
1840
 
 
1841
 
    /**
1842
 
     * Public accessor to the unique name of the RecordSet instance.
1843
 
     *
1844
 
     * @method toString
1845
 
     * @return {String} Unique name of the RecordSet instance.
1846
 
     */
1847
 
    toString : function() {
1848
 
        return "RecordSet instance " + this._sId;
1849
 
    },
1850
 
 
1851
 
    /**
1852
 
     * Returns the number of Records held in the RecordSet.
1853
 
     *
1854
 
     * @method getLength
1855
 
     * @return {Number} Number of records in the RecordSet.
1856
 
     */
1857
 
    getLength : function() {
1858
 
            //return this._length;
1859
 
            return this._records.length;
1860
 
    },
1861
 
 
1862
 
    /**
1863
 
     * Returns Record by ID or RecordSet position index.
1864
 
     *
1865
 
     * @method getRecord
1866
 
     * @param record {YAHOO.widget.Record | Number | String} Record instance,
1867
 
     * RecordSet position index, or Record ID.
1868
 
     * @return {YAHOO.widget.Record} Record object.
1869
 
     */
1870
 
    getRecord : function(record) {
1871
 
        var i;
1872
 
        if(record instanceof widget.Record) {
1873
 
            for(i=0; i<this._records.length; i++) {
1874
 
                if(this._records[i] && (this._records[i]._sId === record._sId)) {
1875
 
                    return record;
1876
 
                }
1877
 
            }
1878
 
        }
1879
 
        else if(lang.isNumber(record)) {
1880
 
            if((record > -1) && (record < this.getLength())) {
1881
 
                return this._records[record];
1882
 
            }
1883
 
        }
1884
 
        else if(lang.isString(record)) {
1885
 
            for(i=0; i<this._records.length; i++) {
1886
 
                if(this._records[i] && (this._records[i]._sId === record)) {
1887
 
                    return this._records[i];
1888
 
                }
1889
 
            }
1890
 
        }
1891
 
        // Not a valid Record for this RecordSet
1892
 
        return null;
1893
 
 
1894
 
    },
1895
 
 
1896
 
    /**
1897
 
     * Returns an array of Records from the RecordSet.
1898
 
     *
1899
 
     * @method getRecords
1900
 
     * @param index {Number} (optional) Recordset position index of which Record to
1901
 
     * start at.
1902
 
     * @param range {Number} (optional) Number of Records to get.
1903
 
     * @return {YAHOO.widget.Record[]} Array of Records starting at given index and
1904
 
     * length equal to given range. If index is not given, all Records are returned.
1905
 
     */
1906
 
    getRecords : function(index, range) {
1907
 
        if(!lang.isNumber(index)) {
1908
 
            return this._records;
1909
 
        }
1910
 
        if(!lang.isNumber(range)) {
1911
 
            return this._records.slice(index);
1912
 
        }
1913
 
        return this._records.slice(index, index+range);
1914
 
    },
1915
 
 
1916
 
    /**
1917
 
     * Returns a boolean indicating whether Records exist in the RecordSet at the
1918
 
     * specified index range.  Returns true if and only if a Record exists at each
1919
 
     * index in the range.
1920
 
     * @method hasRecords
1921
 
     * @param index
1922
 
     * @param range
1923
 
     * @return {Boolean} true if all indices are populated in the RecordSet
1924
 
     */
1925
 
    hasRecords : function (index, range) {
1926
 
        var recs = this.getRecords(index,range);
1927
 
        for (var i = 0; i < range; ++i) {
1928
 
            if (typeof recs[i] === 'undefined') {
1929
 
                return false;
1930
 
            }
1931
 
        }
1932
 
        return true;
1933
 
    },
1934
 
 
1935
 
    /**
1936
 
     * Returns current position index for the given Record.
1937
 
     *
1938
 
     * @method getRecordIndex
1939
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
1940
 
     * @return {Number} Record's RecordSet position index.
1941
 
     */
1942
 
 
1943
 
    getRecordIndex : function(oRecord) {
1944
 
        if(oRecord) {
1945
 
            for(var i=this._records.length-1; i>-1; i--) {
1946
 
                if(this._records[i] && oRecord.getId() === this._records[i].getId()) {
1947
 
                    return i;
1948
 
                }
1949
 
            }
1950
 
        }
1951
 
        return null;
1952
 
 
1953
 
    },
1954
 
 
1955
 
    /**
1956
 
     * Adds one Record to the RecordSet at the given index. If index is null,
1957
 
     * then adds the Record to the end of the RecordSet.
1958
 
     *
1959
 
     * @method addRecord
1960
 
     * @param oData {Object} An object literal of data.
1961
 
     * @param index {Number} (optional) Position index.
1962
 
     * @return {YAHOO.widget.Record} A Record instance.
1963
 
     */
1964
 
    addRecord : function(oData, index) {
1965
 
        if(lang.isObject(oData)) {
1966
 
            var oRecord = this._addRecord(oData, index);
1967
 
            this.fireEvent("recordAddEvent",{record:oRecord,data:oData});
1968
 
            YAHOO.log("Added Record at index " + index +
1969
 
                    " with data " + lang.dump(oData), "info", this.toString());
1970
 
            return oRecord;
1971
 
        }
1972
 
        else {
1973
 
            YAHOO.log("Could not add Record with data" +
1974
 
                    lang.dump(oData), "info", this.toString());
1975
 
            return null;
1976
 
        }
1977
 
    },
1978
 
 
1979
 
    /**
1980
 
     * Adds multiple Records at once to the RecordSet at the given index with the
1981
 
     * given object literal data. If index is null, then the new Records are
1982
 
     * added to the end of the RecordSet.
1983
 
     *
1984
 
     * @method addRecords
1985
 
     * @param aData {Object[]} An object literal data or an array of data object literals.
1986
 
     * @param index {Number} (optional) Position index.
1987
 
     * @return {YAHOO.widget.Record[]} An array of Record instances.
1988
 
     */
1989
 
    addRecords : function(aData, index) {
1990
 
        if(lang.isArray(aData)) {
1991
 
            var newRecords = [],
1992
 
                idx,i,len;
1993
 
 
1994
 
            index = lang.isNumber(index) ? index : this._records.length;
1995
 
            idx = index;
1996
 
 
1997
 
            // Can't go backwards bc we need to preserve order
1998
 
            for(i=0,len=aData.length; i<len; ++i) {
1999
 
                if(lang.isObject(aData[i])) {
2000
 
                    var record = this._addRecord(aData[i], idx++);
2001
 
                    newRecords.push(record);
2002
 
                }
2003
 
           }
2004
 
            this.fireEvent("recordsAddEvent",{records:newRecords,data:aData});
2005
 
            YAHOO.log("Added " + newRecords.length + " Record(s) at index " + index +
2006
 
                    " with data " + lang.dump(aData), "info", this.toString());
2007
 
           return newRecords;
2008
 
        }
2009
 
        else if(lang.isObject(aData)) {
2010
 
            var oRecord = this._addRecord(aData);
2011
 
            this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
2012
 
            YAHOO.log("Added 1 Record at index " + index +
2013
 
                    " with data " + lang.dump(aData), "info", this.toString());
2014
 
            return oRecord;
2015
 
        }
2016
 
        else {
2017
 
            YAHOO.log("Could not add Records with data " +
2018
 
                    lang.dump(aData), "info", this.toString());
2019
 
            return null;
2020
 
        }
2021
 
    },
2022
 
 
2023
 
    /**
2024
 
     * Sets or replaces one Record to the RecordSet at the given index. Unlike
2025
 
     * addRecord, an existing Record at that index is not shifted to preserve it.
2026
 
     * If no index is specified, it adds the Record to the end of the RecordSet.
2027
 
     *
2028
 
     * @method setRecord
2029
 
     * @param oData {Object} An object literal of data.
2030
 
     * @param index {Number} (optional) Position index.
2031
 
     * @return {YAHOO.widget.Record} A Record instance.
2032
 
     */
2033
 
    setRecord : function(oData, index) {
2034
 
        if(lang.isObject(oData)) {
2035
 
            var oRecord = this._setRecord(oData, index);
2036
 
            this.fireEvent("recordSetEvent",{record:oRecord,data:oData});
2037
 
            YAHOO.log("Set Record at index " + index +
2038
 
                    " with data " + lang.dump(oData), "info", this.toString());
2039
 
            return oRecord;
2040
 
        }
2041
 
        else {
2042
 
            YAHOO.log("Could not set Record with data" +
2043
 
                    lang.dump(oData), "info", this.toString());
2044
 
            return null;
2045
 
        }
2046
 
    },
2047
 
 
2048
 
    /**
2049
 
     * Sets or replaces multiple Records at once to the RecordSet with the given
2050
 
     * data, starting at the given index. If index is not specified, then the new
2051
 
     * Records are added to the end of the RecordSet.
2052
 
     *
2053
 
     * @method setRecords
2054
 
     * @param aData {Object[]} An array of object literal data.
2055
 
     * @param index {Number} (optional) Position index.
2056
 
     * @return {YAHOO.widget.Record[]} An array of Record instances.
2057
 
     */
2058
 
    setRecords : function(aData, index) {
2059
 
        var Rec   = widget.Record,
2060
 
            a     = lang.isArray(aData) ? aData : [aData],
2061
 
            added = [],
2062
 
            i = 0, l = a.length, j = 0;
2063
 
 
2064
 
        index = parseInt(index,10)|0;
2065
 
 
2066
 
        for(; i < l; ++i) {
2067
 
            if (typeof a[i] === 'object' && a[i]) {
2068
 
                added[j++] = this._records[index + i] = new Rec(a[i]);
2069
 
            }
2070
 
        }
2071
 
 
2072
 
        this.fireEvent("recordsSetEvent",{records:added,data:aData});
2073
 
        // Backward compatibility for bug 1918245
2074
 
        this.fireEvent("recordsSet",{records:added,data:aData});
2075
 
        YAHOO.log("Set "+j+" Record(s) at index "+index, "info",
2076
 
                  this.toString());
2077
 
 
2078
 
        if (a.length && !added.length) {
2079
 
            YAHOO.log("Could not set Records with data " +
2080
 
                    lang.dump(aData), "info", this.toString());
2081
 
        }
2082
 
 
2083
 
        return added.length > 1 ? added : added[0];
2084
 
    },
2085
 
 
2086
 
    /**
2087
 
     * Updates given Record with given data.
2088
 
     *
2089
 
     * @method updateRecord
2090
 
     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
2091
 
     * a RecordSet position index, or a Record ID.
2092
 
     * @param oData {Object} Object literal of new data.
2093
 
     * @return {YAHOO.widget.Record} Updated Record, or null.
2094
 
     */
2095
 
    updateRecord : function(record, oData) {
2096
 
        var oRecord = this.getRecord(record);
2097
 
        if(oRecord && lang.isObject(oData)) {
2098
 
            // Copy data from the Record for the event that gets fired later
2099
 
            var oldData = {};
2100
 
            for(var key in oRecord._oData) {
2101
 
                if(lang.hasOwnProperty(oRecord._oData, key)) {
2102
 
                    oldData[key] = oRecord._oData[key];
2103
 
                }
2104
 
            }
2105
 
            oRecord._oData = oData;
2106
 
            this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData});
2107
 
            YAHOO.log("Record at index " + this.getRecordIndex(oRecord) +
2108
 
                    " updated with data " + lang.dump(oData), "info", this.toString());
2109
 
            return oRecord;
2110
 
        }
2111
 
        else {
2112
 
            YAHOO.log("Could not update Record " + record, "error", this.toString());
2113
 
            return null;
2114
 
        }
2115
 
    },
2116
 
 
2117
 
    /**
2118
 
     * @method updateKey
2119
 
     * @deprecated Use updateRecordValue
2120
 
     */
2121
 
    updateKey : function(record, sKey, oData) {
2122
 
        this.updateRecordValue(record, sKey, oData);
2123
 
    },
2124
 
    /**
2125
 
     * Sets given Record at given key to given data.
2126
 
     *
2127
 
     * @method updateRecordValue
2128
 
     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
2129
 
     * a RecordSet position index, or a Record ID.
2130
 
     * @param sKey {String} Key name.
2131
 
     * @param oData {Object} New data.
2132
 
     */
2133
 
    updateRecordValue : function(record, sKey, oData) {
2134
 
        var oRecord = this.getRecord(record);
2135
 
        if(oRecord) {
2136
 
            var oldData = null;
2137
 
            var keyValue = oRecord._oData[sKey];
2138
 
            // Copy data from the Record for the event that gets fired later
2139
 
            if(keyValue && lang.isObject(keyValue)) {
2140
 
                oldData = {};
2141
 
                for(var key in keyValue)  {
2142
 
                    if(lang.hasOwnProperty(keyValue, key)) {
2143
 
                        oldData[key] = keyValue[key];
2144
 
                    }
2145
 
                }
2146
 
            }
2147
 
            // Copy by value
2148
 
            else {
2149
 
                oldData = keyValue;
2150
 
            }
2151
 
 
2152
 
            oRecord._oData[sKey] = oData;
2153
 
            this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
2154
 
            this.fireEvent("recordValueUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
2155
 
            YAHOO.log("Key \"" + sKey +
2156
 
                    "\" for Record at index " + this.getRecordIndex(oRecord) +
2157
 
                    " updated to \"" + lang.dump(oData) + "\"", "info", this.toString());
2158
 
        }
2159
 
        else {
2160
 
            YAHOO.log("Could not update key " + sKey + " for Record " + record, "error", this.toString());
2161
 
        }
2162
 
    },
2163
 
 
2164
 
    /**
2165
 
     * Replaces all Records in RecordSet with new object literal data.
2166
 
     *
2167
 
     * @method replaceRecords
2168
 
     * @param data {Object || Object[]} An object literal of data or an array of
2169
 
     * data object literals.
2170
 
     * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or
2171
 
     * an array of Records.
2172
 
     */
2173
 
    replaceRecords : function(data) {
2174
 
        this.reset();
2175
 
        return this.addRecords(data);
2176
 
    },
2177
 
 
2178
 
    /**
2179
 
     * Sorts all Records by given function. Records keep their unique IDs but will
2180
 
     * have new RecordSet position indexes.
2181
 
     *
2182
 
     * @method sortRecords
2183
 
     * @param fnSort {Function} Reference to a sort function.
2184
 
     * @param desc {Boolean} True if sort direction is descending, false if sort
2185
 
     * direction is ascending.
2186
 
     * @return {YAHOO.widget.Record[]} Sorted array of Records.
2187
 
     */
2188
 
    sortRecords : function(fnSort, desc) {
2189
 
        return this._records.sort(function(a, b) {return fnSort(a, b, desc);});
2190
 
    },
2191
 
 
2192
 
    /**
2193
 
     * Reverses all Records, so ["one", "two", "three"] becomes ["three", "two", "one"].
2194
 
     *
2195
 
     * @method reverseRecords
2196
 
     * @return {YAHOO.widget.Record[]} Reverse-sorted array of Records.
2197
 
     */
2198
 
    reverseRecords : function() {
2199
 
        return this._records.reverse();
2200
 
    },
2201
 
 
2202
 
    /**
2203
 
     * Removes the Record at the given position index from the RecordSet. If a range
2204
 
     * is also provided, removes that many Records, starting from the index. Length
2205
 
     * of RecordSet is correspondingly shortened.
2206
 
     *
2207
 
     * @method deleteRecord
2208
 
     * @param index {Number} Record's RecordSet position index.
2209
 
     * @param range {Number} (optional) How many Records to delete.
2210
 
     * @return {Object} A copy of the data held by the deleted Record.
2211
 
     */
2212
 
    deleteRecord : function(index) {
2213
 
        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
2214
 
            // Copy data from the Record for the event that gets fired later
2215
 
            var oData = widget.DataTable._cloneObject(this.getRecord(index).getData());
2216
 
            
2217
 
            this._deleteRecord(index);
2218
 
            this.fireEvent("recordDeleteEvent",{data:oData,index:index});
2219
 
            YAHOO.log("Record deleted at index " + index +
2220
 
                    " and containing data " + lang.dump(oData), "info", this.toString());
2221
 
            return oData;
2222
 
        }
2223
 
        else {
2224
 
            YAHOO.log("Could not delete Record at index " + index, "error", this.toString());
2225
 
            return null;
2226
 
        }
2227
 
    },
2228
 
 
2229
 
    /**
2230
 
     * Removes the Record at the given position index from the RecordSet. If a range
2231
 
     * is also provided, removes that many Records, starting from the index. Length
2232
 
     * of RecordSet is correspondingly shortened.
2233
 
     *
2234
 
     * @method deleteRecords
2235
 
     * @param index {Number} Record's RecordSet position index.
2236
 
     * @param range {Number} (optional) How many Records to delete.
2237
 
     * @return {Object[]} An array of copies of the data held by the deleted Records.     
2238
 
     */
2239
 
    deleteRecords : function(index, range) {
2240
 
        if(!lang.isNumber(range)) {
2241
 
            range = 1;
2242
 
        }
2243
 
        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
2244
 
            var recordsToDelete = this.getRecords(index, range);
2245
 
            // Copy data from each Record for the event that gets fired later
2246
 
            var deletedData = [];
2247
 
            
2248
 
            for(var i=0; i<recordsToDelete.length; i++) {
2249
 
                deletedData[deletedData.length] = widget.DataTable._cloneObject(recordsToDelete[i]);
2250
 
            }
2251
 
            this._deleteRecord(index, range);
2252
 
 
2253
 
            this.fireEvent("recordsDeleteEvent",{data:deletedData,index:index});
2254
 
            YAHOO.log(range + "Record(s) deleted at index " + index +
2255
 
                    " and containing data " + lang.dump(deletedData), "info", this.toString());
2256
 
 
2257
 
            return deletedData;
2258
 
        }
2259
 
        else {
2260
 
            YAHOO.log("Could not delete Records at index " + index, "error", this.toString());
2261
 
            return null;
2262
 
        }
2263
 
    },
2264
 
 
2265
 
    /**
2266
 
     * Deletes all Records from the RecordSet.
2267
 
     *
2268
 
     * @method reset
2269
 
     */
2270
 
    reset : function() {
2271
 
        this._records = [];
2272
 
        //this._length = 0;
2273
 
        this.fireEvent("resetEvent");
2274
 
        YAHOO.log("All Records deleted from RecordSet", "info", this.toString());
2275
 
    }
2276
 
};
2277
 
 
2278
 
/////////////////////////////////////////////////////////////////////////////
2279
 
//
2280
 
// Custom Events
2281
 
//
2282
 
/////////////////////////////////////////////////////////////////////////////
2283
 
 
2284
 
// RecordSet uses EventProvider
2285
 
lang.augmentProto(RS, util.EventProvider);
2286
 
 
2287
 
/**
2288
 
 * Fired when a new Record is added to the RecordSet.
2289
 
 *
2290
 
 * @event recordAddEvent
2291
 
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
2292
 
 * @param oArgs.data {Object} Data added.
2293
 
 */
2294
 
 
2295
 
/**
2296
 
 * Fired when multiple Records are added to the RecordSet at once.
2297
 
 *
2298
 
 * @event recordsAddEvent
2299
 
 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
2300
 
 * @param oArgs.data {Object[]} Data added.
2301
 
 */
2302
 
 
2303
 
/**
2304
 
 * Fired when a Record is set in the RecordSet.
2305
 
 *
2306
 
 * @event recordSetEvent
2307
 
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
2308
 
 * @param oArgs.data {Object} Data added.
2309
 
 */
2310
 
 
2311
 
/**
2312
 
 * Fired when multiple Records are set in the RecordSet at once.
2313
 
 *
2314
 
 * @event recordsSetEvent
2315
 
 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
2316
 
 * @param oArgs.data {Object[]} Data added.
2317
 
 */
2318
 
 
2319
 
/**
2320
 
 * Fired when a Record is updated with new data.
2321
 
 *
2322
 
 * @event recordUpdateEvent
2323
 
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
2324
 
 * @param oArgs.newData {Object} New data.
2325
 
 * @param oArgs.oldData {Object} Old data.
2326
 
 */
2327
 
 
2328
 
/**
2329
 
 * Fired when a Record is deleted from the RecordSet.
2330
 
 *
2331
 
 * @event recordDeleteEvent
2332
 
 * @param oArgs.data {Object} A copy of the data held by the Record,
2333
 
 * or an array of data object literals if multiple Records were deleted at once.
2334
 
 * @param oArgs.index {Object} Index of the deleted Record.
2335
 
 */
2336
 
 
2337
 
/**
2338
 
 * Fired when multiple Records are deleted from the RecordSet at once.
2339
 
 *
2340
 
 * @event recordsDeleteEvent
2341
 
 * @param oArgs.data {Object[]} An array of data object literals copied
2342
 
 * from the Records.
2343
 
 * @param oArgs.index {Object} Index of the first deleted Record.
2344
 
 */
2345
 
 
2346
 
/**
2347
 
 * Fired when all Records are deleted from the RecordSet at once.
2348
 
 *
2349
 
 * @event resetEvent
2350
 
 */
2351
 
 
2352
 
/**
2353
 
 * @event keyUpdateEvent    
2354
 
 * @deprecated Use recordValueUpdateEvent     
2355
 
 */
2356
 
 
2357
 
/**
2358
 
 * Fired when a Record value is updated with new data.
2359
 
 *
2360
 
 * @event recordValueUpdateEvent
2361
 
 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
2362
 
 * @param oArgs.key {String} The updated key.
2363
 
 * @param oArgs.newData {Object} New data.
2364
 
 * @param oArgs.oldData {Object} Old data.
2365
 
 *
2366
 
 */
2367
 
 
2368
 
 
2369
 
/****************************************************************************/
2370
 
/****************************************************************************/
2371
 
/****************************************************************************/
2372
 
 
2373
 
/**
2374
 
 * The Record class defines a DataTable record.
2375
 
 *
2376
 
 * @namespace YAHOO.widget
2377
 
 * @class Record
2378
 
 * @constructor
2379
 
 * @param oConfigs {Object} (optional) Object literal of key/value pairs.
2380
 
 */
2381
 
YAHOO.widget.Record = function(oLiteral) {
2382
 
    this._nCount = widget.Record._nCount;
2383
 
    this._sId = "yui-rec" + this._nCount;
2384
 
    widget.Record._nCount++;
2385
 
    this._oData = {};
2386
 
    if(lang.isObject(oLiteral)) {
2387
 
        for(var sKey in oLiteral) {
2388
 
            if(lang.hasOwnProperty(oLiteral, sKey)) {
2389
 
                this._oData[sKey] = oLiteral[sKey];
2390
 
            }
2391
 
        }
2392
 
    }
2393
 
};
2394
 
 
2395
 
/////////////////////////////////////////////////////////////////////////////
2396
 
//
2397
 
// Private member variables
2398
 
//
2399
 
/////////////////////////////////////////////////////////////////////////////
2400
 
 
2401
 
/**
2402
 
 * Internal class variable to give unique IDs to Record instances.
2403
 
 *
2404
 
 * @property Record._nCount
2405
 
 * @type Number
2406
 
 * @private
2407
 
 */
2408
 
YAHOO.widget.Record._nCount = 0;
2409
 
 
2410
 
YAHOO.widget.Record.prototype = {
2411
 
    /**
2412
 
     * Immutable unique count assigned at instantiation. Remains constant while a
2413
 
     * Record's position index can change from sorting.
2414
 
     *
2415
 
     * @property _nCount
2416
 
     * @type Number
2417
 
     * @private
2418
 
     */
2419
 
    _nCount : null,
2420
 
 
2421
 
    /**
2422
 
     * Immutable unique ID assigned at instantiation. Remains constant while a
2423
 
     * Record's position index can change from sorting.
2424
 
     *
2425
 
     * @property _sId
2426
 
     * @type String
2427
 
     * @private
2428
 
     */
2429
 
    _sId : null,
2430
 
 
2431
 
    /**
2432
 
     * Holds data for the Record in an object literal.
2433
 
     *
2434
 
     * @property _oData
2435
 
     * @type Object
2436
 
     * @private
2437
 
     */
2438
 
    _oData : null,
2439
 
 
2440
 
    /////////////////////////////////////////////////////////////////////////////
2441
 
    //
2442
 
    // Public member variables
2443
 
    //
2444
 
    /////////////////////////////////////////////////////////////////////////////
2445
 
 
2446
 
    /////////////////////////////////////////////////////////////////////////////
2447
 
    //
2448
 
    // Public methods
2449
 
    //
2450
 
    /////////////////////////////////////////////////////////////////////////////
2451
 
 
2452
 
    /**
2453
 
     * Returns unique count assigned at instantiation.
2454
 
     *
2455
 
     * @method getCount
2456
 
     * @return Number
2457
 
     */
2458
 
    getCount : function() {
2459
 
        return this._nCount;
2460
 
    },
2461
 
 
2462
 
    /**
2463
 
     * Returns unique ID assigned at instantiation.
2464
 
     *
2465
 
     * @method getId
2466
 
     * @return String
2467
 
     */
2468
 
    getId : function() {
2469
 
        return this._sId;
2470
 
    },
2471
 
 
2472
 
    /**
2473
 
     * Returns data for the Record for a field if given, or the entire object
2474
 
     * literal otherwise.
2475
 
     *
2476
 
     * @method getData
2477
 
     * @param sField {String} (Optional) The field from which to retrieve data value.
2478
 
     * @return Object
2479
 
     */
2480
 
    getData : function(sField) {
2481
 
        if(lang.isString(sField)) {
2482
 
            return this._oData[sField];
2483
 
        }
2484
 
        else {
2485
 
            return this._oData;
2486
 
        }
2487
 
    },
2488
 
 
2489
 
    /**
2490
 
     * Sets given data at the given key. Use the RecordSet method setValue to trigger
2491
 
     * events. 
2492
 
     *
2493
 
     * @method setData
2494
 
     * @param sKey {String} The key of the new value.
2495
 
     * @param oData {MIXED} The new value.
2496
 
     */
2497
 
    setData : function(sKey, oData) {
2498
 
        this._oData[sKey] = oData;
2499
 
    }
2500
 
};
2501
 
 
2502
 
})();
2503
 
 
2504
 
(function () {
2505
 
 
2506
 
var lang   = YAHOO.lang,
2507
 
    util   = YAHOO.util,
2508
 
    widget = YAHOO.widget,
2509
 
    ua     = YAHOO.env.ua,
2510
 
    
2511
 
    Dom    = util.Dom,
2512
 
    Ev     = util.Event,
2513
 
    DS     = util.DataSourceBase;
2514
 
 
2515
 
/**
2516
 
 * The DataTable widget provides a progressively enhanced DHTML control for
2517
 
 * displaying tabular data across A-grade browsers.
2518
 
 *
2519
 
 * @module datatable
2520
 
 * @requires yahoo, dom, event, element, datasource
2521
 
 * @optional dragdrop, dragdrop
2522
 
 * @title DataTable Widget
2523
 
 */
2524
 
 
2525
 
/****************************************************************************/
2526
 
/****************************************************************************/
2527
 
/****************************************************************************/
2528
 
 
2529
 
/**
2530
 
 * DataTable class for the YUI DataTable widget.
2531
 
 *
2532
 
 * @namespace YAHOO.widget
2533
 
 * @class DataTable
2534
 
 * @extends YAHOO.util.Element
2535
 
 * @constructor
2536
 
 * @param elContainer {HTMLElement} Container element for the TABLE.
2537
 
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
2538
 
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
2539
 
 * @param oConfigs {object} (optional) Object literal of configuration values.
2540
 
 */
2541
 
YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
2542
 
    var DT = widget.DataTable;
2543
 
    
2544
 
    ////////////////////////////////////////////////////////////////////////////
2545
 
    // Backward compatibility for SDT, but prevent infinite loops
2546
 
    
2547
 
    if(oConfigs && oConfigs.scrollable) {
2548
 
        return new YAHOO.widget.ScrollingDataTable(elContainer,aColumnDefs,oDataSource,oConfigs);
2549
 
    }
2550
 
    
2551
 
    ////////////////////////////////////////////////////////////////////////////
2552
 
    // Initialization
2553
 
 
2554
 
    // Internal vars
2555
 
    this._nIndex = DT._nCount;
2556
 
    this._sId = "yui-dt"+this._nIndex;
2557
 
    this._oChainRender = new YAHOO.util.Chain();
2558
 
    this._oChainRender.subscribe("end",this._onRenderChainEnd, this, true);
2559
 
 
2560
 
    // Initialize configs
2561
 
    this._initConfigs(oConfigs);
2562
 
 
2563
 
    // Initialize DataSource
2564
 
    this._initDataSource(oDataSource);
2565
 
    if(!this._oDataSource) {
2566
 
        YAHOO.log("Could not instantiate DataTable due to an invalid DataSource", "error", this.toString());
2567
 
        return;
2568
 
    }
2569
 
 
2570
 
    // Initialize ColumnSet
2571
 
    this._initColumnSet(aColumnDefs);
2572
 
    if(!this._oColumnSet) {
2573
 
        YAHOO.log("Could not instantiate DataTable due to an invalid ColumnSet", "error", this.toString());
2574
 
        return;
2575
 
    }
2576
 
 
2577
 
    // Initialize RecordSet
2578
 
    this._initRecordSet();
2579
 
    if(!this._oRecordSet) {
2580
 
    }
2581
 
 
2582
 
    // Initialize Attributes
2583
 
    DT.superclass.constructor.call(this, elContainer, this.configs);
2584
 
 
2585
 
    // Initialize DOM elements
2586
 
    var okDom = this._initDomElements(elContainer);
2587
 
    if(!okDom) {
2588
 
        YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
2589
 
        return;
2590
 
    }
2591
 
            
2592
 
    // Show message as soon as config is available
2593
 
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
2594
 
    
2595
 
    ////////////////////////////////////////////////////////////////////////////
2596
 
    // Once per instance
2597
 
    this._initEvents();
2598
 
 
2599
 
    DT._nCount++;
2600
 
    DT._nCurrentCount++;
2601
 
    
2602
 
    ////////////////////////////////////////////////////////////////////////////
2603
 
    // Data integration
2604
 
 
2605
 
    // Send a simple initial request
2606
 
    var oCallback = {
2607
 
        success : this.onDataReturnSetRows,
2608
 
        failure : this.onDataReturnSetRows,
2609
 
        scope   : this,
2610
 
        argument: this.getState()
2611
 
    };
2612
 
    
2613
 
    var initialLoad = this.get("initialLoad");
2614
 
    if(initialLoad === true) {
2615
 
        this._oDataSource.sendRequest(this.get("initialRequest"), oCallback);
2616
 
    }
2617
 
    // Do not send an initial request at all
2618
 
    else if(initialLoad === false) {
2619
 
        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
2620
 
    }
2621
 
    // Send an initial request with a custom payload
2622
 
    else {
2623
 
        var oCustom = initialLoad || {};
2624
 
        oCallback.argument = oCustom.argument || {};
2625
 
        this._oDataSource.sendRequest(oCustom.request, oCallback);
2626
 
    }
2627
 
};
2628
 
 
2629
 
var DT = widget.DataTable;
2630
 
 
2631
 
/////////////////////////////////////////////////////////////////////////////
2632
 
//
2633
 
// Public constants
2634
 
//
2635
 
/////////////////////////////////////////////////////////////////////////////
2636
 
 
2637
 
lang.augmentObject(DT, {
2638
 
 
2639
 
    /**
2640
 
     * Class name assigned to outer DataTable container.
2641
 
     *
2642
 
     * @property DataTable.CLASS_DATATABLE
2643
 
     * @type String
2644
 
     * @static
2645
 
     * @final
2646
 
     * @default "yui-dt"
2647
 
     */
2648
 
    CLASS_DATATABLE : "yui-dt",
2649
 
 
2650
 
    /**
2651
 
     * Class name assigned to liner DIV elements.
2652
 
     *
2653
 
     * @property DataTable.CLASS_LINER
2654
 
     * @type String
2655
 
     * @static
2656
 
     * @final
2657
 
     * @default "yui-dt-liner"
2658
 
     */
2659
 
    CLASS_LINER : "yui-dt-liner",
2660
 
 
2661
 
    /**
2662
 
     * Class name assigned to display label elements.
2663
 
     *
2664
 
     * @property DataTable.CLASS_LABEL
2665
 
     * @type String
2666
 
     * @static
2667
 
     * @final
2668
 
     * @default "yui-dt-label"
2669
 
     */
2670
 
    CLASS_LABEL : "yui-dt-label",
2671
 
 
2672
 
    /**
2673
 
     * Class name assigned to messaging elements.
2674
 
     *
2675
 
     * @property DataTable.CLASS_MESSAGE
2676
 
     * @type String
2677
 
     * @static
2678
 
     * @final
2679
 
     * @default "yui-dt-message"
2680
 
     */
2681
 
    CLASS_MESSAGE : "yui-dt-message",
2682
 
 
2683
 
    /**
2684
 
     * Class name assigned to mask element when DataTable is disabled.
2685
 
     *
2686
 
     * @property DataTable.CLASS_MASK
2687
 
     * @type String
2688
 
     * @static
2689
 
     * @final
2690
 
     * @default "yui-dt-mask"
2691
 
     */
2692
 
    CLASS_MASK : "yui-dt-mask",
2693
 
 
2694
 
    /**
2695
 
     * Class name assigned to data elements.
2696
 
     *
2697
 
     * @property DataTable.CLASS_DATA
2698
 
     * @type String
2699
 
     * @static
2700
 
     * @final
2701
 
     * @default "yui-dt-data"
2702
 
     */
2703
 
    CLASS_DATA : "yui-dt-data",
2704
 
 
2705
 
    /**
2706
 
     * Class name assigned to Column drag target.
2707
 
     *
2708
 
     * @property DataTable.CLASS_COLTARGET
2709
 
     * @type String
2710
 
     * @static
2711
 
     * @final
2712
 
     * @default "yui-dt-coltarget"
2713
 
     */
2714
 
    CLASS_COLTARGET : "yui-dt-coltarget",
2715
 
 
2716
 
    /**
2717
 
     * Class name assigned to resizer handle elements.
2718
 
     *
2719
 
     * @property DataTable.CLASS_RESIZER
2720
 
     * @type String
2721
 
     * @static
2722
 
     * @final
2723
 
     * @default "yui-dt-resizer"
2724
 
     */
2725
 
    CLASS_RESIZER : "yui-dt-resizer",
2726
 
 
2727
 
    /**
2728
 
     * Class name assigned to resizer liner elements.
2729
 
     *
2730
 
     * @property DataTable.CLASS_RESIZERLINER
2731
 
     * @type String
2732
 
     * @static
2733
 
     * @final
2734
 
     * @default "yui-dt-resizerliner"
2735
 
     */
2736
 
    CLASS_RESIZERLINER : "yui-dt-resizerliner",
2737
 
 
2738
 
    /**
2739
 
     * Class name assigned to resizer proxy elements.
2740
 
     *
2741
 
     * @property DataTable.CLASS_RESIZERPROXY
2742
 
     * @type String
2743
 
     * @static
2744
 
     * @final
2745
 
     * @default "yui-dt-resizerproxy"
2746
 
     */
2747
 
    CLASS_RESIZERPROXY : "yui-dt-resizerproxy",
2748
 
 
2749
 
    /**
2750
 
     * Class name assigned to CellEditor container elements.
2751
 
     *
2752
 
     * @property DataTable.CLASS_EDITOR
2753
 
     * @type String
2754
 
     * @static
2755
 
     * @final
2756
 
     * @default "yui-dt-editor"
2757
 
     */
2758
 
    CLASS_EDITOR : "yui-dt-editor",
2759
 
 
2760
 
    /**
2761
 
     * Class name assigned to paginator container elements.
2762
 
     *
2763
 
     * @property DataTable.CLASS_PAGINATOR
2764
 
     * @type String
2765
 
     * @static
2766
 
     * @final
2767
 
     * @default "yui-dt-paginator"
2768
 
     */
2769
 
    CLASS_PAGINATOR : "yui-dt-paginator",
2770
 
 
2771
 
    /**
2772
 
     * Class name assigned to page number indicators.
2773
 
     *
2774
 
     * @property DataTable.CLASS_PAGE
2775
 
     * @type String
2776
 
     * @static
2777
 
     * @final
2778
 
     * @default "yui-dt-page"
2779
 
     */
2780
 
    CLASS_PAGE : "yui-dt-page",
2781
 
 
2782
 
    /**
2783
 
     * Class name assigned to default indicators.
2784
 
     *
2785
 
     * @property DataTable.CLASS_DEFAULT
2786
 
     * @type String
2787
 
     * @static
2788
 
     * @final
2789
 
     * @default "yui-dt-default"
2790
 
     */
2791
 
    CLASS_DEFAULT : "yui-dt-default",
2792
 
 
2793
 
    /**
2794
 
     * Class name assigned to previous indicators.
2795
 
     *
2796
 
     * @property DataTable.CLASS_PREVIOUS
2797
 
     * @type String
2798
 
     * @static
2799
 
     * @final
2800
 
     * @default "yui-dt-previous"
2801
 
     */
2802
 
    CLASS_PREVIOUS : "yui-dt-previous",
2803
 
 
2804
 
    /**
2805
 
     * Class name assigned next indicators.
2806
 
     *
2807
 
     * @property DataTable.CLASS_NEXT
2808
 
     * @type String
2809
 
     * @static
2810
 
     * @final
2811
 
     * @default "yui-dt-next"
2812
 
     */
2813
 
    CLASS_NEXT : "yui-dt-next",
2814
 
 
2815
 
    /**
2816
 
     * Class name assigned to first elements.
2817
 
     *
2818
 
     * @property DataTable.CLASS_FIRST
2819
 
     * @type String
2820
 
     * @static
2821
 
     * @final
2822
 
     * @default "yui-dt-first"
2823
 
     */
2824
 
    CLASS_FIRST : "yui-dt-first",
2825
 
 
2826
 
    /**
2827
 
     * Class name assigned to last elements.
2828
 
     *
2829
 
     * @property DataTable.CLASS_LAST
2830
 
     * @type String
2831
 
     * @static
2832
 
     * @final
2833
 
     * @default "yui-dt-last"
2834
 
     */
2835
 
    CLASS_LAST : "yui-dt-last",
2836
 
 
2837
 
    /**
2838
 
     * Class name assigned to even elements.
2839
 
     *
2840
 
     * @property DataTable.CLASS_EVEN
2841
 
     * @type String
2842
 
     * @static
2843
 
     * @final
2844
 
     * @default "yui-dt-even"
2845
 
     */
2846
 
    CLASS_EVEN : "yui-dt-even",
2847
 
 
2848
 
    /**
2849
 
     * Class name assigned to odd elements.
2850
 
     *
2851
 
     * @property DataTable.CLASS_ODD
2852
 
     * @type String
2853
 
     * @static
2854
 
     * @final
2855
 
     * @default "yui-dt-odd"
2856
 
     */
2857
 
    CLASS_ODD : "yui-dt-odd",
2858
 
 
2859
 
    /**
2860
 
     * Class name assigned to selected elements.
2861
 
     *
2862
 
     * @property DataTable.CLASS_SELECTED
2863
 
     * @type String
2864
 
     * @static
2865
 
     * @final
2866
 
     * @default "yui-dt-selected"
2867
 
     */
2868
 
    CLASS_SELECTED : "yui-dt-selected",
2869
 
 
2870
 
    /**
2871
 
     * Class name assigned to highlighted elements.
2872
 
     *
2873
 
     * @property DataTable.CLASS_HIGHLIGHTED
2874
 
     * @type String
2875
 
     * @static
2876
 
     * @final
2877
 
     * @default "yui-dt-highlighted"
2878
 
     */
2879
 
    CLASS_HIGHLIGHTED : "yui-dt-highlighted",
2880
 
 
2881
 
    /**
2882
 
     * Class name assigned to hidden elements.
2883
 
     *
2884
 
     * @property DataTable.CLASS_HIDDEN
2885
 
     * @type String
2886
 
     * @static
2887
 
     * @final
2888
 
     * @default "yui-dt-hidden"
2889
 
     */
2890
 
    CLASS_HIDDEN : "yui-dt-hidden",
2891
 
 
2892
 
    /**
2893
 
     * Class name assigned to disabled elements.
2894
 
     *
2895
 
     * @property DataTable.CLASS_DISABLED
2896
 
     * @type String
2897
 
     * @static
2898
 
     * @final
2899
 
     * @default "yui-dt-disabled"
2900
 
     */
2901
 
    CLASS_DISABLED : "yui-dt-disabled",
2902
 
 
2903
 
    /**
2904
 
     * Class name assigned to empty indicators.
2905
 
     *
2906
 
     * @property DataTable.CLASS_EMPTY
2907
 
     * @type String
2908
 
     * @static
2909
 
     * @final
2910
 
     * @default "yui-dt-empty"
2911
 
     */
2912
 
    CLASS_EMPTY : "yui-dt-empty",
2913
 
 
2914
 
    /**
2915
 
     * Class name assigned to loading indicatorx.
2916
 
     *
2917
 
     * @property DataTable.CLASS_LOADING
2918
 
     * @type String
2919
 
     * @static
2920
 
     * @final
2921
 
     * @default "yui-dt-loading"
2922
 
     */
2923
 
    CLASS_LOADING : "yui-dt-loading",
2924
 
 
2925
 
    /**
2926
 
     * Class name assigned to error indicators.
2927
 
     *
2928
 
     * @property DataTable.CLASS_ERROR
2929
 
     * @type String
2930
 
     * @static
2931
 
     * @final
2932
 
     * @default "yui-dt-error"
2933
 
     */
2934
 
    CLASS_ERROR : "yui-dt-error",
2935
 
 
2936
 
    /**
2937
 
     * Class name assigned to editable elements.
2938
 
     *
2939
 
     * @property DataTable.CLASS_EDITABLE
2940
 
     * @type String
2941
 
     * @static
2942
 
     * @final
2943
 
     * @default "yui-dt-editable"
2944
 
     */
2945
 
    CLASS_EDITABLE : "yui-dt-editable",
2946
 
 
2947
 
    /**
2948
 
     * Class name assigned to draggable elements.
2949
 
     *
2950
 
     * @property DataTable.CLASS_DRAGGABLE
2951
 
     * @type String
2952
 
     * @static
2953
 
     * @final
2954
 
     * @default "yui-dt-draggable"
2955
 
     */
2956
 
    CLASS_DRAGGABLE : "yui-dt-draggable",
2957
 
 
2958
 
    /**
2959
 
     * Class name assigned to resizeable elements.
2960
 
     *
2961
 
     * @property DataTable.CLASS_RESIZEABLE
2962
 
     * @type String
2963
 
     * @static
2964
 
     * @final
2965
 
     * @default "yui-dt-resizeable"
2966
 
     */
2967
 
    CLASS_RESIZEABLE : "yui-dt-resizeable",
2968
 
 
2969
 
    /**
2970
 
     * Class name assigned to scrollable elements.
2971
 
     *
2972
 
     * @property DataTable.CLASS_SCROLLABLE
2973
 
     * @type String
2974
 
     * @static
2975
 
     * @final
2976
 
     * @default "yui-dt-scrollable"
2977
 
     */
2978
 
    CLASS_SCROLLABLE : "yui-dt-scrollable",
2979
 
 
2980
 
    /**
2981
 
     * Class name assigned to sortable elements.
2982
 
     *
2983
 
     * @property DataTable.CLASS_SORTABLE
2984
 
     * @type String
2985
 
     * @static
2986
 
     * @final
2987
 
     * @default "yui-dt-sortable"
2988
 
     */
2989
 
    CLASS_SORTABLE : "yui-dt-sortable",
2990
 
 
2991
 
    /**
2992
 
     * Class name assigned to ascending elements.
2993
 
     *
2994
 
     * @property DataTable.CLASS_ASC
2995
 
     * @type String
2996
 
     * @static
2997
 
     * @final
2998
 
     * @default "yui-dt-asc"
2999
 
     */
3000
 
    CLASS_ASC : "yui-dt-asc",
3001
 
 
3002
 
    /**
3003
 
     * Class name assigned to descending elements.
3004
 
     *
3005
 
     * @property DataTable.CLASS_DESC
3006
 
     * @type String
3007
 
     * @static
3008
 
     * @final
3009
 
     * @default "yui-dt-desc"
3010
 
     */
3011
 
    CLASS_DESC : "yui-dt-desc",
3012
 
 
3013
 
    /**
3014
 
     * Class name assigned to BUTTON elements and/or container elements.
3015
 
     *
3016
 
     * @property DataTable.CLASS_BUTTON
3017
 
     * @type String
3018
 
     * @static
3019
 
     * @final
3020
 
     * @default "yui-dt-button"
3021
 
     */
3022
 
    CLASS_BUTTON : "yui-dt-button",
3023
 
 
3024
 
    /**
3025
 
     * Class name assigned to INPUT TYPE=CHECKBOX elements and/or container elements.
3026
 
     *
3027
 
     * @property DataTable.CLASS_CHECKBOX
3028
 
     * @type String
3029
 
     * @static
3030
 
     * @final
3031
 
     * @default "yui-dt-checkbox"
3032
 
     */
3033
 
    CLASS_CHECKBOX : "yui-dt-checkbox",
3034
 
 
3035
 
    /**
3036
 
     * Class name assigned to SELECT elements and/or container elements.
3037
 
     *
3038
 
     * @property DataTable.CLASS_DROPDOWN
3039
 
     * @type String
3040
 
     * @static
3041
 
     * @final
3042
 
     * @default "yui-dt-dropdown"
3043
 
     */
3044
 
    CLASS_DROPDOWN : "yui-dt-dropdown",
3045
 
 
3046
 
    /**
3047
 
     * Class name assigned to INPUT TYPE=RADIO elements and/or container elements.
3048
 
     *
3049
 
     * @property DataTable.CLASS_RADIO
3050
 
     * @type String
3051
 
     * @static
3052
 
     * @final
3053
 
     * @default "yui-dt-radio"
3054
 
     */
3055
 
    CLASS_RADIO : "yui-dt-radio",
3056
 
 
3057
 
    /////////////////////////////////////////////////////////////////////////
3058
 
    //
3059
 
    // Private static properties
3060
 
    //
3061
 
    /////////////////////////////////////////////////////////////////////////
3062
 
 
3063
 
    /**
3064
 
     * Internal class variable for indexing multiple DataTable instances.
3065
 
     *
3066
 
     * @property DataTable._nCount
3067
 
     * @type Number
3068
 
     * @private
3069
 
     * @static
3070
 
     */
3071
 
    _nCount : 0,
3072
 
 
3073
 
    /**
3074
 
     * Internal class variable tracking current number of DataTable instances,
3075
 
     * so that certain class values can be reset when all instances are destroyed.          
3076
 
     *
3077
 
     * @property DataTable._nCurrentCount
3078
 
     * @type Number
3079
 
     * @private
3080
 
     * @static
3081
 
     */
3082
 
    _nCurrentCount : 0,
3083
 
 
3084
 
    /**
3085
 
     * Reference to the STYLE node that is dynamically created and updated
3086
 
     * in order to manage Column widths.
3087
 
     *
3088
 
     * @property DataTable._elDynStyleNode
3089
 
     * @type HTMLElement
3090
 
     * @private
3091
 
     * @static     
3092
 
     */
3093
 
    _elDynStyleNode : null,
3094
 
 
3095
 
    /**
3096
 
     * Set to true if _elDynStyleNode cannot be populated due to browser incompatibility.
3097
 
     *
3098
 
     * @property DataTable._bDynStylesFallback
3099
 
     * @type boolean
3100
 
     * @private
3101
 
     * @static     
3102
 
     */
3103
 
    _bDynStylesFallback : (ua.ie && (ua.ie<7)) ? true : false,
3104
 
 
3105
 
    /**
3106
 
     * Object literal hash of Columns and their dynamically create style rules.
3107
 
     *
3108
 
     * @property DataTable._oDynStyles
3109
 
     * @type Object
3110
 
     * @private
3111
 
     * @static     
3112
 
     */
3113
 
    _oDynStyles : {},
3114
 
 
3115
 
    /**
3116
 
     * Element reference to shared Column drag target.
3117
 
     *
3118
 
     * @property DataTable._elColumnDragTarget
3119
 
     * @type HTMLElement
3120
 
     * @private
3121
 
     * @static 
3122
 
     */
3123
 
    _elColumnDragTarget : null,
3124
 
 
3125
 
    /**
3126
 
     * Element reference to shared Column resizer proxy.
3127
 
     *
3128
 
     * @property DataTable._elColumnResizerProxy
3129
 
     * @type HTMLElement
3130
 
     * @private
3131
 
     * @static 
3132
 
     */
3133
 
    _elColumnResizerProxy : null,
3134
 
 
3135
 
    /////////////////////////////////////////////////////////////////////////
3136
 
    //
3137
 
    // Private static methods
3138
 
    //
3139
 
    /////////////////////////////////////////////////////////////////////////
3140
 
 
3141
 
    /**
3142
 
     * Clones object literal or array of object literals.
3143
 
     *
3144
 
     * @method DataTable._cloneObject
3145
 
     * @param o {Object} Object.
3146
 
     * @private
3147
 
     * @static     
3148
 
     */
3149
 
    _cloneObject : function(o) {
3150
 
        if(!lang.isValue(o)) {
3151
 
            return o;
3152
 
        }
3153
 
        
3154
 
        var copy = {};
3155
 
        
3156
 
        if(o instanceof YAHOO.widget.BaseCellEditor) {
3157
 
            copy = o;
3158
 
        }
3159
 
        else if(lang.isFunction(o)) {
3160
 
            copy = o;
3161
 
        }
3162
 
        else if(lang.isArray(o)) {
3163
 
            var array = [];
3164
 
            for(var i=0,len=o.length;i<len;i++) {
3165
 
                array[i] = DT._cloneObject(o[i]);
3166
 
            }
3167
 
            copy = array;
3168
 
        }
3169
 
        else if(lang.isObject(o)) { 
3170
 
            for (var x in o){
3171
 
                if(lang.hasOwnProperty(o, x)) {
3172
 
                    if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
3173
 
                        copy[x] = DT._cloneObject(o[x]);
3174
 
                    }
3175
 
                    else {
3176
 
                        copy[x] = o[x];
3177
 
                    }
3178
 
                }
3179
 
            }
3180
 
        }
3181
 
        else {
3182
 
            copy = o;
3183
 
        }
3184
 
    
3185
 
        return copy;
3186
 
    },
3187
 
 
3188
 
    /**
3189
 
     * Destroys shared Column drag target.
3190
 
     *
3191
 
     * @method DataTable._destroyColumnDragTargetEl
3192
 
     * @private
3193
 
     * @static 
3194
 
     */
3195
 
    _destroyColumnDragTargetEl : function() {
3196
 
        if(DT._elColumnDragTarget) {
3197
 
            var el = DT._elColumnDragTarget;
3198
 
            YAHOO.util.Event.purgeElement(el);
3199
 
            el.parentNode.removeChild(el);
3200
 
            DT._elColumnDragTarget = null;
3201
 
            
3202
 
        }
3203
 
    },
3204
 
 
3205
 
    /**
3206
 
     * Creates HTML markup for shared Column drag target.
3207
 
     *
3208
 
     * @method DataTable._initColumnDragTargetEl
3209
 
     * @return {HTMLElement} Reference to Column drag target. 
3210
 
     * @private
3211
 
     * @static 
3212
 
     */
3213
 
    _initColumnDragTargetEl : function() {
3214
 
        if(!DT._elColumnDragTarget) {
3215
 
            // Attach Column drag target element as first child of body
3216
 
            var elColumnDragTarget = document.createElement('div');
3217
 
            elColumnDragTarget.className = DT.CLASS_COLTARGET;
3218
 
            elColumnDragTarget.style.display = "none";
3219
 
            document.body.insertBefore(elColumnDragTarget, document.body.firstChild);
3220
 
 
3221
 
            // Internal tracker of Column drag target
3222
 
            DT._elColumnDragTarget = elColumnDragTarget;
3223
 
            
3224
 
        }
3225
 
        return DT._elColumnDragTarget;
3226
 
    },
3227
 
 
3228
 
    /**
3229
 
     * Destroys shared Column resizer proxy.
3230
 
     *
3231
 
     * @method DataTable._destroyColumnResizerProxyEl
3232
 
     * @return {HTMLElement} Reference to Column resizer proxy.
3233
 
     * @private 
3234
 
     * @static 
3235
 
     */
3236
 
    _destroyColumnResizerProxyEl : function() {
3237
 
        if(DT._elColumnResizerProxy) {
3238
 
            var el = DT._elColumnResizerProxy;
3239
 
            YAHOO.util.Event.purgeElement(el);
3240
 
            el.parentNode.removeChild(el);
3241
 
            DT._elColumnResizerProxy = null;
3242
 
        }
3243
 
    },
3244
 
 
3245
 
    /**
3246
 
     * Creates HTML markup for shared Column resizer proxy.
3247
 
     *
3248
 
     * @method DataTable._initColumnResizerProxyEl
3249
 
     * @return {HTMLElement} Reference to Column resizer proxy.
3250
 
     * @private 
3251
 
     * @static 
3252
 
     */
3253
 
    _initColumnResizerProxyEl : function() {
3254
 
        if(!DT._elColumnResizerProxy) {
3255
 
            // Attach Column resizer element as first child of body
3256
 
            var elColumnResizerProxy = document.createElement("div");
3257
 
            elColumnResizerProxy.id = "yui-dt-colresizerproxy"; // Needed for ColumnResizer
3258
 
            elColumnResizerProxy.className = DT.CLASS_RESIZERPROXY;
3259
 
            document.body.insertBefore(elColumnResizerProxy, document.body.firstChild);
3260
 
 
3261
 
            // Internal tracker of Column resizer proxy
3262
 
            DT._elColumnResizerProxy = elColumnResizerProxy;
3263
 
        }
3264
 
        return DT._elColumnResizerProxy;
3265
 
    },
3266
 
 
3267
 
    /**
3268
 
     * Formats a BUTTON element.
3269
 
     *
3270
 
     * @method DataTable.formatButton
3271
 
     * @param el {HTMLElement} The element to format with markup.
3272
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3273
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3274
 
     * @param oData {Object | Boolean} Data value for the cell. By default, the value
3275
 
     * is what gets written to the BUTTON.
3276
 
     * @static
3277
 
     */
3278
 
    formatButton : function(el, oRecord, oColumn, oData) {
3279
 
        var sValue = lang.isValue(oData) ? oData : "Click";
3280
 
        //TODO: support YAHOO.widget.Button
3281
 
        //if(YAHOO.widget.Button) {
3282
 
 
3283
 
        //}
3284
 
        //else {
3285
 
            el.innerHTML = "<button type=\"button\" class=\""+
3286
 
                    DT.CLASS_BUTTON + "\">" + sValue + "</button>";
3287
 
        //}
3288
 
    },
3289
 
 
3290
 
    /**
3291
 
     * Formats a CHECKBOX element.
3292
 
     *
3293
 
     * @method DataTable.formatCheckbox
3294
 
     * @param el {HTMLElement} The element to format with markup.
3295
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3296
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3297
 
     * @param oData {Object | Boolean} Data value for the cell. Can be a simple
3298
 
     * Boolean to indicate whether checkbox is checked or not. Can be object literal
3299
 
     * {checked:bBoolean, label:sLabel}. Other forms of oData require a custom
3300
 
     * formatter.
3301
 
     * @static
3302
 
     */
3303
 
    formatCheckbox : function(el, oRecord, oColumn, oData) {
3304
 
        var bChecked = oData;
3305
 
        bChecked = (bChecked) ? " checked=\"checked\"" : "";
3306
 
        el.innerHTML = "<input type=\"checkbox\"" + bChecked +
3307
 
                " class=\"" + DT.CLASS_CHECKBOX + "\" />";
3308
 
    },
3309
 
 
3310
 
    /**
3311
 
     * Formats currency. Default unit is USD.
3312
 
     *
3313
 
     * @method DataTable.formatCurrency
3314
 
     * @param el {HTMLElement} The element to format with markup.
3315
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3316
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3317
 
     * @param oData {Number} Data value for the cell.
3318
 
     * @static
3319
 
     */
3320
 
    formatCurrency : function(el, oRecord, oColumn, oData) {
3321
 
        el.innerHTML = util.Number.format(oData, oColumn.currencyOptions || this.get("currencyOptions"));
3322
 
    },
3323
 
 
3324
 
    /**
3325
 
     * Formats JavaScript Dates.
3326
 
     *
3327
 
     * @method DataTable.formatDate
3328
 
     * @param el {HTMLElement} The element to format with markup.
3329
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3330
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3331
 
     * @param oData {Object} Data value for the cell, or null.
3332
 
     * @static
3333
 
     */
3334
 
    formatDate : function(el, oRecord, oColumn, oData) {
3335
 
        var oConfig = oColumn.dateOptions || this.get("dateOptions");
3336
 
        el.innerHTML = util.Date.format(oData, oConfig, oConfig.locale);
3337
 
    },
3338
 
 
3339
 
    /**
3340
 
     * Formats SELECT elements.
3341
 
     *
3342
 
     * @method DataTable.formatDropdown
3343
 
     * @param el {HTMLElement} The element to format with markup.
3344
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3345
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3346
 
     * @param oData {Object} Data value for the cell, or null.
3347
 
     * @static
3348
 
     */
3349
 
    formatDropdown : function(el, oRecord, oColumn, oData) {
3350
 
        var selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field),
3351
 
            options = (lang.isArray(oColumn.dropdownOptions)) ?
3352
 
                oColumn.dropdownOptions : null,
3353
 
 
3354
 
            selectEl,
3355
 
            collection = el.getElementsByTagName("select");
3356
 
 
3357
 
        // Create the form element only once, so we can attach the onChange listener
3358
 
        if(collection.length === 0) {
3359
 
            // Create SELECT element
3360
 
            selectEl = document.createElement("select");
3361
 
            selectEl.className = DT.CLASS_DROPDOWN;
3362
 
            selectEl = el.appendChild(selectEl);
3363
 
 
3364
 
            // Add event listener
3365
 
            Ev.addListener(selectEl,"change",this._onDropdownChange,this);
3366
 
        }
3367
 
 
3368
 
        selectEl = collection[0];
3369
 
 
3370
 
        // Update the form element
3371
 
        if(selectEl) {
3372
 
            // Clear out previous options
3373
 
            selectEl.innerHTML = "";
3374
 
 
3375
 
            // We have options to populate
3376
 
            if(options) {
3377
 
                // Create OPTION elements
3378
 
                for(var i=0; i<options.length; i++) {
3379
 
                    var option = options[i];
3380
 
                    var optionEl = document.createElement("option");
3381
 
                    optionEl.value = (lang.isValue(option.value)) ?
3382
 
                            option.value : option;
3383
 
                    // Bug 2334323: Support legacy text, support label for consistency with DropdownCellEditor
3384
 
                    optionEl.innerHTML = (lang.isValue(option.text)) ?
3385
 
                            option.text : (lang.isValue(option.label)) ? option.label : option;
3386
 
                    optionEl = selectEl.appendChild(optionEl);
3387
 
                    if (optionEl.value == selectedValue) {
3388
 
                        optionEl.selected = true;
3389
 
                    }
3390
 
                }
3391
 
            }
3392
 
            // Selected value is our only option
3393
 
            else {
3394
 
                selectEl.innerHTML = "<option selected value=\"" + selectedValue + "\">" + selectedValue + "</option>";
3395
 
            }
3396
 
        }
3397
 
        else {
3398
 
            el.innerHTML = lang.isValue(oData) ? oData : "";
3399
 
        }
3400
 
    },
3401
 
 
3402
 
    /**
3403
 
     * Formats emails.
3404
 
     *
3405
 
     * @method DataTable.formatEmail
3406
 
     * @param el {HTMLElement} The element to format with markup.
3407
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3408
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3409
 
     * @param oData {Object} Data value for the cell, or null.
3410
 
     * @static
3411
 
     */
3412
 
    formatEmail : function(el, oRecord, oColumn, oData) {
3413
 
        if(lang.isString(oData)) {
3414
 
            el.innerHTML = "<a href=\"mailto:" + oData + "\">" + oData + "</a>";
3415
 
        }
3416
 
        else {
3417
 
            el.innerHTML = lang.isValue(oData) ? oData : "";
3418
 
        }
3419
 
    },
3420
 
 
3421
 
    /**
3422
 
     * Formats links.
3423
 
     *
3424
 
     * @method DataTable.formatLink
3425
 
     * @param el {HTMLElement} The element to format with markup.
3426
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3427
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3428
 
     * @param oData {Object} Data value for the cell, or null.
3429
 
     * @static
3430
 
     */
3431
 
    formatLink : function(el, oRecord, oColumn, oData) {
3432
 
        if(lang.isString(oData)) {
3433
 
            el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>";
3434
 
        }
3435
 
        else {
3436
 
            el.innerHTML = lang.isValue(oData) ? oData : "";
3437
 
        }
3438
 
    },
3439
 
 
3440
 
    /**
3441
 
     * Formats numbers.
3442
 
     *
3443
 
     * @method DataTable.formatNumber
3444
 
     * @param el {HTMLElement} The element to format with markup.
3445
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3446
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3447
 
     * @param oData {Object} Data value for the cell, or null.
3448
 
     * @static
3449
 
     */
3450
 
    formatNumber : function(el, oRecord, oColumn, oData) {
3451
 
        el.innerHTML = util.Number.format(oData, oColumn.numberOptions || this.get("numberOptions"));
3452
 
    },
3453
 
 
3454
 
    /**
3455
 
     * Formats INPUT TYPE=RADIO elements.
3456
 
     *
3457
 
     * @method DataTable.formatRadio
3458
 
     * @param el {HTMLElement} The element to format with markup.
3459
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3460
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3461
 
     * @param oData {Object} (Optional) Data value for the cell.
3462
 
     * @static
3463
 
     */
3464
 
    formatRadio : function(el, oRecord, oColumn, oData) {
3465
 
        var bChecked = oData;
3466
 
        bChecked = (bChecked) ? " checked=\"checked\"" : "";
3467
 
        el.innerHTML = "<input type=\"radio\"" + bChecked +
3468
 
                " name=\""+this.getId()+"-col-" + oColumn.getSanitizedKey() + "\"" +
3469
 
                " class=\"" + DT.CLASS_RADIO+ "\" />";
3470
 
    },
3471
 
 
3472
 
    /**
3473
 
     * Formats text strings.
3474
 
     *
3475
 
     * @method DataTable.formatText
3476
 
     * @param el {HTMLElement} The element to format with markup.
3477
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3478
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3479
 
     * @param oData {Object} (Optional) Data value for the cell.
3480
 
     * @static
3481
 
     */
3482
 
    formatText : function(el, oRecord, oColumn, oData) {
3483
 
        var value = (lang.isValue(oData)) ? oData : "";
3484
 
        //TODO: move to util function
3485
 
        el.innerHTML = value.toString().replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;");
3486
 
    },
3487
 
 
3488
 
    /**
3489
 
     * Formats TEXTAREA elements.
3490
 
     *
3491
 
     * @method DataTable.formatTextarea
3492
 
     * @param el {HTMLElement} The element to format with markup.
3493
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3494
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3495
 
     * @param oData {Object} (Optional) Data value for the cell.
3496
 
     * @static
3497
 
     */
3498
 
    formatTextarea : function(el, oRecord, oColumn, oData) {
3499
 
        var value = (lang.isValue(oData)) ? oData : "",
3500
 
            markup = "<textarea>" + value + "</textarea>";
3501
 
        el.innerHTML = markup;
3502
 
    },
3503
 
 
3504
 
    /**
3505
 
     * Formats INPUT TYPE=TEXT elements.
3506
 
     *
3507
 
     * @method DataTable.formatTextbox
3508
 
     * @param el {HTMLElement} The element to format with markup.
3509
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3510
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3511
 
     * @param oData {Object} (Optional) Data value for the cell.
3512
 
     * @static
3513
 
     */
3514
 
    formatTextbox : function(el, oRecord, oColumn, oData) {
3515
 
        var value = (lang.isValue(oData)) ? oData : "",
3516
 
            markup = "<input type=\"text\" value=\"" + value + "\" />";
3517
 
        el.innerHTML = markup;
3518
 
    },
3519
 
 
3520
 
    /**
3521
 
     * Default cell formatter
3522
 
     *
3523
 
     * @method DataTable.formatDefault
3524
 
     * @param el {HTMLElement} The element to format with markup.
3525
 
     * @param oRecord {YAHOO.widget.Record} Record instance.
3526
 
     * @param oColumn {YAHOO.widget.Column} Column instance.
3527
 
     * @param oData {Object} (Optional) Data value for the cell.
3528
 
     * @static
3529
 
     */
3530
 
    formatDefault : function(el, oRecord, oColumn, oData) {
3531
 
        el.innerHTML = oData === undefined ||
3532
 
                       oData === null ||
3533
 
                       (typeof oData === 'number' && isNaN(oData)) ?
3534
 
                       "&#160;" : oData.toString();
3535
 
    },
3536
 
 
3537
 
    /**
3538
 
     * Validates data value to type Number, doing type conversion as
3539
 
     * necessary. A valid Number value is return, else null is returned
3540
 
     * if input value does not validate.
3541
 
     *
3542
 
     *
3543
 
     * @method DataTable.validateNumber
3544
 
     * @param oData {Object} Data to validate.
3545
 
     * @static
3546
 
    */
3547
 
    validateNumber : function(oData) {
3548
 
        //Convert to number
3549
 
        var number = oData * 1;
3550
 
 
3551
 
        // Validate
3552
 
        if(lang.isNumber(number)) {
3553
 
            return number;
3554
 
        }
3555
 
        else {
3556
 
            YAHOO.log("Could not validate data " + lang.dump(oData) + " to type Number", "warn", this.toString());
3557
 
            return undefined;
3558
 
        }
3559
 
    }
3560
 
});
3561
 
 
3562
 
// Done in separate step so referenced functions are defined.
3563
 
/**
3564
 
 * Cell formatting functions.
3565
 
 * @property DataTable.Formatter
3566
 
 * @type Object
3567
 
 * @static
3568
 
 */
3569
 
DT.Formatter = {
3570
 
    button   : DT.formatButton,
3571
 
    checkbox : DT.formatCheckbox,
3572
 
    currency : DT.formatCurrency,
3573
 
    "date"   : DT.formatDate,
3574
 
    dropdown : DT.formatDropdown,
3575
 
    email    : DT.formatEmail,
3576
 
    link     : DT.formatLink,
3577
 
    "number" : DT.formatNumber,
3578
 
    radio    : DT.formatRadio,
3579
 
    text     : DT.formatText,
3580
 
    textarea : DT.formatTextarea,
3581
 
    textbox  : DT.formatTextbox,
3582
 
 
3583
 
    defaultFormatter : DT.formatDefault
3584
 
};
3585
 
 
3586
 
lang.extend(DT, util.Element, {
3587
 
 
3588
 
/////////////////////////////////////////////////////////////////////////////
3589
 
//
3590
 
// Superclass methods
3591
 
//
3592
 
/////////////////////////////////////////////////////////////////////////////
3593
 
 
3594
 
/**
3595
 
 * Implementation of Element's abstract method. Sets up config values.
3596
 
 *
3597
 
 * @method initAttributes
3598
 
 * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
3599
 
 * @private
3600
 
 */
3601
 
 
3602
 
initAttributes : function(oConfigs) {
3603
 
    oConfigs = oConfigs || {};
3604
 
    DT.superclass.initAttributes.call(this, oConfigs);
3605
 
 
3606
 
    /**
3607
 
    * @attribute summary
3608
 
    * @description Value for the SUMMARY attribute.
3609
 
    * @type String
3610
 
    * @default ""    
3611
 
    */
3612
 
    this.setAttributeConfig("summary", {
3613
 
        value: "",
3614
 
        validator: lang.isString,
3615
 
        method: function(sSummary) {
3616
 
            if(this._elTable) {
3617
 
                this._elTable.summary = sSummary;
3618
 
            }
3619
 
        }
3620
 
    });
3621
 
 
3622
 
    /**
3623
 
    * @attribute selectionMode
3624
 
    * @description Specifies row or cell selection mode. Accepts the following strings:
3625
 
    *    <dl>
3626
 
    *      <dt>"standard"</dt>
3627
 
    *      <dd>Standard row selection with support for modifier keys to enable
3628
 
    *      multiple selections.</dd>
3629
 
    *
3630
 
    *      <dt>"single"</dt>
3631
 
    *      <dd>Row selection with modifier keys disabled to not allow
3632
 
    *      multiple selections.</dd>
3633
 
    *
3634
 
    *      <dt>"singlecell"</dt>
3635
 
    *      <dd>Cell selection with modifier keys disabled to not allow
3636
 
    *      multiple selections.</dd>
3637
 
    *
3638
 
    *      <dt>"cellblock"</dt>
3639
 
    *      <dd>Cell selection with support for modifier keys to enable multiple
3640
 
    *      selections in a block-fashion, like a spreadsheet.</dd>
3641
 
    *
3642
 
    *      <dt>"cellrange"</dt>
3643
 
    *      <dd>Cell selection with support for modifier keys to enable multiple
3644
 
    *      selections in a range-fashion, like a calendar.</dd>
3645
 
    *    </dl>
3646
 
    *
3647
 
    * @default "standard"
3648
 
    * @type String
3649
 
    */
3650
 
    this.setAttributeConfig("selectionMode", {
3651
 
        value: "standard",
3652
 
        validator: lang.isString
3653
 
    });
3654
 
 
3655
 
    /**
3656
 
    * @attribute sortedBy
3657
 
    * @description Object literal provides metadata for initial sort values if
3658
 
    * data will arrive pre-sorted:
3659
 
    * <dl>
3660
 
    *     <dt>sortedBy.key</dt>
3661
 
    *     <dd>{String} Key of sorted Column</dd>
3662
 
    *     <dt>sortedBy.dir</dt>
3663
 
    *     <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
3664
 
    * </dl>
3665
 
    * @type Object | null
3666
 
    */
3667
 
    this.setAttributeConfig("sortedBy", {
3668
 
        value: null,
3669
 
        // TODO: accepted array for nested sorts
3670
 
        validator: function(oNewSortedBy) {
3671
 
            if(oNewSortedBy) {
3672
 
                return (lang.isObject(oNewSortedBy) && oNewSortedBy.key);
3673
 
            }
3674
 
            else {
3675
 
                return (oNewSortedBy === null);
3676
 
            }
3677
 
        },
3678
 
        method: function(oNewSortedBy) {
3679
 
            // Stash the previous value
3680
 
            var oOldSortedBy = this.get("sortedBy");
3681
 
            
3682
 
            // Workaround for bug 1827195
3683
 
            this._configs.sortedBy.value = oNewSortedBy;
3684
 
 
3685
 
            // Remove ASC/DESC from TH
3686
 
            var oOldColumn,
3687
 
                nOldColumnKeyIndex,
3688
 
                oNewColumn,
3689
 
                nNewColumnKeyIndex;
3690
 
                
3691
 
            if(this._elThead) {
3692
 
                if(oOldSortedBy && oOldSortedBy.key && oOldSortedBy.dir) {
3693
 
                    oOldColumn = this._oColumnSet.getColumn(oOldSortedBy.key);
3694
 
                    nOldColumnKeyIndex = oOldColumn.getKeyIndex();
3695
 
                    
3696
 
                    // Remove previous UI from THEAD
3697
 
                    var elOldTh = oOldColumn.getThEl();
3698
 
                    Dom.removeClass(elOldTh, oOldSortedBy.dir);
3699
 
                    this.formatTheadCell(oOldColumn.getThLinerEl().firstChild, oOldColumn, oNewSortedBy);
3700
 
                }
3701
 
                if(oNewSortedBy) {
3702
 
                    oNewColumn = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key);
3703
 
                    nNewColumnKeyIndex = oNewColumn.getKeyIndex();
3704
 
    
3705
 
                    // Update THEAD with new UI
3706
 
                    var elNewTh = oNewColumn.getThEl();
3707
 
                    // Backward compatibility
3708
 
                    if(oNewSortedBy.dir && ((oNewSortedBy.dir == "asc") ||  (oNewSortedBy.dir == "desc"))) {
3709
 
                        var newClass = (oNewSortedBy.dir == "desc") ?
3710
 
                                DT.CLASS_DESC :
3711
 
                                DT.CLASS_ASC;
3712
 
                        Dom.addClass(elNewTh, newClass);
3713
 
                    }
3714
 
                    else {
3715
 
                         var sortClass = oNewSortedBy.dir || DT.CLASS_ASC;
3716
 
                         Dom.addClass(elNewTh, sortClass);
3717
 
                    }
3718
 
                    this.formatTheadCell(oNewColumn.getThLinerEl().firstChild, oNewColumn, oNewSortedBy);
3719
 
                }
3720
 
            }
3721
 
          
3722
 
            if(this._elTbody) {
3723
 
                // Update TBODY UI
3724
 
                this._elTbody.style.display = "none";
3725
 
                var allRows = this._elTbody.rows,
3726
 
                    allCells;
3727
 
                for(var i=allRows.length-1; i>-1; i--) {
3728
 
                    allCells = allRows[i].childNodes;
3729
 
                    if(allCells[nOldColumnKeyIndex]) {
3730
 
                        Dom.removeClass(allCells[nOldColumnKeyIndex], oOldSortedBy.dir);
3731
 
                    }
3732
 
                    if(allCells[nNewColumnKeyIndex]) {
3733
 
                        Dom.addClass(allCells[nNewColumnKeyIndex], oNewSortedBy.dir);
3734
 
                    }
3735
 
                }
3736
 
                this._elTbody.style.display = "";
3737
 
            }
3738
 
                
3739
 
            this._clearTrTemplateEl();
3740
 
        }
3741
 
    });
3742
 
    
3743
 
    /**
3744
 
    * @attribute paginator
3745
 
    * @description An instance of YAHOO.widget.Paginator.
3746
 
    * @default null
3747
 
    * @type {Object|YAHOO.widget.Paginator}
3748
 
    */
3749
 
    this.setAttributeConfig("paginator", {
3750
 
        value : null,
3751
 
        validator : function (val) {
3752
 
            return val === null || val instanceof widget.Paginator;
3753
 
        },
3754
 
        method : function () { this._updatePaginator.apply(this,arguments); }
3755
 
    });
3756
 
 
3757
 
    /**
3758
 
    * @attribute caption
3759
 
    * @description Value for the CAPTION element. NB: Not supported in
3760
 
    * ScrollingDataTable.    
3761
 
    * @type String
3762
 
    */
3763
 
    this.setAttributeConfig("caption", {
3764
 
        value: null,
3765
 
        validator: lang.isString,
3766
 
        method: function(sCaption) {
3767
 
            this._initCaptionEl(sCaption);
3768
 
        }
3769
 
    });
3770
 
 
3771
 
    /**
3772
 
    * @attribute draggableColumns
3773
 
    * @description True if Columns are draggable to reorder, false otherwise.
3774
 
    * The Drag & Drop Utility is required to enable this feature. Only top-level
3775
 
    * and non-nested Columns are draggable. Write once.
3776
 
    * @default false
3777
 
    * @type Boolean
3778
 
    */
3779
 
    this.setAttributeConfig("draggableColumns", {
3780
 
        value: false,
3781
 
        validator: lang.isBoolean,
3782
 
        method: function(oParam) {
3783
 
            if(this._elThead) {
3784
 
                if(oParam) {
3785
 
                    this._initDraggableColumns();
3786
 
                }
3787
 
                else {
3788
 
                    this._destroyDraggableColumns();
3789
 
                }
3790
 
            }
3791
 
        }
3792
 
    });
3793
 
 
3794
 
    /**
3795
 
    * @attribute renderLoopSize          
3796
 
    * @description A value greater than 0 enables DOM rendering of rows to be
3797
 
    * executed from a non-blocking timeout queue and sets how many rows to be
3798
 
    * rendered per timeout. Recommended for very large data sets.     
3799
 
    * @type Number       
3800
 
    * @default 0         
3801
 
    */   
3802
 
     this.setAttributeConfig("renderLoopSize", {         
3803
 
         value: 0,       
3804
 
         validator: lang.isNumber        
3805
 
     });         
3806
 
 
3807
 
    /**
3808
 
    * @attribute formatRow
3809
 
    * @description A function that accepts a TR element and its associated Record
3810
 
    * for custom formatting. The function must return TRUE in order to automatically
3811
 
    * continue formatting of child TD elements, else TD elements will not be
3812
 
    * automatically formatted.
3813
 
    * @type function
3814
 
    * @default null
3815
 
    */
3816
 
    this.setAttributeConfig("formatRow", {
3817
 
        value: null,
3818
 
        validator: lang.isFunction
3819
 
    });
3820
 
 
3821
 
    /**
3822
 
    * @attribute generateRequest
3823
 
    * @description A function that converts an object literal of desired DataTable
3824
 
    * states into a request value which is then passed to the DataSource's
3825
 
    * sendRequest method in order to retrieve data for those states. This
3826
 
    * function is passed an object literal of state data and a reference to the
3827
 
    * DataTable instance:
3828
 
    *     
3829
 
    * <dl>
3830
 
    *   <dt>pagination<dt>
3831
 
    *   <dd>        
3832
 
    *         <dt>offsetRecord</dt>
3833
 
    *         <dd>{Number} Index of the first Record of the desired page</dd>
3834
 
    *         <dt>rowsPerPage</dt>
3835
 
    *         <dd>{Number} Number of rows per page</dd>
3836
 
    *   </dd>
3837
 
    *   <dt>sortedBy</dt>
3838
 
    *   <dd>                
3839
 
    *         <dt>key</dt>
3840
 
    *         <dd>{String} Key of sorted Column</dd>
3841
 
    *         <dt>dir</dt>
3842
 
    *         <dd>{String} Sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
3843
 
    *   </dd>
3844
 
    *   <dt>self</dt>
3845
 
    *   <dd>The DataTable instance</dd>
3846
 
    * </dl>
3847
 
    * 
3848
 
    * and by default returns a String of syntax:
3849
 
    * "sort={sortColumn}&dir={sortDir}&startIndex={pageStartIndex}&results={rowsPerPage}"
3850
 
    * @type function
3851
 
    * @default HTMLFunction
3852
 
    */
3853
 
    this.setAttributeConfig("generateRequest", {
3854
 
        value: function(oState, oSelf) {
3855
 
            // Set defaults
3856
 
            oState = oState || {pagination:null, sortedBy:null};
3857
 
            var sort = (oState.sortedBy) ? oState.sortedBy.key : oSelf.getColumnSet().keys[0].getKey();
3858
 
            var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
3859
 
            var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
3860
 
            var results = (oState.pagination) ? oState.pagination.rowsPerPage : null;
3861
 
            
3862
 
            // Build the request
3863
 
            return  "sort=" + sort +
3864
 
                    "&dir=" + dir +
3865
 
                    "&startIndex=" + startIndex +
3866
 
                    ((results !== null) ? "&results=" + results : "");
3867
 
        },
3868
 
        validator: lang.isFunction
3869
 
    });
3870
 
 
3871
 
    /**
3872
 
    * @attribute initialRequest
3873
 
    * @description Defines the initial request that gets sent to the DataSource
3874
 
    * during initialization. Value is ignored if initialLoad is set to any value
3875
 
    * other than true.    
3876
 
    * @type MIXED
3877
 
    * @default null
3878
 
    */
3879
 
    this.setAttributeConfig("initialRequest", {
3880
 
        value: null
3881
 
    });
3882
 
 
3883
 
    /**
3884
 
    * @attribute initialLoad
3885
 
    * @description Determines whether or not to load data at instantiation. By
3886
 
    * default, will trigger a sendRequest() to the DataSource and pass in the
3887
 
    * request defined by initialRequest. If set to false, data will not load
3888
 
    * at instantiation. Alternatively, implementers who wish to work with a 
3889
 
    * custom payload may pass in an object literal with the following values:
3890
 
    *     
3891
 
    *    <dl>
3892
 
    *      <dt>request (MIXED)</dt>
3893
 
    *      <dd>Request value.</dd>
3894
 
    *
3895
 
    *      <dt>argument (MIXED)</dt>
3896
 
    *      <dd>Custom data that will be passed through to the callback function.</dd>
3897
 
    *    </dl>
3898
 
    *
3899
 
    *                    
3900
 
    * @type Boolean | Object
3901
 
    * @default true
3902
 
    */
3903
 
    this.setAttributeConfig("initialLoad", {
3904
 
        value: true
3905
 
    });
3906
 
    
3907
 
    /**
3908
 
    * @attribute dynamicData
3909
 
    * @description If true, sorting and pagination are relegated to the DataSource
3910
 
    * for handling, using the request returned by the "generateRequest" function.
3911
 
    * Each new DataSource response blows away all previous Records. False by default, so 
3912
 
    * sorting and pagination will be handled directly on the client side, without
3913
 
    * causing any new requests for data from the DataSource.
3914
 
    * @type Boolean
3915
 
    * @default false
3916
 
    */
3917
 
    this.setAttributeConfig("dynamicData", {
3918
 
        value: false,
3919
 
        validator: lang.isBoolean
3920
 
    });
3921
 
 
3922
 
    /**
3923
 
     * @attribute MSG_EMPTY      
3924
 
     * @description Message to display if DataTable has no data.     
3925
 
     * @type String      
3926
 
     * @default "No records found."      
3927
 
     */          
3928
 
     this.setAttributeConfig("MSG_EMPTY", {      
3929
 
         value: "No records found.",     
3930
 
         validator: lang.isString        
3931
 
     });         
3932
 
 
3933
 
    /**
3934
 
     * @attribute MSG_LOADING    
3935
 
     * @description Message to display while DataTable is loading data.
3936
 
     * @type String      
3937
 
     * @default "Loading..."     
3938
 
     */          
3939
 
     this.setAttributeConfig("MSG_LOADING", {    
3940
 
         value: "Loading...",    
3941
 
         validator: lang.isString        
3942
 
     });         
3943
 
 
3944
 
    /**
3945
 
     * @attribute MSG_ERROR      
3946
 
     * @description Message to display while DataTable has data error.
3947
 
     * @type String      
3948
 
     * @default "Data error."    
3949
 
     */          
3950
 
     this.setAttributeConfig("MSG_ERROR", {      
3951
 
         value: "Data error.",   
3952
 
         validator: lang.isString        
3953
 
     });         
3954
 
 
3955
 
    /**
3956
 
     * @attribute MSG_SORTASC 
3957
 
     * @description Message to display in tooltip to sort Column in ascending order.
3958
 
     * @type String      
3959
 
     * @default "Click to sort ascending"        
3960
 
     */          
3961
 
     this.setAttributeConfig("MSG_SORTASC", {    
3962
 
         value: "Click to sort ascending",       
3963
 
         validator: lang.isString,
3964
 
         method: function(sParam) {
3965
 
            if(this._elThead) {
3966
 
                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
3967
 
                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_ASC) {
3968
 
                        allKeys[i]._elThLabel.firstChild.title = sParam;
3969
 
                    }
3970
 
                }
3971
 
            }      
3972
 
         }
3973
 
     });
3974
 
 
3975
 
    /**
3976
 
     * @attribute MSG_SORTDESC 
3977
 
     * @description Message to display in tooltip to sort Column in descending order.
3978
 
     * @type String      
3979
 
     * @default "Click to sort descending"       
3980
 
     */          
3981
 
     this.setAttributeConfig("MSG_SORTDESC", {   
3982
 
         value: "Click to sort descending",      
3983
 
         validator: lang.isString,
3984
 
         method: function(sParam) {
3985
 
            if(this._elThead) {
3986
 
                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
3987
 
                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_DESC) {
3988
 
                        allKeys[i]._elThLabel.firstChild.title = sParam;
3989
 
                    }
3990
 
                }
3991
 
            }               
3992
 
         }
3993
 
     });
3994
 
     
3995
 
    /**
3996
 
     * @attribute currencySymbol
3997
 
     * @deprecated
3998
 
     */
3999
 
    this.setAttributeConfig("currencySymbol", {
4000
 
        value: "$",
4001
 
        validator: lang.isString
4002
 
    });
4003
 
    
4004
 
    /**
4005
 
     * Default config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
4006
 
     * @attribute currencyOptions
4007
 
     * @type Object
4008
 
     * @default {prefix: $, decimalPlaces:2, decimalSeparator:".", thousandsSeparator:","}
4009
 
     */
4010
 
    this.setAttributeConfig("currencyOptions", {
4011
 
        value: {
4012
 
            prefix: this.get("currencySymbol"), // TODO: deprecate currencySymbol
4013
 
            decimalPlaces:2,
4014
 
            decimalSeparator:".",
4015
 
            thousandsSeparator:","
4016
 
        }
4017
 
    });
4018
 
    
4019
 
    /**
4020
 
     * Default config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
4021
 
     * @attribute dateOptions
4022
 
     * @type Object
4023
 
     * @default {format:"%m/%d/%Y", locale:"en"}
4024
 
     */
4025
 
    this.setAttributeConfig("dateOptions", {
4026
 
        value: {format:"%m/%d/%Y", locale:"en"}
4027
 
    });
4028
 
    
4029
 
    /**
4030
 
     * Default config passed to YAHOO.util.Number.format() by the 'number' Column formatter.
4031
 
     * @attribute numberOptions
4032
 
     * @type Object
4033
 
     * @default {decimalPlaces:0, thousandsSeparator:","}
4034
 
     */
4035
 
    this.setAttributeConfig("numberOptions", {
4036
 
        value: {
4037
 
            decimalPlaces:0,
4038
 
            thousandsSeparator:","
4039
 
        }
4040
 
    });
4041
 
 
4042
 
},
4043
 
 
4044
 
/////////////////////////////////////////////////////////////////////////////
4045
 
//
4046
 
// Private member variables
4047
 
//
4048
 
/////////////////////////////////////////////////////////////////////////////
4049
 
 
4050
 
/**
4051
 
 * True if instance is initialized, so as to fire the initEvent after render.
4052
 
 *
4053
 
 * @property _bInit
4054
 
 * @type Boolean
4055
 
 * @default true
4056
 
 * @private
4057
 
 */
4058
 
_bInit : true,
4059
 
 
4060
 
/**
4061
 
 * Index assigned to instance.
4062
 
 *
4063
 
 * @property _nIndex
4064
 
 * @type Number
4065
 
 * @private
4066
 
 */
4067
 
_nIndex : null,
4068
 
 
4069
 
/**
4070
 
 * Counter for IDs assigned to TR elements.
4071
 
 *
4072
 
 * @property _nTrCount
4073
 
 * @type Number
4074
 
 * @private
4075
 
 */
4076
 
_nTrCount : 0,
4077
 
 
4078
 
/**
4079
 
 * Counter for IDs assigned to TD elements.
4080
 
 *
4081
 
 * @property _nTdCount
4082
 
 * @type Number
4083
 
 * @private
4084
 
 */
4085
 
_nTdCount : 0,
4086
 
 
4087
 
/**
4088
 
 * Unique id assigned to instance "yui-dtN", useful prefix for generating unique
4089
 
 * DOM ID strings and log messages.
4090
 
 *
4091
 
 * @property _sId
4092
 
 * @type String
4093
 
 * @private
4094
 
 */
4095
 
_sId : null,
4096
 
 
4097
 
/**
4098
 
 * Render chain.
4099
 
 *
4100
 
 * @property _oChainRender
4101
 
 * @type YAHOO.util.Chain
4102
 
 * @private
4103
 
 */
4104
 
_oChainRender : null,
4105
 
 
4106
 
/**
4107
 
 * DOM reference to the container element for the DataTable instance into which
4108
 
 * all other elements get created.
4109
 
 *
4110
 
 * @property _elContainer
4111
 
 * @type HTMLElement
4112
 
 * @private
4113
 
 */
4114
 
_elContainer : null,
4115
 
 
4116
 
/**
4117
 
 * DOM reference to the mask element for the DataTable instance which disables it.
4118
 
 *
4119
 
 * @property _elMask
4120
 
 * @type HTMLElement
4121
 
 * @private
4122
 
 */
4123
 
_elMask : null,
4124
 
 
4125
 
/**
4126
 
 * DOM reference to the TABLE element for the DataTable instance.
4127
 
 *
4128
 
 * @property _elTable
4129
 
 * @type HTMLElement
4130
 
 * @private
4131
 
 */
4132
 
_elTable : null,
4133
 
 
4134
 
/**
4135
 
 * DOM reference to the CAPTION element for the DataTable instance.
4136
 
 *
4137
 
 * @property _elCaption
4138
 
 * @type HTMLElement
4139
 
 * @private
4140
 
 */
4141
 
_elCaption : null,
4142
 
 
4143
 
/**
4144
 
 * DOM reference to the COLGROUP element for the DataTable instance.
4145
 
 *
4146
 
 * @property _elColgroup
4147
 
 * @type HTMLElement
4148
 
 * @private
4149
 
 */
4150
 
_elColgroup : null,
4151
 
 
4152
 
/**
4153
 
 * DOM reference to the THEAD element for the DataTable instance.
4154
 
 *
4155
 
 * @property _elThead
4156
 
 * @type HTMLElement
4157
 
 * @private
4158
 
 */
4159
 
_elThead : null,
4160
 
 
4161
 
/**
4162
 
 * DOM reference to the primary TBODY element for the DataTable instance.
4163
 
 *
4164
 
 * @property _elTbody
4165
 
 * @type HTMLElement
4166
 
 * @private
4167
 
 */
4168
 
_elTbody : null,
4169
 
 
4170
 
/**
4171
 
 * DOM reference to the secondary TBODY element used to display DataTable messages.
4172
 
 *
4173
 
 * @property _elMsgTbody
4174
 
 * @type HTMLElement
4175
 
 * @private
4176
 
 */
4177
 
_elMsgTbody : null,
4178
 
 
4179
 
/**
4180
 
 * DOM reference to the secondary TBODY element's single TR element used to display DataTable messages.
4181
 
 *
4182
 
 * @property _elMsgTr
4183
 
 * @type HTMLElement
4184
 
 * @private
4185
 
 */
4186
 
_elMsgTr : null,
4187
 
 
4188
 
/**
4189
 
 * DOM reference to the secondary TBODY element's single TD element used to display DataTable messages.
4190
 
 *
4191
 
 * @property _elMsgTd
4192
 
 * @type HTMLElement
4193
 
 * @private
4194
 
 */
4195
 
_elMsgTd : null,
4196
 
 
4197
 
/**
4198
 
 * DataSource instance for the DataTable instance.
4199
 
 *
4200
 
 * @property _oDataSource
4201
 
 * @type YAHOO.util.DataSource
4202
 
 * @private
4203
 
 */
4204
 
_oDataSource : null,
4205
 
 
4206
 
/**
4207
 
 * ColumnSet instance for the DataTable instance.
4208
 
 *
4209
 
 * @property _oColumnSet
4210
 
 * @type YAHOO.widget.ColumnSet
4211
 
 * @private
4212
 
 */
4213
 
_oColumnSet : null,
4214
 
 
4215
 
/**
4216
 
 * RecordSet instance for the DataTable instance.
4217
 
 *
4218
 
 * @property _oRecordSet
4219
 
 * @type YAHOO.widget.RecordSet
4220
 
 * @private
4221
 
 */
4222
 
_oRecordSet : null,
4223
 
 
4224
 
/**
4225
 
 * The active CellEditor instance for the DataTable instance.
4226
 
 *
4227
 
 * @property _oCellEditor
4228
 
 * @type YAHOO.widget.CellEditor
4229
 
 * @private
4230
 
 */
4231
 
_oCellEditor : null,
4232
 
 
4233
 
/**
4234
 
 * ID string of first TR element of the current DataTable page.
4235
 
 *
4236
 
 * @property _sFirstTrId
4237
 
 * @type String
4238
 
 * @private
4239
 
 */
4240
 
_sFirstTrId : null,
4241
 
 
4242
 
/**
4243
 
 * ID string of the last TR element of the current DataTable page.
4244
 
 *
4245
 
 * @property _sLastTrId
4246
 
 * @type String
4247
 
 * @private
4248
 
 */
4249
 
_sLastTrId : null,
4250
 
 
4251
 
/**
4252
 
 * Template row to create all new rows from.
4253
 
 * @property _elTrTemplate
4254
 
 * @type {HTMLElement}
4255
 
 * @private 
4256
 
 */
4257
 
_elTrTemplate : null,
4258
 
 
4259
 
/**
4260
 
 * Sparse array of custom functions to set column widths for browsers that don't
4261
 
 * support dynamic CSS rules.  Functions are added at the index representing
4262
 
 * the number of rows they update.
4263
 
 *
4264
 
 * @property _aDynFunctions
4265
 
 * @type Array
4266
 
 * @private
4267
 
 */
4268
 
_aDynFunctions : [],
4269
 
 
4270
 
 
4271
 
 
4272
 
 
4273
 
 
4274
 
 
4275
 
 
4276
 
 
4277
 
 
4278
 
 
4279
 
 
4280
 
 
4281
 
 
4282
 
 
4283
 
 
4284
 
 
4285
 
 
4286
 
 
4287
 
 
4288
 
 
4289
 
 
4290
 
 
4291
 
 
4292
 
 
4293
 
 
4294
 
 
4295
 
 
4296
 
 
4297
 
 
4298
 
/////////////////////////////////////////////////////////////////////////////
4299
 
//
4300
 
// Private methods
4301
 
//
4302
 
/////////////////////////////////////////////////////////////////////////////
4303
 
 
4304
 
/**
4305
 
 * Clears browser text selection. Useful to call on rowSelectEvent or
4306
 
 * cellSelectEvent to prevent clicks or dblclicks from selecting text in the
4307
 
 * browser.
4308
 
 *
4309
 
 * @method clearTextSelection
4310
 
 */
4311
 
clearTextSelection : function() {
4312
 
    var sel;
4313
 
    if(window.getSelection) {
4314
 
        sel = window.getSelection();
4315
 
    }
4316
 
    else if(document.getSelection) {
4317
 
        sel = document.getSelection();
4318
 
    }
4319
 
    else if(document.selection) {
4320
 
        sel = document.selection;
4321
 
    }
4322
 
    if(sel) {
4323
 
        if(sel.empty) {
4324
 
            sel.empty();
4325
 
        }
4326
 
        else if (sel.removeAllRanges) {
4327
 
            sel.removeAllRanges();
4328
 
        }
4329
 
        else if(sel.collapse) {
4330
 
            sel.collapse();
4331
 
        }
4332
 
    }
4333
 
},
4334
 
 
4335
 
/**
4336
 
 * Sets focus on the given element.
4337
 
 *
4338
 
 * @method _focusEl
4339
 
 * @param el {HTMLElement} Element.
4340
 
 * @private
4341
 
 */
4342
 
_focusEl : function(el) {
4343
 
    el = el || this._elTbody;
4344
 
    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
4345
 
    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
4346
 
    // strange unexpected things as the user clicks on buttons and other controls.
4347
 
    setTimeout(function() {
4348
 
        try {
4349
 
            el.focus();
4350
 
        }
4351
 
        catch(e) {
4352
 
        }
4353
 
    },0);
4354
 
},
4355
 
 
4356
 
/**
4357
 
 * Forces Gecko repaint.
4358
 
 *
4359
 
 * @method _repaintGecko
4360
 
 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
4361
 
 * @private
4362
 
 */
4363
 
_repaintGecko : (ua.gecko) ? 
4364
 
    function(el) {
4365
 
        el = el || this._elContainer;
4366
 
        var parent = el.parentNode;
4367
 
        var nextSibling = el.nextSibling;
4368
 
        parent.insertBefore(parent.removeChild(el), nextSibling);
4369
 
    } : function() {},
4370
 
 
4371
 
/**
4372
 
 * Forces Opera repaint.
4373
 
 *
4374
 
 * @method _repaintOpera
4375
 
 * @private 
4376
 
 */
4377
 
_repaintOpera : (ua.opera) ? 
4378
 
    function() {
4379
 
        if(ua.opera) {
4380
 
            document.documentElement.className += " ";
4381
 
            document.documentElement.className.trim();
4382
 
        }
4383
 
    } : function() {} ,
4384
 
 
4385
 
/**
4386
 
 * Forces Webkit repaint.
4387
 
 *
4388
 
 * @method _repaintWebkit
4389
 
 * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
4390
 
 * @private
4391
 
 */
4392
 
_repaintWebkit : (ua.webkit) ? 
4393
 
    function(el) {
4394
 
        el = el || this._elContainer;
4395
 
        var parent = el.parentNode;
4396
 
        var nextSibling = el.nextSibling;
4397
 
        parent.insertBefore(parent.removeChild(el), nextSibling);
4398
 
    } : function() {},
4399
 
 
4400
 
 
4401
 
 
4402
 
 
4403
 
 
4404
 
 
4405
 
 
4406
 
 
4407
 
 
4408
 
 
4409
 
 
4410
 
 
4411
 
 
4412
 
 
4413
 
 
4414
 
 
4415
 
 
4416
 
 
4417
 
 
4418
 
 
4419
 
 
4420
 
 
4421
 
// INIT FUNCTIONS
4422
 
 
4423
 
/**
4424
 
 * Initializes object literal of config values.
4425
 
 *
4426
 
 * @method _initConfigs
4427
 
 * @param oConfig {Object} Object literal of config values.
4428
 
 * @private
4429
 
 */
4430
 
_initConfigs : function(oConfigs) {
4431
 
    if(!oConfigs || !lang.isObject(oConfigs)) {
4432
 
        oConfigs = {};
4433
 
    }
4434
 
    this.configs = oConfigs;
4435
 
},
4436
 
 
4437
 
/**
4438
 
 * Initializes ColumnSet.
4439
 
 *
4440
 
 * @method _initColumnSet
4441
 
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
4442
 
 * @private
4443
 
 */
4444
 
_initColumnSet : function(aColumnDefs) {
4445
 
    var oColumn, i, len;
4446
 
    
4447
 
    if(this._oColumnSet) {
4448
 
        // First clear _oDynStyles for existing ColumnSet and
4449
 
        // uregister CellEditor Custom Events
4450
 
        for(i=0, len=this._oColumnSet.keys.length; i<len; i++) {
4451
 
            oColumn = this._oColumnSet.keys[i];
4452
 
            DT._oDynStyles["."+this.getId()+"-col-"+oColumn.getSanitizedKey()+" ."+DT.CLASS_LINER] = undefined;
4453
 
            if(oColumn.editor && oColumn.editor.unsubscribeAll) { // Backward compatibility
4454
 
                oColumn.editor.unsubscribeAll();
4455
 
            }
4456
 
        }
4457
 
        
4458
 
        this._oColumnSet = null;
4459
 
        this._clearTrTemplateEl();
4460
 
    }
4461
 
    
4462
 
    if(lang.isArray(aColumnDefs)) {
4463
 
        this._oColumnSet =  new YAHOO.widget.ColumnSet(aColumnDefs);
4464
 
    }
4465
 
    // Backward compatibility
4466
 
    else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) {
4467
 
        this._oColumnSet =  aColumnDefs;
4468
 
        YAHOO.log("DataTable's constructor now requires an array" +
4469
 
        " of object literal Column definitions instead of a ColumnSet instance",
4470
 
        "warn", this.toString());
4471
 
    }
4472
 
 
4473
 
    // Register CellEditor Custom Events
4474
 
    var allKeys = this._oColumnSet.keys;
4475
 
    for(i=0, len=allKeys.length; i<len; i++) {
4476
 
        oColumn = allKeys[i];
4477
 
        if(oColumn.editor && oColumn.editor.subscribe) { // Backward incompatibility
4478
 
            oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
4479
 
            oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
4480
 
            oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
4481
 
            oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
4482
 
            oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
4483
 
            oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
4484
 
            oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
4485
 
            oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
4486
 
        }
4487
 
    }
4488
 
},
4489
 
 
4490
 
/**
4491
 
 * Initializes DataSource.
4492
 
 *
4493
 
 * @method _initDataSource
4494
 
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
4495
 
 * @private
4496
 
 */
4497
 
_initDataSource : function(oDataSource) {
4498
 
    this._oDataSource = null;
4499
 
    if(oDataSource && (oDataSource instanceof DS)) {
4500
 
        this._oDataSource = oDataSource;
4501
 
    }
4502
 
    // Backward compatibility
4503
 
    else {
4504
 
        var tmpTable = null;
4505
 
        var tmpContainer = this._elContainer;
4506
 
        var i=0;
4507
 
        //TODO: this will break if re-initing DS at runtime for SDT
4508
 
        // Peek in container child nodes to see if TABLE already exists
4509
 
        if(tmpContainer.hasChildNodes()) {
4510
 
            var tmpChildren = tmpContainer.childNodes;
4511
 
            for(i=0; i<tmpChildren.length; i++) {
4512
 
                if(tmpChildren[i].nodeName && tmpChildren[i].nodeName.toLowerCase() == "table") {
4513
 
                    tmpTable = tmpChildren[i];
4514
 
                    break;
4515
 
                }
4516
 
            }
4517
 
            if(tmpTable) {
4518
 
                var tmpFieldsArray = [];
4519
 
                for(; i<this._oColumnSet.keys.length; i++) {
4520
 
                    tmpFieldsArray.push({key:this._oColumnSet.keys[i].key});
4521
 
                }
4522
 
 
4523
 
                this._oDataSource = new DS(tmpTable);
4524
 
                this._oDataSource.responseType = DS.TYPE_HTMLTABLE;
4525
 
                this._oDataSource.responseSchema = {fields: tmpFieldsArray};
4526
 
                YAHOO.log("Null DataSource for progressive enhancement from" +
4527
 
                " markup has been deprecated", "warn", this.toString());
4528
 
            }
4529
 
        }
4530
 
    }
4531
 
},
4532
 
 
4533
 
/**
4534
 
 * Initializes RecordSet.
4535
 
 *
4536
 
 * @method _initRecordSet
4537
 
 * @private
4538
 
 */
4539
 
_initRecordSet : function() {
4540
 
    if(this._oRecordSet) {
4541
 
        this._oRecordSet.reset();
4542
 
    }
4543
 
    else {
4544
 
        this._oRecordSet = new YAHOO.widget.RecordSet();
4545
 
    }
4546
 
},
4547
 
 
4548
 
/**
4549
 
 * Initializes DOM elements.
4550
 
 *
4551
 
 * @method _initDomElements
4552
 
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 
4553
 
 * return {Boolean} False in case of error, otherwise true 
4554
 
 * @private
4555
 
 */
4556
 
_initDomElements : function(elContainer) {
4557
 
    // Outer container
4558
 
    this._initContainerEl(elContainer);
4559
 
    // TABLE
4560
 
    this._initTableEl(this._elContainer);
4561
 
    // COLGROUP
4562
 
    this._initColgroupEl(this._elTable);
4563
 
    // THEAD
4564
 
    this._initTheadEl(this._elTable);
4565
 
    
4566
 
    // Message TBODY
4567
 
    this._initMsgTbodyEl(this._elTable);  
4568
 
 
4569
 
    // Primary TBODY
4570
 
    this._initTbodyEl(this._elTable);
4571
 
 
4572
 
    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody) {
4573
 
        YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
4574
 
        return false;
4575
 
    }
4576
 
    else {
4577
 
        return true;
4578
 
    }
4579
 
},
4580
 
 
4581
 
/**
4582
 
 * Destroy's the DataTable outer container element, if available.
4583
 
 *
4584
 
 * @method _destroyContainerEl
4585
 
 * @param elContainer {HTMLElement} Reference to the container element. 
4586
 
 * @private
4587
 
 */
4588
 
_destroyContainerEl : function(elContainer) {
4589
 
    Dom.removeClass(elContainer, DT.CLASS_DATATABLE);
4590
 
    Ev.purgeElement(elContainer, true);
4591
 
    elContainer.innerHTML = "";
4592
 
    
4593
 
    this._elContainer = null;
4594
 
    this._elColgroup = null;
4595
 
    this._elThead = null;
4596
 
    this._elTbody = null;
4597
 
},
4598
 
 
4599
 
/**
4600
 
 * Initializes the DataTable outer container element, including a mask.
4601
 
 *
4602
 
 * @method _initContainerEl
4603
 
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
4604
 
 * @private
4605
 
 */
4606
 
_initContainerEl : function(elContainer) {
4607
 
    // Validate container
4608
 
    elContainer = Dom.get(elContainer);
4609
 
    
4610
 
    if(elContainer && elContainer.nodeName && (elContainer.nodeName.toLowerCase() == "div")) {
4611
 
        // Destroy previous
4612
 
        this._destroyContainerEl(elContainer);
4613
 
 
4614
 
        Dom.addClass(elContainer, DT.CLASS_DATATABLE);
4615
 
        Ev.addListener(elContainer, "focus", this._onTableFocus, this);
4616
 
        Ev.addListener(elContainer, "dblclick", this._onTableDblclick, this);
4617
 
        this._elContainer = elContainer;
4618
 
        
4619
 
        var elMask = document.createElement("div");
4620
 
        elMask.className = DT.CLASS_MASK;
4621
 
        elMask.style.display = "none";
4622
 
        this._elMask = elContainer.appendChild(elMask);
4623
 
    }
4624
 
},
4625
 
 
4626
 
/**
4627
 
 * Destroy's the DataTable TABLE element, if available.
4628
 
 *
4629
 
 * @method _destroyTableEl
4630
 
 * @private
4631
 
 */
4632
 
_destroyTableEl : function() {
4633
 
    var elTable = this._elTable;
4634
 
    if(elTable) {
4635
 
        Ev.purgeElement(elTable, true);
4636
 
        elTable.parentNode.removeChild(elTable);
4637
 
        this._elCaption = null;
4638
 
        this._elColgroup = null;
4639
 
        this._elThead = null;
4640
 
        this._elTbody = null;
4641
 
    }
4642
 
},
4643
 
 
4644
 
/**
4645
 
 * Creates HTML markup CAPTION element.
4646
 
 *
4647
 
 * @method _initCaptionEl
4648
 
 * @param sCaption {String} Text for caption.
4649
 
 * @private
4650
 
 */
4651
 
_initCaptionEl : function(sCaption) {
4652
 
    if(this._elTable && sCaption) {
4653
 
        // Create CAPTION element
4654
 
        if(!this._elCaption) { 
4655
 
            this._elCaption = this._elTable.createCaption();
4656
 
        }
4657
 
        // Set CAPTION value
4658
 
        this._elCaption.innerHTML = sCaption;
4659
 
    }
4660
 
    else if(this._elCaption) {
4661
 
        this._elCaption.parentNode.removeChild(this._elCaption);
4662
 
    }
4663
 
},
4664
 
 
4665
 
/**
4666
 
 * Creates HTML markup for TABLE, COLGROUP, THEAD and TBODY elements in outer
4667
 
 * container element.
4668
 
 *
4669
 
 * @method _initTableEl
4670
 
 * @param elContainer {HTMLElement} Container element into which to create TABLE.
4671
 
 * @private
4672
 
 */
4673
 
_initTableEl : function(elContainer) {
4674
 
    if(elContainer) {
4675
 
        // Destroy previous
4676
 
        this._destroyTableEl();
4677
 
    
4678
 
        // Create TABLE
4679
 
        this._elTable = elContainer.appendChild(document.createElement("table"));  
4680
 
         
4681
 
        // Set SUMMARY attribute
4682
 
        this._elTable.summary = this.get("summary");
4683
 
        
4684
 
        // Create CAPTION element
4685
 
        if(this.get("caption")) {
4686
 
            this._initCaptionEl(this.get("caption"));
4687
 
        }
4688
 
    } 
4689
 
},
4690
 
 
4691
 
/**
4692
 
 * Destroy's the DataTable COLGROUP element, if available.
4693
 
 *
4694
 
 * @method _destroyColgroupEl
4695
 
 * @private
4696
 
 */
4697
 
_destroyColgroupEl : function() {
4698
 
    var elColgroup = this._elColgroup;
4699
 
    if(elColgroup) {
4700
 
        var elTable = elColgroup.parentNode;
4701
 
        Ev.purgeElement(elColgroup, true);
4702
 
        elTable.removeChild(elColgroup);
4703
 
        this._elColgroup = null;
4704
 
    }
4705
 
},
4706
 
 
4707
 
/**
4708
 
 * Initializes COLGROUP and COL elements for managing minWidth.
4709
 
 *
4710
 
 * @method _initColgroupEl
4711
 
 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
4712
 
 * @private
4713
 
 */
4714
 
_initColgroupEl : function(elTable) {
4715
 
    if(elTable) {
4716
 
        // Destroy previous
4717
 
        this._destroyColgroupEl();
4718
 
 
4719
 
        // Add COLs to DOCUMENT FRAGMENT
4720
 
        var allCols = this._aColIds || [],
4721
 
            allKeys = this._oColumnSet.keys,
4722
 
            i = 0, len = allCols.length,
4723
 
            elCol, oColumn,
4724
 
            elFragment = document.createDocumentFragment(),
4725
 
            elColTemplate = document.createElement("col");
4726
 
    
4727
 
        for(i=0,len=allKeys.length; i<len; i++) {
4728
 
            oColumn = allKeys[i];
4729
 
            elCol = elFragment.appendChild(elColTemplate.cloneNode(false));
4730
 
        }
4731
 
    
4732
 
        // Create COLGROUP
4733
 
        var elColgroup = elTable.insertBefore(document.createElement("colgroup"), elTable.firstChild);
4734
 
        elColgroup.appendChild(elFragment);
4735
 
        this._elColgroup = elColgroup;
4736
 
    }
4737
 
},
4738
 
 
4739
 
/**
4740
 
 * Adds a COL element to COLGROUP at given index.
4741
 
 *
4742
 
 * @method _insertColgroupColEl
4743
 
 * @param index {Number} Index of new COL element.
4744
 
 * @private
4745
 
 */
4746
 
_insertColgroupColEl : function(index) {
4747
 
    if(lang.isNumber(index)&& this._elColgroup) {
4748
 
        var nextSibling = this._elColgroup.childNodes[index] || null;
4749
 
        this._elColgroup.insertBefore(document.createElement("col"), nextSibling);
4750
 
    }
4751
 
},
4752
 
 
4753
 
/**
4754
 
 * Removes a COL element to COLGROUP at given index.
4755
 
 *
4756
 
 * @method _removeColgroupColEl
4757
 
 * @param index {Number} Index of removed COL element.
4758
 
 * @private
4759
 
 */
4760
 
_removeColgroupColEl : function(index) {
4761
 
    if(lang.isNumber(index) && this._elColgroup && this._elColgroup.childNodes[index]) {
4762
 
        this._elColgroup.removeChild(this._elColgroup.childNodes[index]);
4763
 
    }
4764
 
},
4765
 
 
4766
 
/**
4767
 
 * Reorders a COL element from old index(es) to new index.
4768
 
 *
4769
 
 * @method _reorderColgroupColEl
4770
 
 * @param aKeyIndexes {Number[]} Array of indexes of removed COL element.
4771
 
 * @param newIndex {Number} New index. 
4772
 
 * @private
4773
 
 */
4774
 
_reorderColgroupColEl : function(aKeyIndexes, newIndex) {
4775
 
    if(lang.isArray(aKeyIndexes) && lang.isNumber(newIndex) && this._elColgroup && (this._elColgroup.childNodes.length > aKeyIndexes[aKeyIndexes.length-1])) {
4776
 
        var i,
4777
 
            tmpCols = [];
4778
 
        // Remove COL
4779
 
        for(i=aKeyIndexes.length-1; i>-1; i--) {
4780
 
            tmpCols.push(this._elColgroup.removeChild(this._elColgroup.childNodes[aKeyIndexes[i]]));
4781
 
        }
4782
 
        // Insert COL
4783
 
        var nextSibling = this._elColgroup.childNodes[newIndex] || null;
4784
 
        for(i=tmpCols.length-1; i>-1; i--) {
4785
 
            this._elColgroup.insertBefore(tmpCols[i], nextSibling);
4786
 
        }
4787
 
    }
4788
 
},
4789
 
 
4790
 
/**
4791
 
 * Destroy's the DataTable THEAD element, if available.
4792
 
 *
4793
 
 * @method _destroyTheadEl
4794
 
 * @private
4795
 
 */
4796
 
_destroyTheadEl : function() {
4797
 
    var elThead = this._elThead;
4798
 
    if(elThead) {
4799
 
        var elTable = elThead.parentNode;
4800
 
        Ev.purgeElement(elThead, true);
4801
 
        this._destroyColumnHelpers();
4802
 
        elTable.removeChild(elThead);
4803
 
        this._elThead = null;
4804
 
    }
4805
 
},
4806
 
 
4807
 
/**
4808
 
 * Initializes THEAD element.
4809
 
 *
4810
 
 * @method _initTheadEl
4811
 
 * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
4812
 
 * @param {HTMLElement} Initialized THEAD element. 
4813
 
 * @private
4814
 
 */
4815
 
_initTheadEl : function(elTable) {
4816
 
    elTable = elTable || this._elTable;
4817
 
    
4818
 
    if(elTable) {
4819
 
        // Destroy previous
4820
 
        this._destroyTheadEl();
4821
 
    
4822
 
        //TODO: append to DOM later for performance
4823
 
        var elThead = (this._elColgroup) ?
4824
 
            elTable.insertBefore(document.createElement("thead"), this._elColgroup.nextSibling) :
4825
 
            elTable.appendChild(document.createElement("thead"));
4826
 
    
4827
 
        // Set up DOM events for THEAD
4828
 
        Ev.addListener(elThead, "focus", this._onTheadFocus, this);
4829
 
        Ev.addListener(elThead, "keydown", this._onTheadKeydown, this);
4830
 
        Ev.addListener(elThead, "mouseover", this._onTableMouseover, this);
4831
 
        Ev.addListener(elThead, "mouseout", this._onTableMouseout, this);
4832
 
        Ev.addListener(elThead, "mousedown", this._onTableMousedown, this);
4833
 
        Ev.addListener(elThead, "mouseup", this._onTableMouseup, this);
4834
 
        Ev.addListener(elThead, "click", this._onTheadClick, this);
4835
 
 
4836
 
        // Since we can't listen for click and dblclick on the same element...
4837
 
        // Attach separately to THEAD and TBODY
4838
 
        ///Ev.addListener(elThead, "dblclick", this._onTableDblclick, this);
4839
 
        
4840
 
       var oColumnSet = this._oColumnSet,
4841
 
            oColumn, i,j, l;
4842
 
        
4843
 
        // Add TRs to the THEAD
4844
 
        var colTree = oColumnSet.tree;
4845
 
        var elTh;
4846
 
        for(i=0; i<colTree.length; i++) {
4847
 
            var elTheadTr = elThead.appendChild(document.createElement("tr"));
4848
 
    
4849
 
            // ...and create TH cells
4850
 
            for(j=0; j<colTree[i].length; j++) {
4851
 
                oColumn = colTree[i][j];
4852
 
                elTh = elTheadTr.appendChild(document.createElement("th"));
4853
 
                this._initThEl(elTh,oColumn);
4854
 
            }
4855
 
    
4856
 
                // Set FIRST/LAST on THEAD rows
4857
 
                if(i === 0) {
4858
 
                    Dom.addClass(elTheadTr, DT.CLASS_FIRST);
4859
 
                }
4860
 
                if(i === (colTree.length-1)) {
4861
 
                    Dom.addClass(elTheadTr, DT.CLASS_LAST);
4862
 
                }
4863
 
 
4864
 
        }
4865
 
 
4866
 
        // Set FIRST/LAST on edge TH elements using the values in ColumnSet headers array
4867
 
        var aFirstHeaders = oColumnSet.headers[0] || [];
4868
 
        for(i=0; i<aFirstHeaders.length; i++) {
4869
 
            Dom.addClass(Dom.get(this.getId() +"-th-"+aFirstHeaders[i]), DT.CLASS_FIRST);
4870
 
        }
4871
 
        var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1] || [];
4872
 
        for(i=0; i<aLastHeaders.length; i++) {
4873
 
            Dom.addClass(Dom.get(this.getId() +"-th-"+aLastHeaders[i]), DT.CLASS_LAST);
4874
 
        }
4875
 
        
4876
 
        YAHOO.log("TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
4877
 
 
4878
 
        ///TODO: try _repaintGecko(this._elContainer) instead
4879
 
        // Bug 1806891
4880
 
        if(ua.webkit && ua.webkit < 420) {
4881
 
            var oSelf = this;
4882
 
            setTimeout(function() {
4883
 
                elThead.style.display = "";
4884
 
            },0);
4885
 
            elThead.style.display = 'none';
4886
 
        }
4887
 
        
4888
 
        this._elThead = elThead;
4889
 
        
4890
 
        // Column helpers needs _elThead to exist
4891
 
        this._initColumnHelpers();  
4892
 
    }
4893
 
},
4894
 
 
4895
 
/**
4896
 
 * Populates TH element as defined by Column.
4897
 
 *
4898
 
 * @method _initThEl
4899
 
 * @param elTh {HTMLElement} TH element reference.
4900
 
 * @param oColumn {YAHOO.widget.Column} Column object.
4901
 
 * @private
4902
 
 */
4903
 
_initThEl : function(elTh, oColumn) {
4904
 
    elTh.id = this.getId() + "-th-" + oColumn.getSanitizedKey(); // Needed for accessibility, getColumn by TH, and ColumnDD
4905
 
    elTh.innerHTML = "";
4906
 
    elTh.rowSpan = oColumn.getRowspan();
4907
 
    elTh.colSpan = oColumn.getColspan();
4908
 
    oColumn._elTh = elTh;
4909
 
    
4910
 
    var elThLiner = elTh.appendChild(document.createElement("div"));
4911
 
    elThLiner.id = elTh.id + "-liner"; // Needed for resizer
4912
 
    elThLiner.className = DT.CLASS_LINER;
4913
 
    oColumn._elThLiner = elThLiner;
4914
 
    
4915
 
    var elThLabel = elThLiner.appendChild(document.createElement("span"));
4916
 
    elThLabel.className = DT.CLASS_LABEL;    
4917
 
 
4918
 
    // Assign abbr attribute
4919
 
    if(oColumn.abbr) {
4920
 
        elTh.abbr = oColumn.abbr;
4921
 
    }
4922
 
    // Clear minWidth on hidden Columns
4923
 
    if(oColumn.hidden) {
4924
 
        this._clearMinWidth(oColumn);
4925
 
    }
4926
 
        
4927
 
    elTh.className = this._getColumnClassNames(oColumn);
4928
 
            
4929
 
    // Set Column width...
4930
 
    if(oColumn.width) {
4931
 
        // Validate minWidth
4932
 
        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
4933
 
                oColumn.minWidth : oColumn.width;
4934
 
        // ...for fallback cases
4935
 
        if(DT._bDynStylesFallback) {
4936
 
            elTh.firstChild.style.overflow = 'hidden';
4937
 
            elTh.firstChild.style.width = nWidth + 'px';        
4938
 
        }
4939
 
        // ...for non fallback cases
4940
 
        else {
4941
 
            this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden');
4942
 
        }
4943
 
    }
4944
 
 
4945
 
    this.formatTheadCell(elThLabel, oColumn, this.get("sortedBy"));
4946
 
    oColumn._elThLabel = elThLabel;
4947
 
},
4948
 
 
4949
 
/**
4950
 
 * Outputs markup into the given TH based on given Column.
4951
 
 *
4952
 
 * @method DataTable.formatTheadCell
4953
 
 * @param elCellLabel {HTMLElement} The label SPAN element within the TH liner,
4954
 
 * not the liner DIV element.     
4955
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
4956
 
 * @param oSortedBy {Object} Sort state object literal.
4957
 
*/
4958
 
formatTheadCell : function(elCellLabel, oColumn, oSortedBy) {
4959
 
    var sKey = oColumn.getKey();
4960
 
    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
4961
 
 
4962
 
    // Add accessibility link for sortable Columns
4963
 
    if(oColumn.sortable) {
4964
 
        // Calculate the direction
4965
 
        var sSortClass = this.getColumnSortDir(oColumn, oSortedBy);
4966
 
        var bDesc = (sSortClass === DT.CLASS_DESC);
4967
 
 
4968
 
        // This is the sorted Column
4969
 
        if(oSortedBy && (oColumn.key === oSortedBy.key)) {
4970
 
            bDesc = !(oSortedBy.dir === DT.CLASS_DESC);
4971
 
        }
4972
 
 
4973
 
        // Generate a unique HREF for visited status
4974
 
        var sHref = this.getId() + "-href-" + oColumn.getSanitizedKey();
4975
 
        
4976
 
        // Generate a dynamic TITLE for sort status
4977
 
        var sTitle = (bDesc) ? this.get("MSG_SORTDESC") : this.get("MSG_SORTASC");
4978
 
        
4979
 
        // Format the element
4980
 
        elCellLabel.innerHTML = "<a href=\"" + sHref + "\" title=\"" + sTitle + "\" class=\"" + DT.CLASS_SORTABLE + "\">" + sLabel + "</a>";
4981
 
    }
4982
 
    // Just display the label for non-sortable Columns
4983
 
    else {
4984
 
        elCellLabel.innerHTML = sLabel;
4985
 
    }
4986
 
},
4987
 
 
4988
 
/**
4989
 
 * Disables DD from top-level Column TH elements.
4990
 
 *
4991
 
 * @method _destroyDraggableColumns
4992
 
 * @private
4993
 
 */
4994
 
_destroyDraggableColumns : function() {
4995
 
    var oColumn, elTh;
4996
 
    for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
4997
 
        oColumn = this._oColumnSet.tree[0][i];
4998
 
        if(oColumn._dd) {
4999
 
            oColumn._dd = oColumn._dd.unreg();
5000
 
            Dom.removeClass(oColumn.getThEl(), DT.CLASS_DRAGGABLE);       
5001
 
        }
5002
 
    }
5003
 
},
5004
 
 
5005
 
/**
5006
 
 * Initializes top-level Column TH elements into DD instances.
5007
 
 *
5008
 
 * @method _initDraggableColumns
5009
 
 * @private
5010
 
 */
5011
 
_initDraggableColumns : function() {
5012
 
    this._destroyDraggableColumns();
5013
 
    if(util.DD) {
5014
 
        var oColumn, elTh, elDragTarget;
5015
 
        for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
5016
 
            oColumn = this._oColumnSet.tree[0][i];
5017
 
            elTh = oColumn.getThEl();
5018
 
            Dom.addClass(elTh, DT.CLASS_DRAGGABLE);
5019
 
            elDragTarget = DT._initColumnDragTargetEl();
5020
 
            oColumn._dd = new YAHOO.widget.ColumnDD(this, oColumn, elTh, elDragTarget);
5021
 
        }
5022
 
    }
5023
 
    else {
5024
 
        YAHOO.log("Could not find DragDrop for draggable Columns", "warn", this.toString());
5025
 
    }
5026
 
},
5027
 
 
5028
 
/**
5029
 
 * Disables resizeability on key Column TH elements.
5030
 
 *
5031
 
 * @method _destroyResizeableColumns
5032
 
 * @private
5033
 
 */
5034
 
_destroyResizeableColumns : function() {
5035
 
    var aKeys = this._oColumnSet.keys;
5036
 
    for(var i=0, len=aKeys.length; i<len; i++) {
5037
 
        if(aKeys[i]._ddResizer) {
5038
 
            aKeys[i]._ddResizer = aKeys[i]._ddResizer.unreg();
5039
 
            Dom.removeClass(aKeys[i].getThEl(), DT.CLASS_RESIZEABLE);
5040
 
        }
5041
 
    }
5042
 
},
5043
 
 
5044
 
/**
5045
 
 * Initializes resizeability on key Column TH elements.
5046
 
 *
5047
 
 * @method _initResizeableColumns
5048
 
 * @private
5049
 
 */
5050
 
_initResizeableColumns : function() {
5051
 
    this._destroyResizeableColumns();
5052
 
    if(util.DD) {
5053
 
        var oColumn, elTh, elThLiner, elThResizerLiner, elThResizer, elResizerProxy, cancelClick;
5054
 
        for(var i=0, len=this._oColumnSet.keys.length; i<len; i++) {
5055
 
            oColumn = this._oColumnSet.keys[i];
5056
 
            if(oColumn.resizeable) {
5057
 
                elTh = oColumn.getThEl();
5058
 
                Dom.addClass(elTh, DT.CLASS_RESIZEABLE);
5059
 
                elThLiner = oColumn.getThLinerEl();
5060
 
                
5061
 
                // Bug 1915349: So resizer is as tall as TH when rowspan > 1
5062
 
                // Create a separate resizer liner with position:relative
5063
 
                elThResizerLiner = elTh.appendChild(document.createElement("div"));
5064
 
                elThResizerLiner.className = DT.CLASS_RESIZERLINER;
5065
 
                
5066
 
                // Move TH contents into the new resizer liner
5067
 
                elThResizerLiner.appendChild(elThLiner);
5068
 
                
5069
 
                // Create the resizer
5070
 
                elThResizer = elThResizerLiner.appendChild(document.createElement("div"));
5071
 
                elThResizer.id = elTh.id + "-resizer"; // Needed for ColumnResizer
5072
 
                elThResizer.className = DT.CLASS_RESIZER;
5073
 
                oColumn._elResizer = elThResizer;
5074
 
 
5075
 
                // Create the resizer proxy, once globally
5076
 
                elResizerProxy = DT._initColumnResizerProxyEl();
5077
 
                oColumn._ddResizer = new YAHOO.util.ColumnResizer(
5078
 
                        this, oColumn, elTh, elThResizer, elResizerProxy);
5079
 
                cancelClick = function(e) {
5080
 
                    Ev.stopPropagation(e);
5081
 
                };
5082
 
                Ev.addListener(elThResizer,"click",cancelClick);
5083
 
            }
5084
 
        }
5085
 
    }
5086
 
    else {
5087
 
        YAHOO.log("Could not find DragDrop for resizeable Columns", "warn", this.toString());
5088
 
    }
5089
 
},
5090
 
 
5091
 
/**
5092
 
 * Destroys elements associated with Column functionality: ColumnDD and ColumnResizers.
5093
 
 *
5094
 
 * @method _destroyColumnHelpers
5095
 
 * @private
5096
 
 */
5097
 
_destroyColumnHelpers : function() {
5098
 
    this._destroyDraggableColumns();
5099
 
    this._destroyResizeableColumns();
5100
 
},
5101
 
 
5102
 
/**
5103
 
 * Initializes elements associated with Column functionality: ColumnDD and ColumnResizers.
5104
 
 *
5105
 
 * @method _initColumnHelpers
5106
 
 * @private
5107
 
 */
5108
 
_initColumnHelpers : function() {
5109
 
    if(this.get("draggableColumns")) {
5110
 
        this._initDraggableColumns();
5111
 
    }
5112
 
    this._initResizeableColumns();
5113
 
},
5114
 
 
5115
 
/**
5116
 
 * Destroy's the DataTable TBODY element, if available.
5117
 
 *
5118
 
 * @method _destroyTbodyEl
5119
 
 * @private
5120
 
 */
5121
 
_destroyTbodyEl : function() {
5122
 
    var elTbody = this._elTbody;
5123
 
    if(elTbody) {
5124
 
        var elTable = elTbody.parentNode;
5125
 
        Ev.purgeElement(elTbody, true);
5126
 
        elTable.removeChild(elTbody);
5127
 
        this._elTbody = null;
5128
 
    }
5129
 
},
5130
 
 
5131
 
/**
5132
 
 * Initializes TBODY element for data.
5133
 
 *
5134
 
 * @method _initTbodyEl
5135
 
 * @param elTable {HTMLElement} TABLE element into which to create TBODY .
5136
 
 * @private
5137
 
 */
5138
 
_initTbodyEl : function(elTable) {
5139
 
    if(elTable) {
5140
 
        // Destroy previous
5141
 
        this._destroyTbodyEl();
5142
 
        
5143
 
        // Create TBODY
5144
 
        var elTbody = elTable.appendChild(document.createElement("tbody"));
5145
 
        elTbody.tabIndex = 0;
5146
 
        elTbody.className = DT.CLASS_DATA;
5147
 
    
5148
 
        // Set up DOM events for TBODY
5149
 
        Ev.addListener(elTbody, "focus", this._onTbodyFocus, this);
5150
 
        Ev.addListener(elTbody, "mouseover", this._onTableMouseover, this);
5151
 
        Ev.addListener(elTbody, "mouseout", this._onTableMouseout, this);
5152
 
        Ev.addListener(elTbody, "mousedown", this._onTableMousedown, this);
5153
 
        Ev.addListener(elTbody, "mouseup", this._onTableMouseup, this);
5154
 
        Ev.addListener(elTbody, "keydown", this._onTbodyKeydown, this);
5155
 
        Ev.addListener(elTbody, "keypress", this._onTableKeypress, this);
5156
 
        Ev.addListener(elTbody, "click", this._onTbodyClick, this);
5157
 
        
5158
 
        // Since we can't listen for click and dblclick on the same element...
5159
 
        // Attach separately to THEAD and TBODY
5160
 
        ///Ev.addListener(elTbody, "dblclick", this._onTableDblclick, this);
5161
 
        
5162
 
    
5163
 
        // IE puts focus outline in the wrong place
5164
 
        if(ua.ie) {
5165
 
            elTbody.hideFocus=true;
5166
 
        }
5167
 
 
5168
 
        this._elTbody = elTbody;
5169
 
    }
5170
 
},
5171
 
 
5172
 
/**
5173
 
 * Destroy's the DataTable message TBODY element, if available.
5174
 
 *
5175
 
 * @method _destroyMsgTbodyEl
5176
 
 * @private
5177
 
 */
5178
 
_destroyMsgTbodyEl : function() {
5179
 
    var elMsgTbody = this._elMsgTbody;
5180
 
    if(elMsgTbody) {
5181
 
        var elTable = elMsgTbody.parentNode;
5182
 
        Ev.purgeElement(elMsgTbody, true);
5183
 
        elTable.removeChild(elMsgTbody);
5184
 
        this._elTbody = null;
5185
 
    }
5186
 
},
5187
 
 
5188
 
/**
5189
 
 * Initializes TBODY element for messaging.
5190
 
 *
5191
 
 * @method _initMsgTbodyEl
5192
 
 * @param elTable {HTMLElement} TABLE element into which to create TBODY 
5193
 
 * @private
5194
 
 */
5195
 
_initMsgTbodyEl : function(elTable) {
5196
 
    if(elTable) {
5197
 
        var elMsgTbody = document.createElement("tbody");
5198
 
        elMsgTbody.className = DT.CLASS_MESSAGE;
5199
 
        var elMsgTr = elMsgTbody.appendChild(document.createElement("tr"));
5200
 
        elMsgTr.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
5201
 
        this._elMsgTr = elMsgTr;
5202
 
        var elMsgTd = elMsgTr.appendChild(document.createElement("td"));
5203
 
        elMsgTd.colSpan = this._oColumnSet.keys.length || 1;
5204
 
        elMsgTd.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
5205
 
        this._elMsgTd = elMsgTd;
5206
 
        elMsgTbody = elTable.insertBefore(elMsgTbody, this._elTbody);
5207
 
        var elMsgLiner = elMsgTd.appendChild(document.createElement("div"));
5208
 
        elMsgLiner.className = DT.CLASS_LINER;
5209
 
        this._elMsgTbody = elMsgTbody;
5210
 
    }
5211
 
},
5212
 
 
5213
 
/**
5214
 
 * Initialize internal event listeners
5215
 
 *
5216
 
 * @method _initEvents
5217
 
 * @private
5218
 
 */
5219
 
_initEvents : function () {
5220
 
    // Initialize Column sort
5221
 
    this._initColumnSort();
5222
 
        
5223
 
    // Add the document level click listener
5224
 
    YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this);
5225
 
 
5226
 
    // Paginator integration
5227
 
    this.subscribe("paginatorChange",function () {
5228
 
        this._handlePaginatorChange.apply(this,arguments);
5229
 
    });
5230
 
 
5231
 
    this.subscribe("initEvent",function () {
5232
 
        this.renderPaginator();
5233
 
    });
5234
 
 
5235
 
    // Initialize CellEditor integration
5236
 
    this._initCellEditing();
5237
 
},
5238
 
 
5239
 
/**      
5240
 
  * Initializes Column sorting.          
5241
 
  *      
5242
 
  * @method _initColumnSort      
5243
 
  * @private     
5244
 
  */     
5245
 
_initColumnSort : function() {
5246
 
    this.subscribe("theadCellClickEvent", this.onEventSortColumn);       
5247
 
 
5248
 
    // Backward compatibility
5249
 
    var oSortedBy = this.get("sortedBy");
5250
 
    if(oSortedBy) {
5251
 
        if(oSortedBy.dir == "desc") {
5252
 
            this._configs.sortedBy.value.dir = DT.CLASS_DESC;
5253
 
        }
5254
 
        else if(oSortedBy.dir == "asc") {
5255
 
            this._configs.sortedBy.value.dir = DT.CLASS_ASC;
5256
 
        }
5257
 
    }
5258
 
},
5259
 
 
5260
 
/**      
5261
 
  * Initializes CellEditor integration.          
5262
 
  *      
5263
 
  * @method _initCellEditing     
5264
 
  * @private     
5265
 
  */     
5266
 
_initCellEditing : function() {
5267
 
    this.subscribe("editorBlurEvent",function () {
5268
 
        this.onEditorBlurEvent.apply(this,arguments);
5269
 
    });
5270
 
    this.subscribe("editorBlockEvent",function () {
5271
 
        this.onEditorBlockEvent.apply(this,arguments);
5272
 
    });
5273
 
    this.subscribe("editorUnblockEvent",function () {
5274
 
        this.onEditorUnblockEvent.apply(this,arguments);
5275
 
    });
5276
 
},
5277
 
 
5278
 
 
5279
 
 
5280
 
 
5281
 
 
5282
 
 
5283
 
 
5284
 
 
5285
 
 
5286
 
 
5287
 
 
5288
 
 
5289
 
 
5290
 
 
5291
 
 
5292
 
 
5293
 
 
5294
 
 
5295
 
 
5296
 
 
5297
 
 
5298
 
 
5299
 
 
5300
 
 
5301
 
 
5302
 
 
5303
 
 
5304
 
 
5305
 
 
5306
 
 
5307
 
 
5308
 
 
5309
 
 
5310
 
// DOM MUTATION FUNCTIONS
5311
 
 
5312
 
/**
5313
 
 * Retruns classnames to represent current Column states.
5314
 
 * @method _getColumnClassnames 
5315
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
5316
 
 * @param aAddClasses {String[]} An array of additional classnames to add to the
5317
 
 * return value.  
5318
 
 * @return {String} A String of classnames to be assigned to TH or TD elements
5319
 
 * for given Column.  
5320
 
 * @private 
5321
 
 */
5322
 
_getColumnClassNames : function (oColumn, aAddClasses) {
5323
 
    var allClasses;
5324
 
    
5325
 
    // Add CSS classes
5326
 
    if(lang.isString(oColumn.className)) {
5327
 
        // Single custom class
5328
 
        allClasses = [oColumn.className];
5329
 
    }
5330
 
    else if(lang.isArray(oColumn.className)) {
5331
 
        // Array of custom classes
5332
 
        allClasses = oColumn.className;
5333
 
    }
5334
 
    else {
5335
 
        // no custom classes
5336
 
        allClasses = [];
5337
 
    }
5338
 
    
5339
 
    // Hook for setting width with via dynamic style uses key since ID is too disposable
5340
 
    allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey();
5341
 
 
5342
 
    // Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
5343
 
    allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
5344
 
 
5345
 
    var isSortedBy = this.get("sortedBy") || {};
5346
 
    // Sorted
5347
 
    if(oColumn.key === isSortedBy.key) {
5348
 
        allClasses[allClasses.length] = isSortedBy.dir || '';
5349
 
    }
5350
 
    // Hidden
5351
 
    if(oColumn.hidden) {
5352
 
        allClasses[allClasses.length] = DT.CLASS_HIDDEN;
5353
 
    }
5354
 
    // Selected
5355
 
    if(oColumn.selected) {
5356
 
        allClasses[allClasses.length] = DT.CLASS_SELECTED;
5357
 
    }
5358
 
    // Sortable
5359
 
    if(oColumn.sortable) {
5360
 
        allClasses[allClasses.length] = DT.CLASS_SORTABLE;
5361
 
    }
5362
 
    // Resizeable
5363
 
    if(oColumn.resizeable) {
5364
 
        allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
5365
 
    }
5366
 
    // Editable
5367
 
    if(oColumn.editor) {
5368
 
        allClasses[allClasses.length] = DT.CLASS_EDITABLE;
5369
 
    }
5370
 
    
5371
 
    // Addtnl classes, including First/Last
5372
 
    if(aAddClasses) {
5373
 
        allClasses = allClasses.concat(aAddClasses);
5374
 
    }
5375
 
    
5376
 
    return allClasses.join(' ');  
5377
 
},
5378
 
 
5379
 
/**
5380
 
 * Clears TR element template in response to any Column state change.
5381
 
 * @method _clearTrTemplateEl
5382
 
 * @private 
5383
 
 */
5384
 
_clearTrTemplateEl : function () {
5385
 
    this._elTrTemplate = null;
5386
 
},
5387
 
 
5388
 
/**
5389
 
 * Returns a new TR element template with TD elements classed with current
5390
 
 * Column states.
5391
 
 * @method _getTrTemplateEl 
5392
 
 * @return {HTMLElement} A TR element to be cloned and added to the DOM.
5393
 
 * @private 
5394
 
 */
5395
 
_getTrTemplateEl : function (oRecord, index) {
5396
 
    // Template is already available
5397
 
    if(this._elTrTemplate) {
5398
 
        return this._elTrTemplate;
5399
 
    }
5400
 
    // Template needs to be created
5401
 
    else {
5402
 
        var d   = document,
5403
 
            tr  = d.createElement('tr'),
5404
 
            td  = d.createElement('td'),
5405
 
            div = d.createElement('div');
5406
 
    
5407
 
        // Append the liner element
5408
 
        td.appendChild(div);
5409
 
 
5410
 
        // Create TD elements into DOCUMENT FRAGMENT
5411
 
        var df = document.createDocumentFragment(),
5412
 
            allKeys = this._oColumnSet.keys,
5413
 
            elTd;
5414
 
 
5415
 
        // Set state for each TD;
5416
 
        var aAddClasses;
5417
 
        for(var i=0, keysLen=allKeys.length; i<keysLen; i++) {
5418
 
            // Clone the TD template
5419
 
            elTd = td.cloneNode(true);
5420
 
 
5421
 
            // Format the base TD
5422
 
            elTd = this._formatTdEl(allKeys[i], elTd, i, (i===keysLen-1));
5423
 
                        
5424
 
            df.appendChild(elTd);
5425
 
        }
5426
 
        tr.appendChild(df);
5427
 
        this._elTrTemplate = tr;
5428
 
        return tr;
5429
 
    }   
5430
 
},
5431
 
 
5432
 
/**
5433
 
 * Formats a basic TD element.
5434
 
 * @method _formatTdEl 
5435
 
 * @param oColumn {YAHOO.widget.Column} Associated Column instance. 
5436
 
 * @param elTd {HTMLElement} An unformatted TD element.
5437
 
 * @param index {Number} Column key index. 
5438
 
 * @param isLast {Boolean} True if Column is last key of the ColumnSet.
5439
 
 * @return {HTMLElement} A formatted TD element.
5440
 
 * @private 
5441
 
 */
5442
 
_formatTdEl : function (oColumn, elTd, index, isLast) {
5443
 
    var oColumnSet = this._oColumnSet;
5444
 
    
5445
 
    // Set the TD's accessibility headers
5446
 
    var allHeaders = oColumnSet.headers,
5447
 
        allColHeaders = allHeaders[index],
5448
 
        sTdHeaders = "",
5449
 
        sHeader;
5450
 
    for(var j=0, headersLen=allColHeaders.length; j < headersLen; j++) {
5451
 
        sHeader = this._sId + "-th-" + allColHeaders[j] + ' ';
5452
 
        sTdHeaders += sHeader;
5453
 
    }
5454
 
    elTd.headers = sTdHeaders;
5455
 
    
5456
 
    // Class the TD element
5457
 
    var aAddClasses = [];
5458
 
    if(index === 0) {
5459
 
        aAddClasses[aAddClasses.length] = DT.CLASS_FIRST;
5460
 
    }
5461
 
    if(isLast) {
5462
 
        aAddClasses[aAddClasses.length] = DT.CLASS_LAST;
5463
 
    }
5464
 
    elTd.className = this._getColumnClassNames(oColumn, aAddClasses);
5465
 
 
5466
 
    // Class the liner element
5467
 
    elTd.firstChild.className = DT.CLASS_LINER;
5468
 
 
5469
 
    // Set Column width for fallback cases
5470
 
    if(oColumn.width && DT._bDynStylesFallback) {
5471
 
        // Validate minWidth
5472
 
        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
5473
 
                oColumn.minWidth : oColumn.width;
5474
 
        elTd.firstChild.style.overflow = 'hidden';
5475
 
        elTd.firstChild.style.width = nWidth + 'px';
5476
 
    }
5477
 
    
5478
 
    return elTd;
5479
 
},
5480
 
 
5481
 
 
5482
 
/**
5483
 
 * Create a new TR element for a given Record and appends it with the correct
5484
 
 * number of Column-state-classed TD elements. Striping is the responsibility of
5485
 
 * the calling function, which may decide to stripe the single row, a subset of
5486
 
 * rows, or all the rows.
5487
 
 * @method _createTrEl
5488
 
 * @param oRecord {YAHOO.widget.Record} Record instance
5489
 
 * @return {HTMLElement} The new TR element.  This must be added to the DOM.
5490
 
 * @private 
5491
 
 */
5492
 
_addTrEl : function (oRecord) {
5493
 
    var elTrTemplate = this._getTrTemplateEl();
5494
 
    
5495
 
    // Clone the TR template.
5496
 
    var elTr = elTrTemplate.cloneNode(true);
5497
 
    
5498
 
    // Populate content
5499
 
    return this._updateTrEl(elTr,oRecord);
5500
 
},
5501
 
 
5502
 
/**
5503
 
 * Formats the contents of the given TR's TD elements with data from the given
5504
 
 * Record. Only innerHTML should change, nothing structural.
5505
 
 *
5506
 
 * @method _updateTrEl
5507
 
 * @param elTr {HTMLElement} The TR element to update.
5508
 
 * @param oRecord {YAHOO.widget.Record} The associated Record instance.
5509
 
 * @return {HTMLElement} DOM reference to the new TR element.
5510
 
 * @private
5511
 
 */
5512
 
_updateTrEl : function(elTr, oRecord) {
5513
 
    var ok = this.get("formatRow") ? this.get("formatRow").call(this, elTr, oRecord) : true;
5514
 
    if(ok) {
5515
 
        // Hide the row to prevent constant reflows
5516
 
        elTr.style.display = 'none';
5517
 
        
5518
 
        // Update TD elements with new data
5519
 
        var allTds = elTr.childNodes,
5520
 
            elTd;
5521
 
        for(var i=0,len=allTds.length; i<len; ++i) {
5522
 
            elTd = allTds[i];
5523
 
            
5524
 
            // Set the cell content
5525
 
            this.formatCell(allTds[i].firstChild, oRecord, this._oColumnSet.keys[i]);
5526
 
        }
5527
 
        
5528
 
        // Redisplay the row for reflow
5529
 
        elTr.style.display = '';
5530
 
    }
5531
 
    
5532
 
    elTr.id = oRecord.getId(); // Needed for Record association and tracking of FIRST/LAST
5533
 
    return elTr;
5534
 
},
5535
 
 
5536
 
 
5537
 
/**
5538
 
 * Deletes TR element by DOM reference or by DataTable page row index.
5539
 
 *
5540
 
 * @method _deleteTrEl
5541
 
 * @param row {HTMLElement | Number} TR element reference or Datatable page row index.
5542
 
 * @return {Boolean} Returns true if successful, else returns false.
5543
 
 * @private
5544
 
 */
5545
 
_deleteTrEl : function(row) {
5546
 
    var rowIndex;
5547
 
 
5548
 
    // Get page row index for the element
5549
 
    if(!lang.isNumber(row)) {
5550
 
        rowIndex = Dom.get(row).sectionRowIndex;
5551
 
    }
5552
 
    else {
5553
 
        rowIndex = row;
5554
 
    }
5555
 
    if(lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
5556
 
        // Cannot use tbody.deleteRow due to IE6 instability
5557
 
        //return this._elTbody.deleteRow(rowIndex);
5558
 
        return this._elTbody.removeChild(this.getTrEl(row));
5559
 
    }
5560
 
    else {
5561
 
        return null;
5562
 
    }
5563
 
},
5564
 
 
5565
 
 
5566
 
 
5567
 
 
5568
 
 
5569
 
 
5570
 
 
5571
 
 
5572
 
 
5573
 
 
5574
 
 
5575
 
 
5576
 
 
5577
 
 
5578
 
 
5579
 
 
5580
 
 
5581
 
 
5582
 
 
5583
 
 
5584
 
 
5585
 
 
5586
 
 
5587
 
 
5588
 
 
5589
 
 
5590
 
 
5591
 
// CSS/STATE FUNCTIONS
5592
 
 
5593
 
 
5594
 
 
5595
 
 
5596
 
/**
5597
 
 * Removes the class YAHOO.widget.DataTable.CLASS_FIRST from the first TR element
5598
 
 * of the DataTable page and updates internal tracker.
5599
 
 *
5600
 
 * @method _unsetFirstRow
5601
 
 * @private
5602
 
 */
5603
 
_unsetFirstRow : function() {
5604
 
    // Remove FIRST
5605
 
    if(this._sFirstTrId) {
5606
 
        Dom.removeClass(this._sFirstTrId, DT.CLASS_FIRST);
5607
 
        this._sFirstTrId = null;
5608
 
    }
5609
 
},
5610
 
 
5611
 
/**
5612
 
 * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element
5613
 
 * of the DataTable page and updates internal tracker.
5614
 
 *
5615
 
 * @method _setFirstRow
5616
 
 * @private
5617
 
 */
5618
 
_setFirstRow : function() {
5619
 
    this._unsetFirstRow();
5620
 
    var elTr = this.getFirstTrEl();
5621
 
    if(elTr) {
5622
 
        // Set FIRST
5623
 
        Dom.addClass(elTr, DT.CLASS_FIRST);
5624
 
        this._sFirstTrId = elTr.id;
5625
 
    }
5626
 
},
5627
 
 
5628
 
/**
5629
 
 * Removes the class YAHOO.widget.DataTable.CLASS_LAST from the last TR element
5630
 
 * of the DataTable page and updates internal tracker.
5631
 
 *
5632
 
 * @method _unsetLastRow
5633
 
 * @private
5634
 
 */
5635
 
_unsetLastRow : function() {
5636
 
    // Unassign previous class
5637
 
    if(this._sLastTrId) {
5638
 
        Dom.removeClass(this._sLastTrId, DT.CLASS_LAST);
5639
 
        this._sLastTrId = null;
5640
 
    }   
5641
 
},
5642
 
 
5643
 
/**
5644
 
 * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element
5645
 
 * of the DataTable page and updates internal tracker.
5646
 
 *
5647
 
 * @method _setLastRow
5648
 
 * @private
5649
 
 */
5650
 
_setLastRow : function() {
5651
 
    this._unsetLastRow();
5652
 
    var elTr = this.getLastTrEl();
5653
 
    if(elTr) {
5654
 
        // Assign class
5655
 
        Dom.addClass(elTr, DT.CLASS_LAST);
5656
 
        this._sLastTrId = elTr.id;
5657
 
    }
5658
 
},
5659
 
 
5660
 
/**
5661
 
 * Assigns the classes DT.CLASS_EVEN and DT.CLASS_ODD to one, many, or all TR elements.
5662
 
 *
5663
 
 * @method _setRowStripes
5664
 
 * @param row {HTMLElement | String | Number} (optional) HTML TR element reference
5665
 
 * or string ID, or page row index of where to start striping.
5666
 
 * @param range {Number} (optional) If given, how many rows to stripe, otherwise
5667
 
 * stripe all the rows until the end.
5668
 
 * @private
5669
 
 */
5670
 
_setRowStripes : function(row, range) {
5671
 
    // Default values stripe all rows
5672
 
    var allRows = this._elTbody.rows,
5673
 
        nStartIndex = 0,
5674
 
        nEndIndex = allRows.length,
5675
 
        aOdds = [], nOddIdx = 0,
5676
 
        aEvens = [], nEvenIdx = 0;
5677
 
 
5678
 
    // Stripe a subset
5679
 
    if((row !== null) && (row !== undefined)) {
5680
 
        // Validate given start row
5681
 
        var elStartRow = this.getTrEl(row);
5682
 
        if(elStartRow) {
5683
 
            nStartIndex = elStartRow.sectionRowIndex;
5684
 
 
5685
 
            // Validate given range
5686
 
            if(lang.isNumber(range) && (range > 1)) {
5687
 
                nEndIndex = nStartIndex + range;
5688
 
            }
5689
 
        }
5690
 
    }
5691
 
 
5692
 
    for(var i=nStartIndex; i<nEndIndex; i++) {
5693
 
        if(i%2) {
5694
 
            aOdds[nOddIdx++] = allRows[i];
5695
 
        } else {
5696
 
            aEvens[nEvenIdx++] = allRows[i];
5697
 
        }
5698
 
    }
5699
 
 
5700
 
    if (aOdds.length) {
5701
 
        Dom.replaceClass(aOdds, DT.CLASS_EVEN, DT.CLASS_ODD);
5702
 
    }
5703
 
 
5704
 
    if (aEvens.length) {
5705
 
        Dom.replaceClass(aEvens, DT.CLASS_ODD, DT.CLASS_EVEN);
5706
 
    }
5707
 
},
5708
 
 
5709
 
/**
5710
 
 * Assigns the class DT.CLASS_SELECTED to TR and TD elements.
5711
 
 *
5712
 
 * @method _setSelections
5713
 
 * @private
5714
 
 */
5715
 
_setSelections : function() {
5716
 
    // Keep track of selected rows
5717
 
    var allSelectedRows = this.getSelectedRows();
5718
 
    // Keep track of selected cells
5719
 
    var allSelectedCells = this.getSelectedCells();
5720
 
    // Anything to select?
5721
 
    if((allSelectedRows.length>0) || (allSelectedCells.length > 0)) {
5722
 
        var oColumnSet = this._oColumnSet,
5723
 
            el;
5724
 
        // Loop over each row
5725
 
        for(var i=0; i<allSelectedRows.length; i++) {
5726
 
            el = Dom.get(allSelectedRows[i]);
5727
 
            if(el) {
5728
 
                Dom.addClass(el, DT.CLASS_SELECTED);
5729
 
            }
5730
 
        }
5731
 
        // Loop over each cell
5732
 
        for(i=0; i<allSelectedCells.length; i++) {
5733
 
            el = Dom.get(allSelectedCells[i].recordId);
5734
 
            if(el) {
5735
 
                Dom.addClass(el.childNodes[oColumnSet.getColumn(allSelectedCells[i].columnKey).getKeyIndex()], DT.CLASS_SELECTED);
5736
 
            }
5737
 
        }
5738
 
    }       
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
 
 
5770
 
 
5771
 
 
5772
 
 
5773
 
 
5774
 
 
5775
 
 
5776
 
 
5777
 
 
5778
 
 
5779
 
 
5780
 
 
5781
 
 
5782
 
 
5783
 
/////////////////////////////////////////////////////////////////////////////
5784
 
//
5785
 
// Private DOM Event Handlers
5786
 
//
5787
 
/////////////////////////////////////////////////////////////////////////////
5788
 
 
5789
 
/**
5790
 
 * Validates minWidths whenever the render chain ends.
5791
 
 *
5792
 
 * @method _onRenderChainEnd
5793
 
 * @private
5794
 
 */
5795
 
_onRenderChainEnd : function() {
5796
 
    // Hide loading message
5797
 
    this.hideTableMessage();
5798
 
    
5799
 
    // Show empty message
5800
 
    if(this._elTbody.rows.length === 0) {
5801
 
        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);        
5802
 
    }
5803
 
 
5804
 
    // Execute in timeout thread to give implementers a chance
5805
 
    // to subscribe after the constructor
5806
 
    var oSelf = this;
5807
 
    setTimeout(function() {
5808
 
        if((oSelf instanceof DT) && oSelf._sId) {        
5809
 
            // Init event
5810
 
            if(oSelf._bInit) {
5811
 
                oSelf._bInit = false;
5812
 
                oSelf.fireEvent("initEvent");
5813
 
            }
5814
 
    
5815
 
            // Render event
5816
 
            oSelf.fireEvent("renderEvent");
5817
 
            // Backward compatibility
5818
 
            oSelf.fireEvent("refreshEvent");
5819
 
            YAHOO.log("DataTable rendered", "info", oSelf.toString());
5820
 
    
5821
 
            // Post-render routine
5822
 
            oSelf.validateColumnWidths();
5823
 
    
5824
 
            // Post-render event
5825
 
            oSelf.fireEvent("postRenderEvent");
5826
 
            
5827
 
            /*if(YAHOO.example.Performance.trialStart) {
5828
 
                YAHOO.log((new Date()).getTime() - YAHOO.example.Performance.trialStart.getTime() + " ms", "time");
5829
 
                YAHOO.example.Performance.trialStart = null;
5830
 
            }*/
5831
 
            
5832
 
            YAHOO.log("Post-render routine executed", "info", oSelf.toString());
5833
 
        }
5834
 
    }, 0);
5835
 
},
5836
 
 
5837
 
/**
5838
 
 * Handles click events on the DOCUMENT.
5839
 
 *
5840
 
 * @method _onDocumentClick
5841
 
 * @param e {HTMLEvent} The click event.
5842
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5843
 
 * @private
5844
 
 */
5845
 
_onDocumentClick : function(e, oSelf) {
5846
 
    var elTarget = Ev.getTarget(e);
5847
 
    var elTag = elTarget.nodeName.toLowerCase();
5848
 
 
5849
 
    if(!Dom.isAncestor(oSelf._elContainer, elTarget)) {
5850
 
        oSelf.fireEvent("tableBlurEvent");
5851
 
 
5852
 
        // Fires editorBlurEvent when click is not within the TABLE.
5853
 
        // For cases when click is within the TABLE, due to timing issues,
5854
 
        // the editorBlurEvent needs to get fired by the lower-level DOM click
5855
 
        // handlers below rather than by the TABLE click handler directly.
5856
 
        if(oSelf._oCellEditor) {
5857
 
            if(oSelf._oCellEditor.getContainerEl) {
5858
 
                var elContainer = oSelf._oCellEditor.getContainerEl();
5859
 
                // Only if the click was not within the CellEditor container
5860
 
                if(!Dom.isAncestor(elContainer, elTarget) &&
5861
 
                        (elContainer.id !== elTarget.id)) {
5862
 
                    oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
5863
 
                }
5864
 
            }
5865
 
            // Backward Compatibility
5866
 
            else if(oSelf._oCellEditor.isActive) {
5867
 
                // Only if the click was not within the Cell Editor container
5868
 
                if(!Dom.isAncestor(oSelf._oCellEditor.container, elTarget) &&
5869
 
                        (oSelf._oCellEditor.container.id !== elTarget.id)) {
5870
 
                    oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
5871
 
                }
5872
 
            }
5873
 
        }
5874
 
    }
5875
 
},
5876
 
 
5877
 
/**
5878
 
 * Handles focus events on the DataTable instance.
5879
 
 *
5880
 
 * @method _onTableFocus
5881
 
 * @param e {HTMLEvent} The focus event.
5882
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5883
 
 * @private
5884
 
 */
5885
 
_onTableFocus : function(e, oSelf) {
5886
 
    oSelf.fireEvent("tableFocusEvent");
5887
 
},
5888
 
 
5889
 
/**
5890
 
 * Handles focus events on the THEAD element.
5891
 
 *
5892
 
 * @method _onTheadFocus
5893
 
 * @param e {HTMLEvent} The focus event.
5894
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5895
 
 * @private
5896
 
 */
5897
 
_onTheadFocus : function(e, oSelf) {
5898
 
    oSelf.fireEvent("theadFocusEvent");
5899
 
    oSelf.fireEvent("tableFocusEvent");
5900
 
},
5901
 
 
5902
 
/**
5903
 
 * Handles focus events on the TBODY element.
5904
 
 *
5905
 
 * @method _onTbodyFocus
5906
 
 * @param e {HTMLEvent} The focus event.
5907
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5908
 
 * @private
5909
 
 */
5910
 
_onTbodyFocus : function(e, oSelf) {
5911
 
    oSelf.fireEvent("tbodyFocusEvent");
5912
 
    oSelf.fireEvent("tableFocusEvent");
5913
 
},
5914
 
 
5915
 
/**
5916
 
 * Handles mouseover events on the DataTable instance.
5917
 
 *
5918
 
 * @method _onTableMouseover
5919
 
 * @param e {HTMLEvent} The mouseover event.
5920
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5921
 
 * @private
5922
 
 */
5923
 
_onTableMouseover : function(e, oSelf) {
5924
 
    var elTarget = Ev.getTarget(e);
5925
 
        var elTag = elTarget.nodeName.toLowerCase();
5926
 
        var bKeepBubbling = true;
5927
 
        while(elTarget && (elTag != "table")) {
5928
 
            switch(elTag) {
5929
 
                case "body":
5930
 
                     return;
5931
 
                case "a":
5932
 
                    break;
5933
 
                case "td":
5934
 
                    bKeepBubbling = oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
5935
 
                    break;
5936
 
                case "span":
5937
 
                    if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
5938
 
                        bKeepBubbling = oSelf.fireEvent("theadLabelMouseoverEvent",{target:elTarget,event:e});
5939
 
                        // Backward compatibility
5940
 
                        bKeepBubbling = oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
5941
 
                    }
5942
 
                    break;
5943
 
                case "th":
5944
 
                    bKeepBubbling = oSelf.fireEvent("theadCellMouseoverEvent",{target:elTarget,event:e});
5945
 
                    // Backward compatibility
5946
 
                    bKeepBubbling = oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
5947
 
                    break;
5948
 
                case "tr":
5949
 
                    if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
5950
 
                        bKeepBubbling = oSelf.fireEvent("theadRowMouseoverEvent",{target:elTarget,event:e});
5951
 
                        // Backward compatibility
5952
 
                        bKeepBubbling = oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
5953
 
                    }
5954
 
                    else {
5955
 
                        bKeepBubbling = oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
5956
 
                    }
5957
 
                    break;
5958
 
                default:
5959
 
                    break;
5960
 
            }
5961
 
            if(bKeepBubbling === false) {
5962
 
                return;
5963
 
            }
5964
 
            else {
5965
 
                elTarget = elTarget.parentNode;
5966
 
                if(elTarget) {
5967
 
                    elTag = elTarget.nodeName.toLowerCase();
5968
 
                }
5969
 
            }
5970
 
        }
5971
 
        oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elContainer),event:e});
5972
 
},
5973
 
 
5974
 
/**
5975
 
 * Handles mouseout events on the DataTable instance.
5976
 
 *
5977
 
 * @method _onTableMouseout
5978
 
 * @param e {HTMLEvent} The mouseout event.
5979
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5980
 
 * @private
5981
 
 */
5982
 
_onTableMouseout : function(e, oSelf) {
5983
 
    var elTarget = Ev.getTarget(e);
5984
 
    var elTag = elTarget.nodeName.toLowerCase();
5985
 
    var bKeepBubbling = true;
5986
 
    while(elTarget && (elTag != "table")) {
5987
 
        switch(elTag) {
5988
 
            case "body":
5989
 
                return;
5990
 
            case "a":
5991
 
                break;
5992
 
            case "td":
5993
 
                bKeepBubbling = oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
5994
 
                break;
5995
 
            case "span":
5996
 
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
5997
 
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseoutEvent",{target:elTarget,event:e});
5998
 
                    // Backward compatibility
5999
 
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
6000
 
                }
6001
 
                break;
6002
 
            case "th":
6003
 
                bKeepBubbling = oSelf.fireEvent("theadCellMouseoutEvent",{target:elTarget,event:e});
6004
 
                // Backward compatibility
6005
 
                bKeepBubbling = oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
6006
 
                break;
6007
 
            case "tr":
6008
 
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6009
 
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseoutEvent",{target:elTarget,event:e});
6010
 
                    // Backward compatibility
6011
 
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
6012
 
                }
6013
 
                else {
6014
 
                    bKeepBubbling = oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
6015
 
                }
6016
 
                break;
6017
 
            default:
6018
 
                break;
6019
 
        }
6020
 
        if(bKeepBubbling === false) {
6021
 
            return;
6022
 
        }
6023
 
        else {
6024
 
            elTarget = elTarget.parentNode;
6025
 
            if(elTarget) {
6026
 
                elTag = elTarget.nodeName.toLowerCase();
6027
 
            }
6028
 
        }
6029
 
    }
6030
 
    oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elContainer),event:e});
6031
 
},
6032
 
 
6033
 
/**
6034
 
 * Handles mousedown events on the DataTable instance.
6035
 
 *
6036
 
 * @method _onTableMousedown
6037
 
 * @param e {HTMLEvent} The mousedown event.
6038
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6039
 
 * @private
6040
 
 */
6041
 
_onTableMousedown : function(e, oSelf) {
6042
 
    var elTarget = Ev.getTarget(e);
6043
 
    var elTag = elTarget.nodeName.toLowerCase();
6044
 
    var bKeepBubbling = true;
6045
 
    while(elTarget && (elTag != "table")) {
6046
 
        switch(elTag) {
6047
 
            case "body":
6048
 
                return;
6049
 
            case "a":
6050
 
                break;
6051
 
            case "td":
6052
 
                bKeepBubbling = oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
6053
 
                break;
6054
 
            case "span":
6055
 
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6056
 
                    bKeepBubbling = oSelf.fireEvent("theadLabelMousedownEvent",{target:elTarget,event:e});
6057
 
                    // Backward compatibility
6058
 
                    bKeepBubbling = oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
6059
 
                }
6060
 
                break;
6061
 
            case "th":
6062
 
                bKeepBubbling = oSelf.fireEvent("theadCellMousedownEvent",{target:elTarget,event:e});
6063
 
                // Backward compatibility
6064
 
                bKeepBubbling = oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
6065
 
                break;
6066
 
            case "tr":
6067
 
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6068
 
                    bKeepBubbling = oSelf.fireEvent("theadRowMousedownEvent",{target:elTarget,event:e});
6069
 
                    // Backward compatibility
6070
 
                    bKeepBubbling = oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
6071
 
                }
6072
 
                else {
6073
 
                    bKeepBubbling = oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
6074
 
                }
6075
 
                break;
6076
 
            default:
6077
 
                break;
6078
 
        }
6079
 
        if(bKeepBubbling === false) {
6080
 
            return;
6081
 
        }
6082
 
        else {
6083
 
            elTarget = elTarget.parentNode;
6084
 
            if(elTarget) {
6085
 
                elTag = elTarget.nodeName.toLowerCase();
6086
 
            }
6087
 
        }
6088
 
    }
6089
 
    oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elContainer),event:e});
6090
 
},
6091
 
 
6092
 
/**
6093
 
 * Handles mouseup events on the DataTable instance.
6094
 
 *
6095
 
 * @method _onTableMouseup
6096
 
 * @param e {HTMLEvent} The mouseup event.
6097
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6098
 
 * @private
6099
 
 */
6100
 
_onTableMouseup : function(e, oSelf) {
6101
 
    var elTarget = Ev.getTarget(e);
6102
 
    var elTag = elTarget.nodeName.toLowerCase();
6103
 
    var bKeepBubbling = true;
6104
 
    while(elTarget && (elTag != "table")) {
6105
 
        switch(elTag) {
6106
 
            case "body":
6107
 
                return;
6108
 
            case "a":
6109
 
                break;
6110
 
            case "td":
6111
 
                bKeepBubbling = oSelf.fireEvent("cellMouseupEvent",{target:elTarget,event:e});
6112
 
                break;
6113
 
            case "span":
6114
 
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6115
 
                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseupEvent",{target:elTarget,event:e});
6116
 
                    // Backward compatibility
6117
 
                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseupEvent",{target:elTarget,event:e});
6118
 
                }
6119
 
                break;
6120
 
            case "th":
6121
 
                bKeepBubbling = oSelf.fireEvent("theadCellMouseupEvent",{target:elTarget,event:e});
6122
 
                // Backward compatibility
6123
 
                bKeepBubbling = oSelf.fireEvent("headerCellMouseupEvent",{target:elTarget,event:e});
6124
 
                break;
6125
 
            case "tr":
6126
 
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6127
 
                    bKeepBubbling = oSelf.fireEvent("theadRowMouseupEvent",{target:elTarget,event:e});
6128
 
                    // Backward compatibility
6129
 
                    bKeepBubbling = oSelf.fireEvent("headerRowMouseupEvent",{target:elTarget,event:e});
6130
 
                }
6131
 
                else {
6132
 
                    bKeepBubbling = oSelf.fireEvent("rowMouseupEvent",{target:elTarget,event:e});
6133
 
                }
6134
 
                break;
6135
 
            default:
6136
 
                break;
6137
 
        }
6138
 
        if(bKeepBubbling === false) {
6139
 
            return;
6140
 
        }
6141
 
        else {
6142
 
            elTarget = elTarget.parentNode;
6143
 
            if(elTarget) {
6144
 
                elTag = elTarget.nodeName.toLowerCase();
6145
 
            }
6146
 
        }
6147
 
    }
6148
 
    oSelf.fireEvent("tableMouseupEvent",{target:(elTarget || oSelf._elContainer),event:e});
6149
 
},
6150
 
 
6151
 
/**
6152
 
 * Handles dblclick events on the DataTable instance.
6153
 
 *
6154
 
 * @method _onTableDblclick
6155
 
 * @param e {HTMLEvent} The dblclick event.
6156
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6157
 
 * @private
6158
 
 */
6159
 
_onTableDblclick : function(e, oSelf) {
6160
 
    var elTarget = Ev.getTarget(e);
6161
 
    var elTag = elTarget.nodeName.toLowerCase();
6162
 
    var bKeepBubbling = true;
6163
 
    while(elTarget && (elTag != "table")) {
6164
 
        switch(elTag) {
6165
 
            case "body":
6166
 
                return;
6167
 
            case "td":
6168
 
                bKeepBubbling = oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
6169
 
                break;
6170
 
            case "span":
6171
 
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6172
 
                    bKeepBubbling = oSelf.fireEvent("theadLabelDblclickEvent",{target:elTarget,event:e});
6173
 
                    // Backward compatibility
6174
 
                    bKeepBubbling = oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
6175
 
                }
6176
 
                break;
6177
 
            case "th":
6178
 
                bKeepBubbling = oSelf.fireEvent("theadCellDblclickEvent",{target:elTarget,event:e});
6179
 
                // Backward compatibility
6180
 
                bKeepBubbling = oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
6181
 
                break;
6182
 
            case "tr":
6183
 
                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6184
 
                    bKeepBubbling = oSelf.fireEvent("theadRowDblclickEvent",{target:elTarget,event:e});
6185
 
                    // Backward compatibility
6186
 
                    bKeepBubbling = oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
6187
 
                }
6188
 
                else {
6189
 
                    bKeepBubbling = oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
6190
 
                }
6191
 
                break;
6192
 
            default:
6193
 
                break;
6194
 
        }
6195
 
        if(bKeepBubbling === false) {
6196
 
            return;
6197
 
        }
6198
 
        else {
6199
 
            elTarget = elTarget.parentNode;
6200
 
            if(elTarget) {
6201
 
                elTag = elTarget.nodeName.toLowerCase();
6202
 
            }
6203
 
        }
6204
 
    }
6205
 
    oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elContainer),event:e});
6206
 
},
6207
 
/**
6208
 
 * Handles keydown events on the THEAD element.
6209
 
 *
6210
 
 * @method _onTheadKeydown
6211
 
 * @param e {HTMLEvent} The key event.
6212
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6213
 
 * @private
6214
 
 */
6215
 
_onTheadKeydown : function(e, oSelf) {
6216
 
    var elTarget = Ev.getTarget(e);
6217
 
    var elTag = elTarget.nodeName.toLowerCase();
6218
 
    var bKeepBubbling = true;
6219
 
    while(elTarget && (elTag != "table")) {
6220
 
        switch(elTag) {
6221
 
            case "body":
6222
 
                return;
6223
 
            case "input":
6224
 
            case "textarea":
6225
 
                // TODO: implement textareaKeyEvent
6226
 
                break;
6227
 
            case "thead":
6228
 
                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
6229
 
                break;
6230
 
            default:
6231
 
                break;
6232
 
        }
6233
 
        if(bKeepBubbling === false) {
6234
 
            return;
6235
 
        }
6236
 
        else {
6237
 
            elTarget = elTarget.parentNode;
6238
 
            if(elTarget) {
6239
 
                elTag = elTarget.nodeName.toLowerCase();
6240
 
            }
6241
 
        }
6242
 
    }
6243
 
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
6244
 
},
6245
 
 
6246
 
/**
6247
 
 * Handles keydown events on the TBODY element. Handles selection behavior,
6248
 
 * provides hooks for ENTER to edit functionality.
6249
 
 *
6250
 
 * @method _onTbodyKeydown
6251
 
 * @param e {HTMLEvent} The key event.
6252
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6253
 
 * @private
6254
 
 */
6255
 
_onTbodyKeydown : function(e, oSelf) {
6256
 
    var sMode = oSelf.get("selectionMode");
6257
 
 
6258
 
    if(sMode == "standard") {
6259
 
        oSelf._handleStandardSelectionByKey(e);
6260
 
    }
6261
 
    else if(sMode == "single") {
6262
 
        oSelf._handleSingleSelectionByKey(e);
6263
 
    }
6264
 
    else if(sMode == "cellblock") {
6265
 
        oSelf._handleCellBlockSelectionByKey(e);
6266
 
    }
6267
 
    else if(sMode == "cellrange") {
6268
 
        oSelf._handleCellRangeSelectionByKey(e);
6269
 
    }
6270
 
    else if(sMode == "singlecell") {
6271
 
        oSelf._handleSingleCellSelectionByKey(e);
6272
 
    }
6273
 
    
6274
 
    if(oSelf._oCellEditor) {
6275
 
        if(oSelf._oCellEditor.fireEvent) {
6276
 
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
6277
 
        }
6278
 
        else if(oSelf._oCellEditor.isActive) {
6279
 
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
6280
 
        }
6281
 
    }
6282
 
 
6283
 
    var elTarget = Ev.getTarget(e);
6284
 
    var elTag = elTarget.nodeName.toLowerCase();
6285
 
    var bKeepBubbling = true;
6286
 
    while(elTarget && (elTag != "table")) {
6287
 
        switch(elTag) {
6288
 
            case "body":
6289
 
                return;
6290
 
            case "tbody":
6291
 
                bKeepBubbling = oSelf.fireEvent("tbodyKeyEvent",{target:elTarget,event:e});
6292
 
                break;
6293
 
            default:
6294
 
                break;
6295
 
        }
6296
 
        if(bKeepBubbling === false) {
6297
 
            return;
6298
 
        }
6299
 
        else {
6300
 
            elTarget = elTarget.parentNode;
6301
 
            if(elTarget) {
6302
 
                elTag = elTarget.nodeName.toLowerCase();
6303
 
            }
6304
 
        }
6305
 
    }
6306
 
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
6307
 
},
6308
 
 
6309
 
/**
6310
 
 * Handles keypress events on the TABLE. Mainly to support stopEvent on Mac.
6311
 
 *
6312
 
 * @method _onTableKeypress
6313
 
 * @param e {HTMLEvent} The key event.
6314
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6315
 
 * @private
6316
 
 */
6317
 
_onTableKeypress : function(e, oSelf) {
6318
 
    if(ua.opera || (navigator.userAgent.toLowerCase().indexOf("mac") !== -1) && (ua.webkit < 420)) {
6319
 
        var nKey = Ev.getCharCode(e);
6320
 
        // arrow down
6321
 
        if(nKey == 40) {
6322
 
            Ev.stopEvent(e);
6323
 
        }
6324
 
        // arrow up
6325
 
        else if(nKey == 38) {
6326
 
            Ev.stopEvent(e);
6327
 
        }
6328
 
    }
6329
 
},
6330
 
 
6331
 
/**
6332
 
 * Handles click events on the THEAD element.
6333
 
 *
6334
 
 * @method _onTheadClick
6335
 
 * @param e {HTMLEvent} The click event.
6336
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6337
 
 * @private
6338
 
 */
6339
 
_onTheadClick : function(e, oSelf) {
6340
 
    // This blurs the CellEditor
6341
 
    if(oSelf._oCellEditor) {
6342
 
        if(oSelf._oCellEditor.fireEvent) {
6343
 
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
6344
 
        }
6345
 
        // Backward compatibility
6346
 
        else if(oSelf._oCellEditor.isActive) {
6347
 
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
6348
 
        }
6349
 
    }
6350
 
 
6351
 
    var elTarget = Ev.getTarget(e),
6352
 
        elTag = elTarget.nodeName.toLowerCase(),
6353
 
        bKeepBubbling = true;
6354
 
    while(elTarget && (elTag != "table")) {
6355
 
        switch(elTag) {
6356
 
            case "body":
6357
 
                return;
6358
 
            case "input":
6359
 
                var sType = elTarget.type.toLowerCase();
6360
 
                if(sType == "checkbox") {
6361
 
                    bKeepBubbling = oSelf.fireEvent("theadCheckboxClickEvent",{target:elTarget,event:e});
6362
 
                }
6363
 
                else if(sType == "radio") {
6364
 
                    bKeepBubbling = oSelf.fireEvent("theadRadioClickEvent",{target:elTarget,event:e});
6365
 
                }
6366
 
                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
6367
 
                    bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
6368
 
                }
6369
 
                break;
6370
 
            case "a":
6371
 
                bKeepBubbling = oSelf.fireEvent("theadLinkClickEvent",{target:elTarget,event:e});
6372
 
                break;
6373
 
            case "button":
6374
 
                bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
6375
 
                break;
6376
 
            case "span":
6377
 
                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6378
 
                    bKeepBubbling = oSelf.fireEvent("theadLabelClickEvent",{target:elTarget,event:e});
6379
 
                    // Backward compatibility
6380
 
                    bKeepBubbling = oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
6381
 
                }
6382
 
                break;
6383
 
            case "th":
6384
 
                bKeepBubbling = oSelf.fireEvent("theadCellClickEvent",{target:elTarget,event:e});
6385
 
                // Backward compatibility
6386
 
                bKeepBubbling = oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
6387
 
                break;
6388
 
            case "tr":
6389
 
                bKeepBubbling = oSelf.fireEvent("theadRowClickEvent",{target:elTarget,event:e});
6390
 
                // Backward compatibility
6391
 
                bKeepBubbling = oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
6392
 
                break;
6393
 
            default:
6394
 
                break;
6395
 
        }
6396
 
        if(bKeepBubbling === false) {
6397
 
            return;
6398
 
        }
6399
 
        else {
6400
 
            elTarget = elTarget.parentNode;
6401
 
            if(elTarget) {
6402
 
                elTag = elTarget.nodeName.toLowerCase();
6403
 
            }
6404
 
        }
6405
 
    }
6406
 
    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
6407
 
},
6408
 
 
6409
 
/**
6410
 
 * Handles click events on the primary TBODY element.
6411
 
 *
6412
 
 * @method _onTbodyClick
6413
 
 * @param e {HTMLEvent} The click event.
6414
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6415
 
 * @private
6416
 
 */
6417
 
_onTbodyClick : function(e, oSelf) {
6418
 
    // This blurs the CellEditor
6419
 
    if(oSelf._oCellEditor) {
6420
 
        if(oSelf._oCellEditor.fireEvent) {
6421
 
            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
6422
 
        }
6423
 
        else if(oSelf._oCellEditor.isActive) {
6424
 
            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
6425
 
        }
6426
 
    }
6427
 
 
6428
 
    // Fire Custom Events
6429
 
    var elTarget = Ev.getTarget(e),
6430
 
        elTag = elTarget.nodeName.toLowerCase(),
6431
 
        bKeepBubbling = true;
6432
 
    while(elTarget && (elTag != "table")) {
6433
 
        switch(elTag) {
6434
 
            case "body":
6435
 
                return;
6436
 
            case "input":
6437
 
                var sType = elTarget.type.toLowerCase();
6438
 
                if(sType == "checkbox") {
6439
 
                    bKeepBubbling = oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e});
6440
 
                }
6441
 
                else if(sType == "radio") {
6442
 
                    bKeepBubbling = oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e});
6443
 
                }
6444
 
                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
6445
 
                    bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
6446
 
                }
6447
 
                break;
6448
 
            case "a":
6449
 
                bKeepBubbling = oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
6450
 
                break;
6451
 
            case "button":
6452
 
                bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
6453
 
                break;
6454
 
            case "td":
6455
 
                bKeepBubbling = oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
6456
 
                break;
6457
 
            case "tr":
6458
 
                bKeepBubbling = oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
6459
 
                break;
6460
 
            default:
6461
 
                break;
6462
 
        }
6463
 
        if(bKeepBubbling === false) {
6464
 
            return;
6465
 
        }
6466
 
        else {
6467
 
            elTarget = elTarget.parentNode;
6468
 
            if(elTarget) {
6469
 
                elTag = elTarget.nodeName.toLowerCase();
6470
 
            }
6471
 
        }
6472
 
    }
6473
 
    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
6474
 
},
6475
 
 
6476
 
/**
6477
 
 * Handles change events on SELECT elements within DataTable.
6478
 
 *
6479
 
 * @method _onDropdownChange
6480
 
 * @param e {HTMLEvent} The change event.
6481
 
 * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6482
 
 * @private
6483
 
 */
6484
 
_onDropdownChange : function(e, oSelf) {
6485
 
    var elTarget = Ev.getTarget(e);
6486
 
    oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget});
6487
 
},
6488
 
 
6489
 
 
6490
 
 
6491
 
 
6492
 
 
6493
 
 
6494
 
 
6495
 
 
6496
 
 
6497
 
 
6498
 
 
6499
 
 
6500
 
 
6501
 
 
6502
 
 
6503
 
 
6504
 
 
6505
 
 
6506
 
 
6507
 
 
6508
 
 
6509
 
 
6510
 
 
6511
 
 
6512
 
 
6513
 
 
6514
 
 
6515
 
 
6516
 
 
6517
 
 
6518
 
 
6519
 
 
6520
 
/////////////////////////////////////////////////////////////////////////////
6521
 
//
6522
 
// Public member variables
6523
 
//
6524
 
/////////////////////////////////////////////////////////////////////////////
6525
 
/**
6526
 
 * Returns object literal of initial configs.
6527
 
 *
6528
 
 * @property configs
6529
 
 * @type Object
6530
 
 * @default {} 
6531
 
 */
6532
 
configs: null,
6533
 
 
6534
 
 
6535
 
/////////////////////////////////////////////////////////////////////////////
6536
 
//
6537
 
// Public methods
6538
 
//
6539
 
/////////////////////////////////////////////////////////////////////////////
6540
 
 
6541
 
/**
6542
 
 * Returns unique id assigned to instance, which is a useful prefix for
6543
 
 * generating unique DOM ID strings.
6544
 
 *
6545
 
 * @method getId
6546
 
 * @return {String} Unique ID of the DataSource instance.
6547
 
 */
6548
 
getId : function() {
6549
 
    return this._sId;
6550
 
},
6551
 
 
6552
 
/**
6553
 
 * DataSource instance name, for logging.
6554
 
 *
6555
 
 * @method toString
6556
 
 * @return {String} Unique name of the DataSource instance.
6557
 
 */
6558
 
 
6559
 
toString : function() {
6560
 
    return "DataTable instance " + this._sId;
6561
 
},
6562
 
 
6563
 
/**
6564
 
 * Returns the DataTable instance's DataSource instance.
6565
 
 *
6566
 
 * @method getDataSource
6567
 
 * @return {YAHOO.util.DataSource} DataSource instance.
6568
 
 */
6569
 
getDataSource : function() {
6570
 
    return this._oDataSource;
6571
 
},
6572
 
 
6573
 
/**
6574
 
 * Returns the DataTable instance's ColumnSet instance.
6575
 
 *
6576
 
 * @method getColumnSet
6577
 
 * @return {YAHOO.widget.ColumnSet} ColumnSet instance.
6578
 
 */
6579
 
getColumnSet : function() {
6580
 
    return this._oColumnSet;
6581
 
},
6582
 
 
6583
 
/**
6584
 
 * Returns the DataTable instance's RecordSet instance.
6585
 
 *
6586
 
 * @method getRecordSet
6587
 
 * @return {YAHOO.widget.RecordSet} RecordSet instance.
6588
 
 */
6589
 
getRecordSet : function() {
6590
 
    return this._oRecordSet;
6591
 
},
6592
 
 
6593
 
/**
6594
 
 * Returns on object literal representing the DataTable instance's current
6595
 
 * state with the following properties:
6596
 
 * <dl>
6597
 
 * <dt>pagination</dt>
6598
 
 * <dd>Instance of YAHOO.widget.Paginator</dd>
6599
 
 *
6600
 
 * <dt>sortedBy</dt>
6601
 
 * <dd>
6602
 
 *     <dl>
6603
 
 *         <dt>sortedBy.key</dt>
6604
 
 *         <dd>{String} Key of sorted Column</dd>
6605
 
 *         <dt>sortedBy.dir</dt>
6606
 
 *         <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
6607
 
 *     </dl>
6608
 
 * </dd>
6609
 
 *
6610
 
 * <dt>selectedRows</dt>
6611
 
 * <dd>Array of selected rows by Record ID.</dd>
6612
 
 *
6613
 
 * <dt>selectedCells</dt>
6614
 
 * <dd>Selected cells as an array of object literals:
6615
 
 *     {recordId:sRecordId, columnKey:sColumnKey}</dd>
6616
 
 * </dl>
6617
 
 *  
6618
 
 * @method getState
6619
 
 * @return {Object} DataTable instance state object literal values.
6620
 
 */
6621
 
getState : function() {
6622
 
    return {
6623
 
        totalRecords: this.get('paginator') ? this.get('paginator').get("totalRecords") : this._oRecordSet.getLength(),
6624
 
        pagination: this.get("paginator") ? this.get("paginator").getState() : null,
6625
 
        sortedBy: this.get("sortedBy"),
6626
 
        selectedRows: this.getSelectedRows(),
6627
 
        selectedCells: this.getSelectedCells()
6628
 
    };
6629
 
},
6630
 
 
6631
 
 
6632
 
 
6633
 
 
6634
 
 
6635
 
 
6636
 
 
6637
 
 
6638
 
 
6639
 
 
6640
 
 
6641
 
 
6642
 
 
6643
 
 
6644
 
 
6645
 
 
6646
 
 
6647
 
 
6648
 
 
6649
 
 
6650
 
 
6651
 
 
6652
 
 
6653
 
 
6654
 
 
6655
 
 
6656
 
 
6657
 
 
6658
 
 
6659
 
 
6660
 
 
6661
 
 
6662
 
 
6663
 
 
6664
 
 
6665
 
 
6666
 
 
6667
 
 
6668
 
 
6669
 
 
6670
 
 
6671
 
 
6672
 
 
6673
 
// DOM ACCESSORS
6674
 
 
6675
 
/**
6676
 
 * Returns DOM reference to the DataTable's container element.
6677
 
 *
6678
 
 * @method getContainerEl
6679
 
 * @return {HTMLElement} Reference to DIV element.
6680
 
 */
6681
 
getContainerEl : function() {
6682
 
    return this._elContainer;
6683
 
},
6684
 
 
6685
 
/**
6686
 
 * Returns DOM reference to the DataTable's TABLE element.
6687
 
 *
6688
 
 * @method getTableEl
6689
 
 * @return {HTMLElement} Reference to TABLE element.
6690
 
 */
6691
 
getTableEl : function() {
6692
 
    return this._elTable;
6693
 
},
6694
 
 
6695
 
/**
6696
 
 * Returns DOM reference to the DataTable's THEAD element.
6697
 
 *
6698
 
 * @method getTheadEl
6699
 
 * @return {HTMLElement} Reference to THEAD element.
6700
 
 */
6701
 
getTheadEl : function() {
6702
 
    return this._elThead;
6703
 
},
6704
 
 
6705
 
/**
6706
 
 * Returns DOM reference to the DataTable's primary TBODY element.
6707
 
 *
6708
 
 * @method getTbodyEl
6709
 
 * @return {HTMLElement} Reference to TBODY element.
6710
 
 */
6711
 
getTbodyEl : function() {
6712
 
    return this._elTbody;
6713
 
},
6714
 
 
6715
 
/**
6716
 
 * Returns DOM reference to the DataTable's secondary TBODY element that is
6717
 
 * used to display messages.
6718
 
 *
6719
 
 * @method getMsgTbodyEl
6720
 
 * @return {HTMLElement} Reference to TBODY element.
6721
 
 */
6722
 
getMsgTbodyEl : function() {
6723
 
    return this._elMsgTbody;
6724
 
},
6725
 
 
6726
 
/**
6727
 
 * Returns DOM reference to the TD element within the secondary TBODY that is
6728
 
 * used to display messages.
6729
 
 *
6730
 
 * @method getMsgTdEl
6731
 
 * @return {HTMLElement} Reference to TD element.
6732
 
 */
6733
 
getMsgTdEl : function() {
6734
 
    return this._elMsgTd;
6735
 
},
6736
 
 
6737
 
/**
6738
 
 * Returns the corresponding TR reference for a given DOM element, ID string or
6739
 
 * directly page row index. If the given identifier is a child of a TR element,
6740
 
 * then DOM tree is traversed until a parent TR element is returned, otherwise
6741
 
 * null.
6742
 
 *
6743
 
 * @method getTrEl
6744
 
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to
6745
 
 * get: by element reference, ID string, page row index, or Record.
6746
 
 * @return {HTMLElement} Reference to TR element, or null.
6747
 
 */
6748
 
getTrEl : function(row) {
6749
 
    // By Record
6750
 
    if(row instanceof YAHOO.widget.Record) {
6751
 
        return document.getElementById(row.getId());
6752
 
    }
6753
 
    // By page row index
6754
 
    else if(lang.isNumber(row)) {
6755
 
        var allRows = this._elTbody.rows;
6756
 
        return ((row > -1) && (row < allRows.length)) ? allRows[row] : null;
6757
 
    }
6758
 
    // By ID string or element reference
6759
 
    else {
6760
 
        var elRow = (lang.isString(row)) ? document.getElementById(row) : row;
6761
 
 
6762
 
        // Validate HTML element
6763
 
        if(elRow && (elRow.ownerDocument == document)) {
6764
 
            // Validate TR element
6765
 
            if(elRow.nodeName.toLowerCase() != "tr") {
6766
 
                // Traverse up the DOM to find the corresponding TR element
6767
 
                elRow = Dom.getAncestorByTagName(elRow,"tr");
6768
 
            }
6769
 
 
6770
 
            return elRow;
6771
 
        }
6772
 
    }
6773
 
 
6774
 
    return null;
6775
 
},
6776
 
 
6777
 
/**
6778
 
 * Returns DOM reference to the first TR element in the DataTable page, or null.
6779
 
 *
6780
 
 * @method getFirstTrEl
6781
 
 * @return {HTMLElement} Reference to TR element.
6782
 
 */
6783
 
getFirstTrEl : function() {
6784
 
    return this._elTbody.rows[0] || null;
6785
 
},
6786
 
 
6787
 
/**
6788
 
 * Returns DOM reference to the last TR element in the DataTable page, or null.
6789
 
 *
6790
 
 * @method getLastTrEl
6791
 
 * @return {HTMLElement} Reference to last TR element.
6792
 
 */
6793
 
getLastTrEl : function() {
6794
 
    var allRows = this._elTbody.rows;
6795
 
        if(allRows.length > 0) {
6796
 
            return allRows[allRows.length-1] || null;
6797
 
        }
6798
 
},
6799
 
 
6800
 
/**
6801
 
 * Returns DOM reference to the next TR element from the given TR element, or null.
6802
 
 *
6803
 
 * @method getNextTrEl
6804
 
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
6805
 
 * reference, ID string, page row index, or Record from which to get next TR element.
6806
 
 * @return {HTMLElement} Reference to next TR element.
6807
 
 */
6808
 
getNextTrEl : function(row) {
6809
 
    var nThisTrIndex = this.getTrIndex(row);
6810
 
    if(nThisTrIndex !== null) {
6811
 
        var allRows = this._elTbody.rows;
6812
 
        if(nThisTrIndex < allRows.length-1) {
6813
 
            return allRows[nThisTrIndex+1];
6814
 
        }
6815
 
    }
6816
 
 
6817
 
    YAHOO.log("Could not get next TR element for row " + row, "info", this.toString());
6818
 
    return null;
6819
 
},
6820
 
 
6821
 
/**
6822
 
 * Returns DOM reference to the previous TR element from the given TR element, or null.
6823
 
 *
6824
 
 * @method getPreviousTrEl
6825
 
 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
6826
 
 * reference, ID string, page row index, or Record from which to get previous TR element.
6827
 
 * @return {HTMLElement} Reference to previous TR element.
6828
 
 */
6829
 
getPreviousTrEl : function(row) {
6830
 
    var nThisTrIndex = this.getTrIndex(row);
6831
 
    if(nThisTrIndex !== null) {
6832
 
        var allRows = this._elTbody.rows;
6833
 
        if(nThisTrIndex > 0) {
6834
 
            return allRows[nThisTrIndex-1];
6835
 
        }
6836
 
    }
6837
 
 
6838
 
    YAHOO.log("Could not get previous TR element for row " + row, "info", this.toString());
6839
 
    return null;
6840
 
},
6841
 
 
6842
 
/**
6843
 
 * Returns DOM reference to a TD liner element.
6844
 
 *
6845
 
 * @method getTdLinerEl
6846
 
 * @param cell {HTMLElement | Object} TD element or child of a TD element, or
6847
 
 * object literal of syntax {record:oRecord, column:oColumn}.
6848
 
 * @return {HTMLElement} Reference to TD liner element.
6849
 
 */
6850
 
getTdLinerEl : function(cell) {
6851
 
    var elCell = this.getTdEl(cell);
6852
 
    return elCell.firstChild || null;
6853
 
},
6854
 
 
6855
 
/**
6856
 
 * Returns DOM reference to a TD element.
6857
 
 *
6858
 
 * @method getTdEl
6859
 
 * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
6860
 
 * object literal of syntax {record:oRecord, column:oColumn}.
6861
 
 * @return {HTMLElement} Reference to TD element.
6862
 
 */
6863
 
getTdEl : function(cell) {
6864
 
    var elCell;
6865
 
    var el = Dom.get(cell);
6866
 
 
6867
 
    // Validate HTML element
6868
 
    if(el && (el.ownerDocument == document)) {
6869
 
        // Validate TD element
6870
 
        if(el.nodeName.toLowerCase() != "td") {
6871
 
            // Traverse up the DOM to find the corresponding TR element
6872
 
            elCell = Dom.getAncestorByTagName(el, "td");
6873
 
        }
6874
 
        else {
6875
 
            elCell = el;
6876
 
        }
6877
 
 
6878
 
        return elCell;
6879
 
    }
6880
 
    else if(cell) {
6881
 
        var oRecord, nColKeyIndex;
6882
 
 
6883
 
        if(lang.isString(cell.columnKey) && lang.isString(cell.recordId)) {
6884
 
            oRecord = this.getRecord(cell.recordId);
6885
 
            var oColumn = this.getColumn(cell.columnKey);
6886
 
            if(oColumn) {
6887
 
                nColKeyIndex = oColumn.getKeyIndex();
6888
 
            }
6889
 
 
6890
 
        }
6891
 
        if(cell.record && cell.column && cell.column.getKeyIndex) {
6892
 
            oRecord = cell.record;
6893
 
            nColKeyIndex = cell.column.getKeyIndex();
6894
 
        }
6895
 
        var elRow = this.getTrEl(oRecord);
6896
 
        if((nColKeyIndex !== null) && elRow && elRow.cells && elRow.cells.length > 0) {
6897
 
            return elRow.cells[nColKeyIndex] || null;
6898
 
        }
6899
 
    }
6900
 
 
6901
 
    return null;
6902
 
},
6903
 
 
6904
 
/**
6905
 
 * Returns DOM reference to the first TD element in the DataTable page (by default),
6906
 
 * the first TD element of the optionally given row, or null.
6907
 
 *
6908
 
 * @method getFirstTdEl
6909
 
 * @param row {HTMLElement} (optional) row from which to get first TD
6910
 
 * @return {HTMLElement} Reference to TD element.
6911
 
 */
6912
 
getFirstTdEl : function(row) {
6913
 
    var elRow = this.getTrEl(row) || this.getFirstTrEl();
6914
 
    if(elRow && (elRow.cells.length > 0)) {
6915
 
        return elRow.cells[0];
6916
 
    }
6917
 
    YAHOO.log("Could not get first TD element for row " + elRow, "info", this.toString());
6918
 
    return null;
6919
 
},
6920
 
 
6921
 
/**
6922
 
 * Returns DOM reference to the last TD element in the DataTable page (by default),
6923
 
 * the first TD element of the optionally given row, or null.
6924
 
 *
6925
 
 * @method getLastTdEl
6926
 
 * @return {HTMLElement} Reference to last TD element.
6927
 
 */
6928
 
getLastTdEl : function(row) {
6929
 
    var elRow = this.getTrEl(row) || this.getLastTrEl();
6930
 
    if(elRow && (elRow.cells.length > 0)) {
6931
 
        return elRow.cells[elRow.cells.length-1];
6932
 
    }
6933
 
    YAHOO.log("Could not get last TD element for row " + elRow, "info", this.toString());
6934
 
    return null;
6935
 
},
6936
 
 
6937
 
/**
6938
 
 * Returns DOM reference to the next TD element from the given cell, or null.
6939
 
 *
6940
 
 * @method getNextTdEl
6941
 
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
6942
 
 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
6943
 
 * @return {HTMLElement} Reference to next TD element, or null.
6944
 
 */
6945
 
getNextTdEl : function(cell) {
6946
 
    var elCell = this.getTdEl(cell);
6947
 
    if(elCell) {
6948
 
        var nThisTdIndex = elCell.cellIndex;
6949
 
        var elRow = this.getTrEl(elCell);
6950
 
        if(nThisTdIndex < elRow.cells.length-1) {
6951
 
            return elRow.cells[nThisTdIndex+1];
6952
 
        }
6953
 
        else {
6954
 
            var elNextRow = this.getNextTrEl(elRow);
6955
 
            if(elNextRow) {
6956
 
                return elNextRow.cells[0];
6957
 
            }
6958
 
        }
6959
 
    }
6960
 
    YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
6961
 
    return null;
6962
 
},
6963
 
 
6964
 
/**
6965
 
 * Returns DOM reference to the previous TD element from the given cell, or null.
6966
 
 *
6967
 
 * @method getPreviousTdEl
6968
 
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
6969
 
 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
6970
 
 * @return {HTMLElement} Reference to previous TD element, or null.
6971
 
 */
6972
 
getPreviousTdEl : function(cell) {
6973
 
    var elCell = this.getTdEl(cell);
6974
 
    if(elCell) {
6975
 
        var nThisTdIndex = elCell.cellIndex;
6976
 
        var elRow = this.getTrEl(elCell);
6977
 
        if(nThisTdIndex > 0) {
6978
 
            return elRow.cells[nThisTdIndex-1];
6979
 
        }
6980
 
        else {
6981
 
            var elPreviousRow = this.getPreviousTrEl(elRow);
6982
 
            if(elPreviousRow) {
6983
 
                return this.getLastTdEl(elPreviousRow);
6984
 
            }
6985
 
        }
6986
 
    }
6987
 
    YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
6988
 
    return null;
6989
 
},
6990
 
 
6991
 
/**
6992
 
 * Returns DOM reference to the above TD element from the given cell, or null.
6993
 
 *
6994
 
 * @method getAboveTdEl
6995
 
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
6996
 
 * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
6997
 
 * @return {HTMLElement} Reference to next TD element, or null.
6998
 
 */
6999
 
getAboveTdEl : function(cell) {
7000
 
    var elCell = this.getTdEl(cell);
7001
 
    if(elCell) {
7002
 
        var elPreviousRow = this.getPreviousTrEl(elCell);
7003
 
        if(elPreviousRow) {
7004
 
            return elPreviousRow.cells[elCell.cellIndex];
7005
 
        }
7006
 
    }
7007
 
    YAHOO.log("Could not get above TD element for cell " + cell, "info", this.toString());
7008
 
    return null;
7009
 
},
7010
 
 
7011
 
/**
7012
 
 * Returns DOM reference to the below TD element from the given cell, or null.
7013
 
 *
7014
 
 * @method getBelowTdEl
7015
 
 * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
7016
 
 * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
7017
 
 * @return {HTMLElement} Reference to previous TD element, or null.
7018
 
 */
7019
 
getBelowTdEl : function(cell) {
7020
 
    var elCell = this.getTdEl(cell);
7021
 
    if(elCell) {
7022
 
        var elNextRow = this.getNextTrEl(elCell);
7023
 
        if(elNextRow) {
7024
 
            return elNextRow.cells[elCell.cellIndex];
7025
 
        }
7026
 
    }
7027
 
    YAHOO.log("Could not get below TD element for cell " + cell, "info", this.toString());
7028
 
    return null;
7029
 
},
7030
 
 
7031
 
/**
7032
 
 * Returns DOM reference to a TH liner element. Needed to normalize for resizeable 
7033
 
 * Columns, which have an additional resizer liner DIV element between the TH
7034
 
 * element and the liner DIV element. 
7035
 
 *
7036
 
 * @method getThLinerEl
7037
 
 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
7038
 
 * DOM element reference, or string ID.
7039
 
 * @return {HTMLElement} Reference to TH liner element.
7040
 
 */
7041
 
getThLinerEl : function(theadCell) {
7042
 
    var oColumn = this.getColumn(theadCell);
7043
 
    return (oColumn) ? oColumn.getThLinerEl() : null;
7044
 
},
7045
 
 
7046
 
/**
7047
 
 * Returns DOM reference to a TH element.
7048
 
 *
7049
 
 * @method getThEl
7050
 
 * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
7051
 
 * DOM element reference, or string ID.
7052
 
 * @return {HTMLElement} Reference to TH element.
7053
 
 */
7054
 
getThEl : function(theadCell) {
7055
 
    var elTh;
7056
 
 
7057
 
    // Validate Column instance
7058
 
    if(theadCell instanceof YAHOO.widget.Column) {
7059
 
        var oColumn = theadCell;
7060
 
        elTh = oColumn.getThEl();
7061
 
        if(elTh) {
7062
 
            return elTh;
7063
 
        }
7064
 
    }
7065
 
    // Validate HTML element
7066
 
    else {
7067
 
        var el = Dom.get(theadCell);
7068
 
 
7069
 
        if(el && (el.ownerDocument == document)) {
7070
 
            // Validate TH element
7071
 
            if(el.nodeName.toLowerCase() != "th") {
7072
 
                // Traverse up the DOM to find the corresponding TR element
7073
 
                elTh = Dom.getAncestorByTagName(el,"th");
7074
 
            }
7075
 
            else {
7076
 
                elTh = el;
7077
 
            }
7078
 
 
7079
 
            return elTh;
7080
 
        }
7081
 
    }
7082
 
 
7083
 
    return null;
7084
 
},
7085
 
 
7086
 
/**
7087
 
 * Returns the page row index of given row. Returns null if the row is not on the
7088
 
 * current DataTable page.
7089
 
 *
7090
 
 * @method getTrIndex
7091
 
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID
7092
 
 * string reference to an element within the DataTable page, a Record instance,
7093
 
 * or a Record's RecordSet index.
7094
 
 * @return {Number} Page row index, or null if row does not exist or is not on current page.
7095
 
 */
7096
 
getTrIndex : function(row) {
7097
 
    var nRecordIndex;
7098
 
 
7099
 
    // By Record
7100
 
    if(row instanceof YAHOO.widget.Record) {
7101
 
        nRecordIndex = this._oRecordSet.getRecordIndex(row);
7102
 
        if(nRecordIndex === null) {
7103
 
            // Not a valid Record
7104
 
            return null;
7105
 
        }
7106
 
    }
7107
 
    // Calculate page row index from Record index
7108
 
    else if(lang.isNumber(row)) {
7109
 
        nRecordIndex = row;
7110
 
    }
7111
 
    if(lang.isNumber(nRecordIndex)) {
7112
 
        // Validate the number
7113
 
        if((nRecordIndex > -1) && (nRecordIndex < this._oRecordSet.getLength())) {
7114
 
            // DataTable is paginated
7115
 
            var oPaginator = this.get('paginator');
7116
 
            if(oPaginator) {
7117
 
                // Check the record index is within the indices of the
7118
 
                // current page
7119
 
                var rng = oPaginator.getPageRecords();
7120
 
                if (rng && nRecordIndex >= rng[0] && nRecordIndex <= rng[1]) {
7121
 
                    // This Record is on current page
7122
 
                    return nRecordIndex - rng[0];
7123
 
                }
7124
 
                // This Record is not on current page
7125
 
                else {
7126
 
                    return null;
7127
 
                }
7128
 
            }
7129
 
            // Not paginated, just return the Record index
7130
 
            else {
7131
 
                return nRecordIndex;
7132
 
            }
7133
 
        }
7134
 
        // RecordSet index is out of range
7135
 
        else {
7136
 
            return null;
7137
 
        }
7138
 
    }
7139
 
    // By element reference or ID string
7140
 
    else {
7141
 
        // Validate TR element
7142
 
        var elRow = this.getTrEl(row);
7143
 
        if(elRow && (elRow.ownerDocument == document) &&
7144
 
                (elRow.parentNode == this._elTbody)) {
7145
 
            return elRow.sectionRowIndex;
7146
 
        }
7147
 
    }
7148
 
 
7149
 
    YAHOO.log("Could not get page row index for row " + row, "info", this.toString());
7150
 
    return null;
7151
 
},
7152
 
 
7153
 
 
7154
 
 
7155
 
 
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
 
 
7186
 
 
7187
 
 
7188
 
 
7189
 
 
7190
 
 
7191
 
 
7192
 
 
7193
 
 
7194
 
 
7195
 
 
7196
 
 
7197
 
 
7198
 
// TABLE FUNCTIONS
7199
 
 
7200
 
/**
7201
 
 * Resets a RecordSet with the given data and populates the page view
7202
 
 * with the new data. Any previous data, and selection and sort states are
7203
 
 * cleared. New data should be added as a separate step. 
7204
 
 *
7205
 
 * @method initializeTable
7206
 
 */
7207
 
initializeTable : function() {
7208
 
    // Reset init flag
7209
 
    this._bInit = true;
7210
 
    
7211
 
    // Clear the RecordSet
7212
 
    this._oRecordSet.reset();
7213
 
 
7214
 
    // Clear the Paginator's totalRecords if paginating
7215
 
    var pag = this.get('paginator');
7216
 
    if (pag) {
7217
 
        pag.set('totalRecords',0);
7218
 
    }
7219
 
 
7220
 
    // Clear selections
7221
 
    this._unselectAllTrEls();
7222
 
    this._unselectAllTdEls();
7223
 
    this._aSelections = null;
7224
 
    this._oAnchorRecord = null;
7225
 
    this._oAnchorCell = null;
7226
 
    
7227
 
    // Clear sort
7228
 
    this.set("sortedBy", null);
7229
 
},
7230
 
 
7231
 
/**
7232
 
 * Internal wrapper calls run() on render Chain instance.
7233
 
 *
7234
 
 * @method _runRenderChain
7235
 
 * @private 
7236
 
 */
7237
 
_runRenderChain : function() {
7238
 
    this._oChainRender.run();
7239
 
},
7240
 
 
7241
 
/**
7242
 
 * Renders the view with existing Records from the RecordSet while
7243
 
 * maintaining sort, pagination, and selection states. For performance, reuses
7244
 
 * existing DOM elements when possible while deleting extraneous elements.
7245
 
 *
7246
 
 * @method render
7247
 
 */
7248
 
render : function() {
7249
 
//YAHOO.example.Performance.trialStart = new Date();
7250
 
 
7251
 
    this._oChainRender.stop();
7252
 
    YAHOO.log("DataTable rendering...", "info", this.toString());
7253
 
 
7254
 
    var i, j, k, len, allRecords;
7255
 
 
7256
 
    var oPaginator = this.get('paginator');
7257
 
    // Paginator is enabled, show a subset of Records and update Paginator UI
7258
 
    if(oPaginator) {
7259
 
        allRecords = this._oRecordSet.getRecords(
7260
 
                        oPaginator.getStartIndex(),
7261
 
                        oPaginator.getRowsPerPage());
7262
 
    }
7263
 
    // Not paginated, show all records
7264
 
    else {
7265
 
        allRecords = this._oRecordSet.getRecords();
7266
 
    }
7267
 
 
7268
 
    // From the top, update in-place existing rows, so as to reuse DOM elements
7269
 
    var elTbody = this._elTbody,
7270
 
        loopN = this.get("renderLoopSize"),
7271
 
        nRecordsLength = allRecords.length;
7272
 
    
7273
 
    // Table has rows
7274
 
    if(nRecordsLength > 0) {                
7275
 
        elTbody.style.display = "none";
7276
 
        while(elTbody.lastChild) {
7277
 
            elTbody.removeChild(elTbody.lastChild);
7278
 
        }
7279
 
        elTbody.style.display = "";
7280
 
 
7281
 
        // Set up the loop Chain to render rows
7282
 
        this._oChainRender.add({
7283
 
            method: function(oArg) {
7284
 
                if((this instanceof DT) && this._sId) {
7285
 
                    var i = oArg.nCurrentRecord,
7286
 
                        endRecordIndex = ((oArg.nCurrentRecord+oArg.nLoopLength) > nRecordsLength) ?
7287
 
                                nRecordsLength : (oArg.nCurrentRecord+oArg.nLoopLength),
7288
 
                        elRow, nextSibling;
7289
 
 
7290
 
                    elTbody.style.display = "none";
7291
 
                    
7292
 
                    for(; i<endRecordIndex; i++) {
7293
 
                        elRow = Dom.get(allRecords[i].getId());
7294
 
                        elRow = elRow || this._addTrEl(allRecords[i]);
7295
 
                        nextSibling = elTbody.childNodes[i] || null;
7296
 
                        elTbody.insertBefore(elRow, nextSibling);
7297
 
                    }
7298
 
                    elTbody.style.display = "";
7299
 
                    
7300
 
                    // Set up for the next loop
7301
 
                    oArg.nCurrentRecord = i;
7302
 
                }
7303
 
            },
7304
 
            scope: this,
7305
 
            iterations: (loopN > 0) ? Math.ceil(nRecordsLength/loopN) : 1,
7306
 
            argument: {
7307
 
                nCurrentRecord: 0,//nRecordsLength-1,  // Start at first Record
7308
 
                nLoopLength: (loopN > 0) ? loopN : nRecordsLength
7309
 
            },
7310
 
            timeout: (loopN > 0) ? 0 : -1
7311
 
        });
7312
 
        
7313
 
        // Post-render tasks
7314
 
        this._oChainRender.add({
7315
 
            method: function(oArg) {
7316
 
                if((this instanceof DT) && this._sId) {
7317
 
                    while(elTbody.rows.length > nRecordsLength) {
7318
 
                        elTbody.removeChild(elTbody.lastChild);
7319
 
                    }
7320
 
                    this._setFirstRow();
7321
 
                    this._setLastRow();
7322
 
                    this._setRowStripes();
7323
 
                    this._setSelections();
7324
 
                }
7325
 
            },
7326
 
            scope: this,
7327
 
            timeout: (loopN > 0) ? 0 : -1
7328
 
        });
7329
 
     
7330
 
    }
7331
 
    // Table has no rows
7332
 
    else {
7333
 
        // Set up the loop Chain to delete rows
7334
 
        var nTotal = elTbody.rows.length;
7335
 
        if(nTotal > 0) {
7336
 
            this._oChainRender.add({
7337
 
                method: function(oArg) {
7338
 
                    if((this instanceof DT) && this._sId) {
7339
 
                        var i = oArg.nCurrent,
7340
 
                            loopN = oArg.nLoopLength,
7341
 
                            nIterEnd = (i - loopN < 0) ? -1 : i - loopN;
7342
 
    
7343
 
                        elTbody.style.display = "none";
7344
 
                        
7345
 
                        for(; i>nIterEnd; i--) {
7346
 
                            elTbody.deleteRow(-1);
7347
 
                        }
7348
 
                        elTbody.style.display = "";
7349
 
                        
7350
 
                        // Set up for the next loop
7351
 
                        oArg.nCurrent = i;
7352
 
                    }
7353
 
                },
7354
 
                scope: this,
7355
 
                iterations: (loopN > 0) ? Math.ceil(nTotal/loopN) : 1,
7356
 
                argument: {
7357
 
                    nCurrent: nTotal, 
7358
 
                    nLoopLength: (loopN > 0) ? loopN : nTotal
7359
 
                },
7360
 
                timeout: (loopN > 0) ? 0 : -1
7361
 
            });
7362
 
        }
7363
 
    }
7364
 
    this._runRenderChain();
7365
 
},
7366
 
 
7367
 
/**
7368
 
 * Disables DataTable UI.
7369
 
 *
7370
 
 * @method disable
7371
 
 */
7372
 
disable : function() {
7373
 
    var elTable = this._elTable;
7374
 
    var elMask = this._elMask;
7375
 
    elMask.style.width = elTable.offsetWidth + "px";
7376
 
    elMask.style.height = elTable.offsetHeight + "px";
7377
 
    elMask.style.display = "";
7378
 
    this.fireEvent("disableEvent");
7379
 
},
7380
 
 
7381
 
/**
7382
 
 * Undisables DataTable UI.
7383
 
 *
7384
 
 * @method undisable
7385
 
 */
7386
 
undisable : function() {
7387
 
    this._elMask.style.display = "none";
7388
 
    this.fireEvent("undisableEvent");
7389
 
},
7390
 
 
7391
 
/**
7392
 
 * Nulls out the entire DataTable instance and related objects, removes attached
7393
 
 * event listeners, and clears out DOM elements inside the container. After
7394
 
 * calling this method, the instance reference should be expliclitly nulled by
7395
 
 * implementer, as in myDataTable = null. Use with caution!
7396
 
 *
7397
 
 * @method destroy
7398
 
 */
7399
 
destroy : function() {
7400
 
    // Store for later
7401
 
    var instanceName = this.toString();
7402
 
 
7403
 
    this._oChainRender.stop();
7404
 
    
7405
 
    // Destroy static resizer proxy and column proxy
7406
 
    DT._destroyColumnDragTargetEl();
7407
 
    DT._destroyColumnResizerProxyEl();
7408
 
    
7409
 
    // Destroy ColumnDD and ColumnResizers
7410
 
    this._destroyColumnHelpers();
7411
 
    
7412
 
    // Destroy all CellEditors
7413
 
    var oCellEditor;
7414
 
    for(var i=0, len=this._oColumnSet.flat.length; i<len; i++) {
7415
 
        oCellEditor = this._oColumnSet.flat[i].editor;
7416
 
        if(oCellEditor && oCellEditor.destroy) {
7417
 
            oCellEditor.destroy();
7418
 
            this._oColumnSet.flat[i].editor = null;
7419
 
        }
7420
 
    }
7421
 
 
7422
 
    // Unhook custom events
7423
 
    this._oRecordSet.unsubscribeAll();
7424
 
    this.unsubscribeAll();
7425
 
 
7426
 
    // Unhook DOM events
7427
 
    Ev.removeListener(document, "click", this._onDocumentClick);
7428
 
    
7429
 
    // Clear out the container
7430
 
    this._destroyContainerEl(this._elContainer);
7431
 
 
7432
 
    // Null out objects
7433
 
    for(var param in this) {
7434
 
        if(lang.hasOwnProperty(this, param)) {
7435
 
            this[param] = null;
7436
 
        }
7437
 
    }
7438
 
    
7439
 
    // Clean up static values
7440
 
    DT._nCurrentCount--;
7441
 
    
7442
 
    if(DT._nCurrentCount < 1) {
7443
 
        if(DT._elDynStyleNode) {
7444
 
            document.getElementsByTagName('head')[0].removeChild(DT._elDynStyleNode);
7445
 
            DT._elDynStyleNode = null;
7446
 
        }
7447
 
    }
7448
 
 
7449
 
    YAHOO.log("DataTable instance destroyed: " + instanceName);
7450
 
},
7451
 
 
7452
 
/**
7453
 
 * Displays message within secondary TBODY.
7454
 
 *
7455
 
 * @method showTableMessage
7456
 
 * @param sHTML {String} (optional) Value for innerHTMlang.
7457
 
 * @param sClassName {String} (optional) Classname.
7458
 
 */
7459
 
showTableMessage : function(sHTML, sClassName) {
7460
 
    var elCell = this._elMsgTd;
7461
 
    if(lang.isString(sHTML)) {
7462
 
        elCell.firstChild.innerHTML = sHTML;
7463
 
    }
7464
 
    if(lang.isString(sClassName)) {
7465
 
        elCell.className = sClassName;
7466
 
    }
7467
 
 
7468
 
    this._elMsgTbody.style.display = "";
7469
 
 
7470
 
    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
7471
 
    YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
7472
 
},
7473
 
 
7474
 
/**
7475
 
 * Hides secondary TBODY.
7476
 
 *
7477
 
 * @method hideTableMessage
7478
 
 */
7479
 
hideTableMessage : function() {
7480
 
    if(this._elMsgTbody.style.display != "none") {
7481
 
        this._elMsgTbody.style.display = "none";
7482
 
        this._elMsgTbody.parentNode.style.width = "";
7483
 
        this.fireEvent("tableMsgHideEvent");
7484
 
        YAHOO.log("DataTable message hidden", "info", this.toString());
7485
 
    }
7486
 
},
7487
 
 
7488
 
/**
7489
 
 * Brings focus to the TBODY element. Alias to focusTbodyEl.
7490
 
 *
7491
 
 * @method focus
7492
 
 */
7493
 
focus : function() {
7494
 
    this.focusTbodyEl();
7495
 
},
7496
 
 
7497
 
/**
7498
 
 * Brings focus to the THEAD element.
7499
 
 *
7500
 
 * @method focusTheadEl
7501
 
 */
7502
 
focusTheadEl : function() {
7503
 
    this._focusEl(this._elThead);
7504
 
},
7505
 
 
7506
 
/**
7507
 
 * Brings focus to the TBODY element.
7508
 
 *
7509
 
 * @method focusTbodyEl
7510
 
 */
7511
 
focusTbodyEl : function() {
7512
 
    this._focusEl(this._elTbody);
7513
 
},
7514
 
 
7515
 
/**
7516
 
 * Setting display:none on DataTable or any parent may impact width validations.
7517
 
 * After setting display back to "", implementers should call this method to 
7518
 
 * manually perform those validations.
7519
 
 *
7520
 
 * @method onShow
7521
 
 */
7522
 
onShow : function() {
7523
 
    this.validateColumnWidths();
7524
 
    
7525
 
    for(var allKeys = this._oColumnSet.keys, i=0, len=allKeys.length, col; i<len; i++) {
7526
 
        col = allKeys[i];
7527
 
        if(col._ddResizer) {
7528
 
            col._ddResizer.resetResizerEl();
7529
 
        }
7530
 
    }
7531
 
},
7532
 
 
7533
 
 
7534
 
 
7535
 
 
7536
 
 
7537
 
 
7538
 
 
7539
 
 
7540
 
 
7541
 
 
7542
 
 
7543
 
 
7544
 
 
7545
 
 
7546
 
 
7547
 
 
7548
 
 
7549
 
 
7550
 
 
7551
 
 
7552
 
 
7553
 
 
7554
 
 
7555
 
 
7556
 
 
7557
 
 
7558
 
 
7559
 
 
7560
 
 
7561
 
 
7562
 
 
7563
 
 
7564
 
 
7565
 
 
7566
 
 
7567
 
 
7568
 
 
7569
 
 
7570
 
 
7571
 
 
7572
 
 
7573
 
 
7574
 
 
7575
 
 
7576
 
 
7577
 
 
7578
 
 
7579
 
 
7580
 
 
7581
 
 
7582
 
 
7583
 
 
7584
 
 
7585
 
 
7586
 
 
7587
 
 
7588
 
 
7589
 
 
7590
 
 
7591
 
 
7592
 
 
7593
 
 
7594
 
 
7595
 
 
7596
 
 
7597
 
 
7598
 
 
7599
 
// RECORDSET FUNCTIONS
7600
 
 
7601
 
/**
7602
 
 * Returns Record index for given TR element or page row index.
7603
 
 *
7604
 
 * @method getRecordIndex
7605
 
 * @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR
7606
 
 * element reference or page row index.
7607
 
 * @return {Number} Record's RecordSet index, or null.
7608
 
 */
7609
 
getRecordIndex : function(row) {
7610
 
    var nTrIndex;
7611
 
 
7612
 
    if(!lang.isNumber(row)) {
7613
 
        // By Record
7614
 
        if(row instanceof YAHOO.widget.Record) {
7615
 
            return this._oRecordSet.getRecordIndex(row);
7616
 
        }
7617
 
        // By element reference
7618
 
        else {
7619
 
            // Find the TR element
7620
 
            var el = this.getTrEl(row);
7621
 
            if(el) {
7622
 
                nTrIndex = el.sectionRowIndex;
7623
 
            }
7624
 
        }
7625
 
    }
7626
 
    // By page row index
7627
 
    else {
7628
 
        nTrIndex = row;
7629
 
    }
7630
 
 
7631
 
    if(lang.isNumber(nTrIndex)) {
7632
 
        var oPaginator = this.get("paginator");
7633
 
        if(oPaginator) {
7634
 
            return oPaginator.get('recordOffset') + nTrIndex;
7635
 
        }
7636
 
        else {
7637
 
            return nTrIndex;
7638
 
        }
7639
 
    }
7640
 
 
7641
 
    YAHOO.log("Could not get Record index for row " + row, "info", this.toString());
7642
 
    return null;
7643
 
},
7644
 
 
7645
 
/**
7646
 
 * For the given identifier, returns the associated Record instance.
7647
 
 *
7648
 
 * @method getRecord
7649
 
 * @param row {HTMLElement | Number | String} DOM reference to a TR element (or
7650
 
 * child of a TR element), RecordSet position index, or Record ID.
7651
 
 * @return {YAHOO.widget.Record} Record instance.
7652
 
 */
7653
 
getRecord : function(row) {
7654
 
    var oRecord = this._oRecordSet.getRecord(row);
7655
 
 
7656
 
    if(!oRecord) {
7657
 
        // Validate TR element
7658
 
        var elRow = this.getTrEl(row);
7659
 
        if(elRow) {
7660
 
            oRecord = this._oRecordSet.getRecord(this.getRecordIndex(elRow.sectionRowIndex));
7661
 
        }
7662
 
    }
7663
 
 
7664
 
    if(oRecord instanceof YAHOO.widget.Record) {
7665
 
        return this._oRecordSet.getRecord(oRecord);
7666
 
    }
7667
 
    else {
7668
 
        YAHOO.log("Could not get Record for row at " + row, "info", this.toString());
7669
 
        return null;
7670
 
    }
7671
 
},
7672
 
 
7673
 
 
7674
 
 
7675
 
 
7676
 
 
7677
 
 
7678
 
 
7679
 
 
7680
 
 
7681
 
 
7682
 
 
7683
 
 
7684
 
 
7685
 
 
7686
 
 
7687
 
 
7688
 
 
7689
 
 
7690
 
 
7691
 
 
7692
 
 
7693
 
 
7694
 
 
7695
 
 
7696
 
 
7697
 
 
7698
 
 
7699
 
 
7700
 
 
7701
 
 
7702
 
 
7703
 
 
7704
 
 
7705
 
 
7706
 
 
7707
 
 
7708
 
 
7709
 
 
7710
 
 
7711
 
 
7712
 
 
7713
 
 
7714
 
 
7715
 
 
7716
 
 
7717
 
 
7718
 
// COLUMN FUNCTIONS
7719
 
 
7720
 
/**
7721
 
 * For the given identifier, returns the associated Column instance. Note: For
7722
 
 * getting Columns by Column ID string, please use the method getColumnById().
7723
 
 *
7724
 
 * @method getColumn
7725
 
 * @param column {HTMLElement | String | Number} TH/TD element (or child of a
7726
 
 * TH/TD element), a Column key, or a ColumnSet key index.
7727
 
 * @return {YAHOO.widget.Column} Column instance.
7728
 
 */
7729
 
getColumn : function(column) {
7730
 
    var oColumn = this._oColumnSet.getColumn(column);
7731
 
 
7732
 
    if(!oColumn) {
7733
 
        // Validate TD element
7734
 
        var elCell = this.getTdEl(column);
7735
 
        if(elCell) {
7736
 
            oColumn = this._oColumnSet.getColumn(elCell.cellIndex);
7737
 
        }
7738
 
        // Validate TH element
7739
 
        else {
7740
 
            elCell = this.getThEl(column);
7741
 
            if(elCell) {
7742
 
                // Find by TH el ID
7743
 
                var allColumns = this._oColumnSet.flat;
7744
 
                for(var i=0, len=allColumns.length; i<len; i++) {
7745
 
                    if(allColumns[i].getThEl().id === elCell.id) {
7746
 
                        oColumn = allColumns[i];
7747
 
                    } 
7748
 
                }
7749
 
            }
7750
 
        }
7751
 
    }
7752
 
    if(!oColumn) {
7753
 
        YAHOO.log("Could not get Column for column at " + column, "info", this.toString());
7754
 
    }
7755
 
    return oColumn;
7756
 
},
7757
 
 
7758
 
/**
7759
 
 * For the given Column ID, returns the associated Column instance. Note: For
7760
 
 * getting Columns by key, please use the method getColumn().
7761
 
 *
7762
 
 * @method getColumnById
7763
 
 * @param column {String} Column ID string.
7764
 
 * @return {YAHOO.widget.Column} Column instance.
7765
 
 */
7766
 
getColumnById : function(column) {
7767
 
    return this._oColumnSet.getColumnById(column);
7768
 
},
7769
 
 
7770
 
/**
7771
 
 * For the given Column instance, returns next direction to sort.
7772
 
 *
7773
 
 * @method getColumnSortDir
7774
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
7775
 
 * @param oSortedBy {Object} (optional) Specify the state, or use current state. 
7776
 
 * @return {String} YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTableCLASS_DESC.
7777
 
 */
7778
 
getColumnSortDir : function(oColumn, oSortedBy) {
7779
 
    // Backward compatibility
7780
 
    if(oColumn.sortOptions && oColumn.sortOptions.defaultOrder) {
7781
 
        if(oColumn.sortOptions.defaultOrder == "asc") {
7782
 
            oColumn.sortOptions.defaultDir = DT.CLASS_ASC;
7783
 
        }
7784
 
        else if (oColumn.sortOptions.defaultOrder == "desc") {
7785
 
            oColumn.sortOptions.defaultDir = DT.CLASS_DESC;
7786
 
        }
7787
 
    }
7788
 
    
7789
 
    // What is the Column's default sort direction?
7790
 
    var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultDir) ? oColumn.sortOptions.defaultDir : DT.CLASS_ASC;
7791
 
 
7792
 
    // Is the Column currently sorted?
7793
 
    var bSorted = false;
7794
 
    oSortedBy = oSortedBy || this.get("sortedBy");
7795
 
    if(oSortedBy && (oSortedBy.key === oColumn.key)) {
7796
 
        bSorted = true;
7797
 
        if(oSortedBy.dir) {
7798
 
            sortDir = (oSortedBy.dir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
7799
 
        }
7800
 
        else {
7801
 
            sortDir = (sortDir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
7802
 
        }
7803
 
    }
7804
 
    return sortDir;
7805
 
},
7806
 
 
7807
 
/**
7808
 
 * Overridable method gives implementers a hook to show loading message before
7809
 
 * sorting Column.
7810
 
 *
7811
 
 * @method doBeforeSortColumn
7812
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
7813
 
 * @param sSortDir {String} YAHOO.widget.DataTable.CLASS_ASC or
7814
 
 * YAHOO.widget.DataTable.CLASS_DESC.
7815
 
 * @return {Boolean} Return true to continue sorting Column.
7816
 
 */
7817
 
doBeforeSortColumn : function(oColumn, sSortDir) {
7818
 
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
7819
 
    return true;
7820
 
},
7821
 
 
7822
 
/**
7823
 
 * Sorts given Column. If "dynamicData" is true, current selections are purged before
7824
 
 * a request is sent to the DataSource for data for the new state (using the
7825
 
 * request returned by "generateRequest()").
7826
 
 *
7827
 
 * @method sortColumn
7828
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
7829
 
 * @param sDir {String} (Optional) YAHOO.widget.DataTable.CLASS_ASC or
7830
 
 * YAHOO.widget.DataTable.CLASS_DESC
7831
 
 */
7832
 
sortColumn : function(oColumn, sDir) {
7833
 
    if(oColumn && (oColumn instanceof YAHOO.widget.Column)) {
7834
 
        if(!oColumn.sortable) {
7835
 
            Dom.addClass(this.getThEl(oColumn), DT.CLASS_SORTABLE);
7836
 
        }
7837
 
        
7838
 
        // Validate given direction
7839
 
        if(sDir && (sDir !== DT.CLASS_ASC) && (sDir !== DT.CLASS_DESC)) {
7840
 
            sDir = null;
7841
 
        }
7842
 
        
7843
 
        // Get the sort dir
7844
 
        var sSortDir = sDir || this.getColumnSortDir(oColumn);
7845
 
 
7846
 
        // Is the Column currently sorted?
7847
 
        var oSortedBy = this.get("sortedBy") || {};
7848
 
        var bSorted = (oSortedBy.key === oColumn.key) ? true : false;
7849
 
 
7850
 
        var ok = this.doBeforeSortColumn(oColumn, sSortDir);
7851
 
        if(ok) {
7852
 
            // Server-side sort
7853
 
            if(this.get("dynamicData")) {
7854
 
                // Get current state
7855
 
                var oState = this.getState();
7856
 
                
7857
 
                // Reset record offset, if paginated
7858
 
                if(oState.pagination) {
7859
 
                    oState.pagination.recordOffset = 0;
7860
 
                }
7861
 
                
7862
 
                // Update sortedBy to new values
7863
 
                oState.sortedBy = {
7864
 
                    key: oColumn.key,
7865
 
                    dir: sSortDir
7866
 
                };
7867
 
                
7868
 
                // Get the request for the new state
7869
 
                var request = this.get("generateRequest")(oState, this);
7870
 
 
7871
 
                // Purge selections
7872
 
                this.unselectAllRows();
7873
 
                this.unselectAllCells();
7874
 
 
7875
 
                // Send request for new data
7876
 
                var callback = {
7877
 
                    success : this.onDataReturnSetRows,
7878
 
                    failure : this.onDataReturnSetRows,
7879
 
                    argument : oState, // Pass along the new state to the callback
7880
 
                    scope : this
7881
 
                };
7882
 
                this._oDataSource.sendRequest(request, callback);            
7883
 
            }
7884
 
            // Client-side sort
7885
 
            else {
7886
 
                // Is there a custom sort handler function defined?
7887
 
                var sortFnc = (oColumn.sortOptions && lang.isFunction(oColumn.sortOptions.sortFunction)) ?
7888
 
                        // Custom sort function
7889
 
                        oColumn.sortOptions.sortFunction : null;
7890
 
                   
7891
 
                // Sort the Records
7892
 
                if(!bSorted || sDir || sortFnc) {
7893
 
                    // Get the field to sort
7894
 
                    var sField = (oColumn.sortOptions && oColumn.sortOptions.field) ? oColumn.sortOptions.field : oColumn.field;
7895
 
 
7896
 
                    // Default sort function if necessary
7897
 
                    sortFnc = sortFnc || 
7898
 
                        function(a, b, desc) {
7899
 
                            var sorted = YAHOO.util.Sort.compare(a.getData(sField),b.getData(sField), desc);
7900
 
                            if(sorted === 0) {
7901
 
                                return YAHOO.util.Sort.compare(a.getCount(),b.getCount(), desc); // Bug 1932978
7902
 
                            }
7903
 
                            else {
7904
 
                                return sorted;
7905
 
                            }
7906
 
                        };
7907
 
                    // Sort the Records        
7908
 
                    this._oRecordSet.sortRecords(sortFnc, ((sSortDir == DT.CLASS_DESC) ? true : false));
7909
 
                }
7910
 
                // Just reverse the Records
7911
 
                else {
7912
 
                    this._oRecordSet.reverseRecords();
7913
 
                }
7914
 
        
7915
 
                // Reset to first page if paginated
7916
 
                var oPaginator = this.get('paginator');
7917
 
                if (oPaginator) {
7918
 
                    // Set page silently, so as not to fire change event.
7919
 
                    oPaginator.setPage(1,true);
7920
 
                }
7921
 
        
7922
 
                // Update UI via sortedBy
7923
 
                this.render();
7924
 
                this.set("sortedBy", {key:oColumn.key, dir:sSortDir, column:oColumn}); 
7925
 
            }       
7926
 
            
7927
 
            this.fireEvent("columnSortEvent",{column:oColumn,dir:sSortDir});
7928
 
            YAHOO.log("Column \"" + oColumn.key + "\" sorted \"" + sSortDir + "\"", "info", this.toString());
7929
 
            return;
7930
 
        }
7931
 
    }
7932
 
    YAHOO.log("Could not sort Column \"" + oColumn.key + "\"", "warn", this.toString());
7933
 
},
7934
 
 
7935
 
/**
7936
 
 * Sets given Column to given pixel width. If new width is less than minimum
7937
 
 * width, sets to minimum width. Updates oColumn.width value.
7938
 
 *
7939
 
 * @method setColumnWidth
7940
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
7941
 
 * @param nWidth {Number} New width in pixels. A null value auto-sizes Column,
7942
 
 * subject to minWidth and maxAutoWidth validations. 
7943
 
 */
7944
 
setColumnWidth : function(oColumn, nWidth) {
7945
 
    if(!(oColumn instanceof YAHOO.widget.Column)) {
7946
 
        oColumn = this.getColumn(oColumn);
7947
 
    }
7948
 
    if(oColumn) {
7949
 
        // Validate new width against minimum width
7950
 
        if(lang.isNumber(nWidth)) {
7951
 
            // This is why we must require a Number... :-|
7952
 
            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
7953
 
 
7954
 
            // Save state
7955
 
            oColumn.width = nWidth;
7956
 
            
7957
 
            // Resize the DOM elements
7958
 
            this._setColumnWidth(oColumn, nWidth+"px");
7959
 
            
7960
 
            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
7961
 
            YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
7962
 
        }
7963
 
        // Unsets a width to auto-size
7964
 
        else if(nWidth === null) {
7965
 
            // Save state
7966
 
            oColumn.width = nWidth;
7967
 
            
7968
 
            // Resize the DOM elements
7969
 
            this._setColumnWidth(oColumn, "auto");
7970
 
            this.validateColumnWidths(oColumn);
7971
 
            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
7972
 
            YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
7973
 
        }
7974
 
                
7975
 
        // Bug 2339454: resize then sort misaligment
7976
 
        this._clearTrTemplateEl();
7977
 
    }
7978
 
    else {
7979
 
        YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
7980
 
    }
7981
 
},
7982
 
 
7983
 
/**
7984
 
 * Sets liner DIV elements of given Column to given width. When value should be
7985
 
 * auto-calculated to fit content overflow is set to visible, otherwise overflow
7986
 
 * is set to hidden. No validations against minimum width and no updating
7987
 
 * Column.width value.
7988
 
 *
7989
 
 * @method _setColumnWidth
7990
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
7991
 
 * @param sWidth {String} New width value.
7992
 
 * @param sOverflow {String} Should be "hidden" when Column width is explicitly
7993
 
 * being set to a value, but should be "visible" when Column is meant to auto-fit content.  
7994
 
 * @private
7995
 
 */
7996
 
_setColumnWidth : function(oColumn, sWidth, sOverflow) {
7997
 
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
7998
 
        sOverflow = sOverflow || (((sWidth === '') || (sWidth === 'auto')) ? 'visible' : 'hidden');
7999
 
    
8000
 
        // Dynamic style algorithm
8001
 
        if(!DT._bDynStylesFallback) {
8002
 
            this._setColumnWidthDynStyles(oColumn, sWidth, sOverflow);
8003
 
        }
8004
 
        // Dynamic function algorithm
8005
 
        else {
8006
 
            this._setColumnWidthDynFunction(oColumn, sWidth, sOverflow);
8007
 
        }
8008
 
    }
8009
 
    else {
8010
 
        YAHOO.log("Could not set width of unknown Column " + oColumn + " to " + sWidth, "warn", this.toString());
8011
 
    }
8012
 
},
8013
 
 
8014
 
/**
8015
 
 * Updates width of a Column's liner DIV elements by dynamically creating a
8016
 
 * STYLE node and writing and updating CSS style rules to it. If this fails during
8017
 
 * runtime, the fallback method _setColumnWidthDynFunction() will be called.
8018
 
 * Notes: This technique is not performant in IE6. IE7 crashes if DataTable is
8019
 
 * nested within another TABLE element. For these cases, it is recommended to
8020
 
 * use the method _setColumnWidthDynFunction by setting _bDynStylesFallback to TRUE.
8021
 
 *
8022
 
 * @method _setColumnWidthDynStyles
8023
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
8024
 
 * @param sWidth {String} New width value.
8025
 
 * @private
8026
 
 */
8027
 
_setColumnWidthDynStyles : function(oColumn, sWidth, sOverflow) {
8028
 
    var s = DT._elDynStyleNode,
8029
 
        rule;
8030
 
    
8031
 
    // Create a new STYLE node
8032
 
    if(!s) {
8033
 
        s = document.createElement('style');
8034
 
        s.type = 'text/css';
8035
 
        s = document.getElementsByTagName('head').item(0).appendChild(s);
8036
 
        DT._elDynStyleNode = s;
8037
 
    }
8038
 
    
8039
 
    // We have a STYLE node to update
8040
 
    if(s) {
8041
 
        // Use unique classname for this Column instance as a hook for resizing
8042
 
        var sClassname = "." + this.getId() + "-col-" + oColumn.getSanitizedKey() + " ." + DT.CLASS_LINER;
8043
 
        
8044
 
        // Hide for performance
8045
 
        if(this._elTbody) {
8046
 
            this._elTbody.style.display = 'none';
8047
 
        }
8048
 
        
8049
 
        rule = DT._oDynStyles[sClassname];
8050
 
 
8051
 
        // The Column does not yet have a rule
8052
 
        if(!rule) {
8053
 
            if(s.styleSheet && s.styleSheet.addRule) {
8054
 
                s.styleSheet.addRule(sClassname,"overflow:"+sOverflow);
8055
 
                s.styleSheet.addRule(sClassname,'width:'+sWidth);
8056
 
                rule = s.styleSheet.rules[s.styleSheet.rules.length-1];
8057
 
                DT._oDynStyles[sClassname] = rule;
8058
 
            }
8059
 
            else if(s.sheet && s.sheet.insertRule) {
8060
 
                s.sheet.insertRule(sClassname+" {overflow:"+sOverflow+";width:"+sWidth+";}",s.sheet.cssRules.length);
8061
 
                rule = s.sheet.cssRules[s.sheet.cssRules.length-1];
8062
 
                DT._oDynStyles[sClassname] = rule;
8063
 
            }
8064
 
        }
8065
 
        // We have a rule to update
8066
 
        else {
8067
 
            rule.style.overflow = sOverflow;
8068
 
            rule.style.width = sWidth;
8069
 
        } 
8070
 
        
8071
 
        // Unhide
8072
 
        if(this._elTbody) {
8073
 
            this._elTbody.style.display = '';
8074
 
        }
8075
 
    }
8076
 
    
8077
 
    // That was not a success, we must call the fallback routine
8078
 
    if(!rule) {
8079
 
        DT._bDynStylesFallback = true;
8080
 
        this._setColumnWidthDynFunction(oColumn, sWidth);
8081
 
    }
8082
 
},
8083
 
 
8084
 
/**
8085
 
 * Updates width of a Column's liner DIV elements by dynamically creating a
8086
 
 * function to update all element style properties in one pass. Note: This
8087
 
 * technique is not supported in sandboxed environments that prohibit EVALs.    
8088
 
 *
8089
 
 * @method _setColumnWidthDynFunction
8090
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
8091
 
 * @param sWidth {String} New width value.
8092
 
 * @private
8093
 
 */
8094
 
_setColumnWidthDynFunction : function(oColumn, sWidth, sOverflow) {
8095
 
    // TODO: why is this here?
8096
 
    if(sWidth == 'auto') {
8097
 
        sWidth = ''; 
8098
 
    }
8099
 
    
8100
 
    // Create one function for each value of rows.length
8101
 
    var rowslen = this._elTbody ? this._elTbody.rows.length : 0;
8102
 
    
8103
 
    // Dynamically create the function
8104
 
    if (!this._aDynFunctions[rowslen]) {
8105
 
        
8106
 
        //Compile a custom function to do all the liner div width
8107
 
        //assignments at the same time.  A unique function is required
8108
 
        //for each unique number of rows in _elTbody.  This will
8109
 
        //result in a function declaration like:
8110
 
        //function (oColumn,sWidth,sOverflow) {
8111
 
        //    var colIdx = oColumn.getKeyIndex();
8112
 
        //    oColumn.getThLinerEl().style.overflow =
8113
 
        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.overflow =
8114
 
        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.overflow =
8115
 
        //    ... (for all row indices in this._elTbody.rows.length - 1)
8116
 
        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.overflow =
8117
 
        //    sOverflow;
8118
 
        //    oColumn.getThLinerEl().style.width =
8119
 
        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.width =
8120
 
        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.width =
8121
 
        //    ... (for all row indices in this._elTbody.rows.length - 1)
8122
 
        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.width =
8123
 
        //    sWidth;
8124
 
        //}
8125
 
        
8126
 
        var i,j,k;
8127
 
        var resizerDef = [
8128
 
            'var colIdx=oColumn.getKeyIndex();',
8129
 
            'oColumn.getThLinerEl().style.overflow='
8130
 
        ];
8131
 
        for (i=rowslen-1, j=2; i >= 0; --i) {
8132
 
            resizerDef[j++] = 'this._elTbody.rows[';
8133
 
            resizerDef[j++] = i;
8134
 
            resizerDef[j++] = '].cells[colIdx].firstChild.style.overflow=';
8135
 
        }
8136
 
        resizerDef[j] = 'sOverflow;';
8137
 
        resizerDef[j+1] = 'oColumn.getThLinerEl().style.width=';
8138
 
        for (i=rowslen-1, k=j+2; i >= 0; --i) {
8139
 
            resizerDef[k++] = 'this._elTbody.rows[';
8140
 
            resizerDef[k++] = i;
8141
 
            resizerDef[k++] = '].cells[colIdx].firstChild.style.width=';
8142
 
        }
8143
 
        resizerDef[k] = 'sWidth;';
8144
 
        this._aDynFunctions[rowslen] =
8145
 
            new Function('oColumn','sWidth','sOverflow',resizerDef.join(''));
8146
 
    }
8147
 
    
8148
 
    // Get the function to execute
8149
 
    var resizerFn = this._aDynFunctions[rowslen];
8150
 
 
8151
 
    // TODO: Hide TBODY for performance in _setColumnWidthDynFunction?
8152
 
    if (resizerFn) {
8153
 
        resizerFn.call(this,oColumn,sWidth,sOverflow);
8154
 
    }
8155
 
},
8156
 
 
8157
 
/**
8158
 
 * For one or all Columns, when Column is not hidden, width is not set, and minWidth
8159
 
 * and/or maxAutoWidth is set, validates auto-width against minWidth and maxAutoWidth.
8160
 
 *
8161
 
 * @method validateColumnWidths
8162
 
 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
8163
 
 */
8164
 
validateColumnWidths : function(oColumn) {
8165
 
    var elColgroup = this._elColgroup;
8166
 
    var elColgroupClone = elColgroup.cloneNode(true);
8167
 
    var bNeedsValidation = false;
8168
 
    var allKeys = this._oColumnSet.keys;
8169
 
    var elThLiner;
8170
 
    // Validate just one Column's minWidth and/or maxAutoWidth
8171
 
    if(oColumn && !oColumn.hidden && !oColumn.width && (oColumn.getKeyIndex() !== null)) {
8172
 
            elThLiner = oColumn.getThLinerEl();
8173
 
            if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
8174
 
                elColgroupClone.childNodes[oColumn.getKeyIndex()].style.width = 
8175
 
                        oColumn.minWidth + 
8176
 
                        (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
8177
 
                        (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
8178
 
                bNeedsValidation = true;
8179
 
            }
8180
 
            else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
8181
 
                this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
8182
 
            }
8183
 
    }
8184
 
    // Validate all Columns
8185
 
    else {
8186
 
        for(var i=0, len=allKeys.length; i<len; i++) {
8187
 
            oColumn = allKeys[i];
8188
 
            if(!oColumn.hidden && !oColumn.width) {
8189
 
                elThLiner = oColumn.getThLinerEl();
8190
 
                if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
8191
 
                    elColgroupClone.childNodes[i].style.width = 
8192
 
                            oColumn.minWidth + 
8193
 
                            (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
8194
 
                            (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
8195
 
                    bNeedsValidation = true;
8196
 
                }
8197
 
                else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
8198
 
                    this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
8199
 
                }
8200
 
            }
8201
 
        }
8202
 
    }
8203
 
    if(bNeedsValidation) {
8204
 
        elColgroup.parentNode.replaceChild(elColgroupClone, elColgroup);
8205
 
        this._elColgroup = elColgroupClone;
8206
 
    }
8207
 
},
8208
 
 
8209
 
/**
8210
 
 * Clears minWidth.
8211
 
 *
8212
 
 * @method _clearMinWidth
8213
 
 * @param oColumn {YAHOO.widget.Column} Which Column.
8214
 
 * @private
8215
 
 */
8216
 
_clearMinWidth : function(oColumn) {
8217
 
    if(oColumn.getKeyIndex() !== null) {
8218
 
        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = '';
8219
 
    }
8220
 
},
8221
 
 
8222
 
/**
8223
 
 * Restores minWidth.
8224
 
 *
8225
 
 * @method _restoreMinWidth
8226
 
 * @param oColumn {YAHOO.widget.Column} Which Column.
8227
 
 * @private
8228
 
 */
8229
 
_restoreMinWidth : function(oColumn) {
8230
 
    if(oColumn.minWidth && (oColumn.getKeyIndex() !== null)) {
8231
 
        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = oColumn.minWidth + 'px';
8232
 
    }
8233
 
},
8234
 
 
8235
 
/**
8236
 
 * Hides given Column. NOTE: You cannot hide/show nested Columns. You can only
8237
 
 * hide/show non-nested Columns, and top-level parent Columns (which will
8238
 
 * hide/show all children Columns).
8239
 
 *
8240
 
 * @method hideColumn
8241
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
8242
 
 */
8243
 
hideColumn : function(oColumn) {
8244
 
    if(!(oColumn instanceof YAHOO.widget.Column)) {
8245
 
        oColumn = this.getColumn(oColumn);
8246
 
    }
8247
 
    // Only top-level Columns can get hidden due to issues in FF2 and SF3
8248
 
    if(oColumn && !oColumn.hidden && oColumn.getTreeIndex() !== null) {
8249
 
        
8250
 
        var allrows = this.getTbodyEl().rows;
8251
 
        var l = allrows.length;
8252
 
        var allDescendants = this._oColumnSet.getDescendants(oColumn);
8253
 
        
8254
 
        // Hide each nested Column
8255
 
        for(var i=0; i<allDescendants.length; i++) {
8256
 
            var thisColumn = allDescendants[i];
8257
 
            thisColumn.hidden = true;
8258
 
 
8259
 
            // Style the head cell
8260
 
            Dom.addClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
8261
 
            
8262
 
            // Does this Column have body cells?
8263
 
            var thisKeyIndex = thisColumn.getKeyIndex();
8264
 
            if(thisKeyIndex !== null) {                    
8265
 
                // Clear minWidth
8266
 
                this._clearMinWidth(oColumn);
8267
 
                
8268
 
                // Style the body cells
8269
 
                for(var j=0;j<l;j++) {
8270
 
                    Dom.addClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
8271
 
                }
8272
 
            }
8273
 
            
8274
 
            this.fireEvent("columnHideEvent",{column:thisColumn});
8275
 
            YAHOO.log("Column \"" + oColumn.key + "\" hidden", "info", this.toString());
8276
 
        }
8277
 
      
8278
 
        this._repaintOpera();
8279
 
        this._clearTrTemplateEl();
8280
 
    }
8281
 
    else {
8282
 
        YAHOO.log("Could not hide Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be hidden", "warn", this.toString());
8283
 
    }
8284
 
},
8285
 
 
8286
 
/**
8287
 
 * Shows given Column. NOTE: You cannot hide/show nested Columns. You can only
8288
 
 * hide/show non-nested Columns, and top-level parent Columns (which will
8289
 
 * hide/show all children Columns).
8290
 
 *
8291
 
 * @method showColumn
8292
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
8293
 
 */
8294
 
showColumn : function(oColumn) {
8295
 
    if(!(oColumn instanceof YAHOO.widget.Column)) {
8296
 
        oColumn = this.getColumn(oColumn);
8297
 
    }
8298
 
    // Only top-level Columns can get hidden
8299
 
    if(oColumn && oColumn.hidden && (oColumn.getTreeIndex() !== null)) {
8300
 
        var allrows = this.getTbodyEl().rows;
8301
 
        var l = allrows.length;
8302
 
        var allDescendants = this._oColumnSet.getDescendants(oColumn);
8303
 
        
8304
 
        // Show each nested Column
8305
 
        for(var i=0; i<allDescendants.length; i++) {
8306
 
            var thisColumn = allDescendants[i];
8307
 
            thisColumn.hidden = false;
8308
 
            
8309
 
            // Unstyle the head cell
8310
 
            Dom.removeClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
8311
 
 
8312
 
            // Does this Column have body cells?
8313
 
            var thisKeyIndex = thisColumn.getKeyIndex();
8314
 
            if(thisKeyIndex !== null) {
8315
 
                // Restore minWidth
8316
 
                this._restoreMinWidth(oColumn);
8317
 
                
8318
 
            
8319
 
                // Unstyle the body cells
8320
 
                for(var j=0;j<l;j++) {
8321
 
                    Dom.removeClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
8322
 
                }
8323
 
            }
8324
 
 
8325
 
            this.fireEvent("columnShowEvent",{column:thisColumn});
8326
 
            YAHOO.log("Column \"" + oColumn.key + "\" shown", "info", this.toString());
8327
 
        }
8328
 
        this._clearTrTemplateEl();
8329
 
    }
8330
 
    else {
8331
 
        YAHOO.log("Could not show Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be shown", "warn", this.toString());
8332
 
    }
8333
 
},
8334
 
 
8335
 
/**
8336
 
 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
8337
 
 * non-nested Columns, and top-level parent Columns (which will remove all
8338
 
 * children Columns).
8339
 
 *
8340
 
 * @method removeColumn
8341
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
8342
 
 * @return oColumn {YAHOO.widget.Column} Removed Column instance.
8343
 
 */
8344
 
removeColumn : function(oColumn) {
8345
 
    // Validate Column
8346
 
    if(!(oColumn instanceof YAHOO.widget.Column)) {
8347
 
        oColumn = this.getColumn(oColumn);
8348
 
    }
8349
 
    if(oColumn) {
8350
 
        var nColTreeIndex = oColumn.getTreeIndex();
8351
 
        if(nColTreeIndex !== null) {
8352
 
            // Which key index(es)
8353
 
            var i, len,
8354
 
                aKeyIndexes = oColumn.getKeyIndex();
8355
 
            // Must be a parent Column
8356
 
            if(aKeyIndexes === null) {
8357
 
                var descKeyIndexes = [];
8358
 
                var allDescendants = this._oColumnSet.getDescendants(oColumn);
8359
 
                for(i=0, len=allDescendants.length; i<len; i++) {
8360
 
                    // Is this descendant a key Column?
8361
 
                    var thisKey = allDescendants[i].getKeyIndex();
8362
 
                    if(thisKey !== null) {
8363
 
                        descKeyIndexes[descKeyIndexes.length] = thisKey;
8364
 
                    }
8365
 
                }
8366
 
                if(descKeyIndexes.length > 0) {
8367
 
                    aKeyIndexes = descKeyIndexes;
8368
 
                }
8369
 
            }
8370
 
            // Must be a key Column
8371
 
            else {
8372
 
                aKeyIndexes = [aKeyIndexes];
8373
 
            }
8374
 
            
8375
 
            if(aKeyIndexes !== null) {
8376
 
                // Sort the indexes so we can remove from the right
8377
 
                aKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
8378
 
                
8379
 
                // Destroy previous THEAD
8380
 
                this._destroyTheadEl();
8381
 
    
8382
 
                // Create new THEAD
8383
 
                var aOrigColumnDefs = this._oColumnSet.getDefinitions();
8384
 
                oColumn = aOrigColumnDefs.splice(nColTreeIndex,1)[0];
8385
 
                this._initColumnSet(aOrigColumnDefs);
8386
 
                this._initTheadEl();
8387
 
                
8388
 
                // Remove COL
8389
 
                for(i=aKeyIndexes.length-1; i>-1; i--) {
8390
 
                    this._removeColgroupColEl(aKeyIndexes[i]);
8391
 
                }
8392
 
                
8393
 
                // Remove TD
8394
 
                var allRows = this._elTbody.rows;
8395
 
                if(allRows.length > 0) {
8396
 
                    var loopN = this.get("renderLoopSize"),
8397
 
                        loopEnd = allRows.length;
8398
 
                    this._oChainRender.add({
8399
 
                        method: function(oArg) {
8400
 
                            if((this instanceof DT) && this._sId) {
8401
 
                                var i = oArg.nCurrentRow,
8402
 
                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
8403
 
                                    aIndexes = oArg.aIndexes,
8404
 
                                    j;
8405
 
                                for(; i < len; ++i) {
8406
 
                                    for(j = aIndexes.length-1; j>-1; j--) {
8407
 
                                        allRows[i].removeChild(allRows[i].childNodes[aIndexes[j]]);
8408
 
                                    }
8409
 
                                }
8410
 
                                oArg.nCurrentRow = i;
8411
 
                            }
8412
 
                        },
8413
 
                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
8414
 
                        argument: {nCurrentRow:0, aIndexes:aKeyIndexes},
8415
 
                        scope: this,
8416
 
                        timeout: (loopN > 0) ? 0 : -1
8417
 
                    });
8418
 
                    this._runRenderChain();
8419
 
                }
8420
 
        
8421
 
                this.fireEvent("columnRemoveEvent",{column:oColumn});
8422
 
                YAHOO.log("Column \"" + oColumn.key + "\" removed", "info", this.toString());
8423
 
                return oColumn;
8424
 
            }
8425
 
        }
8426
 
    }
8427
 
    YAHOO.log("Could not remove Column \"" + oColumn.key + "\". Only non-nested Columns can be removed", "warn", this.toString());
8428
 
},
8429
 
 
8430
 
/**
8431
 
 * Inserts given Column at the index if given, otherwise at the end. NOTE: You
8432
 
 * can only add non-nested Columns and top-level parent Columns. You cannot add
8433
 
 * a nested Column to an existing parent.
8434
 
 *
8435
 
 * @method insertColumn
8436
 
 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
8437
 
 * definition or a Column instance.
8438
 
 * @param index {Number} (optional) New tree index.
8439
 
 * @return oColumn {YAHOO.widget.Column} Inserted Column instance. 
8440
 
 */
8441
 
insertColumn : function(oColumn, index) {
8442
 
    // Validate Column
8443
 
    if(oColumn instanceof YAHOO.widget.Column) {
8444
 
        oColumn = oColumn.getDefinition();
8445
 
    }
8446
 
    else if(oColumn.constructor !== Object) {
8447
 
        YAHOO.log("Could not insert Column \"" + oColumn + "\" due to invalid argument", "warn", this.toString());
8448
 
        return;
8449
 
    }
8450
 
    
8451
 
    // Validate index or append new Column to the end of the ColumnSet
8452
 
    var oColumnSet = this._oColumnSet;
8453
 
    if(!lang.isValue(index) || !lang.isNumber(index)) {
8454
 
        index = oColumnSet.tree[0].length;
8455
 
    }
8456
 
    
8457
 
    // Destroy previous THEAD
8458
 
    this._destroyTheadEl();
8459
 
    
8460
 
    // Create new THEAD
8461
 
    var aNewColumnDefs = this._oColumnSet.getDefinitions();
8462
 
    aNewColumnDefs.splice(index, 0, oColumn);
8463
 
    this._initColumnSet(aNewColumnDefs);
8464
 
    this._initTheadEl();
8465
 
    
8466
 
    // Need to refresh the reference
8467
 
    oColumnSet = this._oColumnSet;
8468
 
    var oNewColumn = oColumnSet.tree[0][index];
8469
 
    
8470
 
    // Get key index(es) for new Column
8471
 
    var i, len,
8472
 
        descKeyIndexes = [];
8473
 
    var allDescendants = oColumnSet.getDescendants(oNewColumn);
8474
 
    for(i=0, len=allDescendants.length; i<len; i++) {
8475
 
        // Is this descendant a key Column?
8476
 
        var thisKey = allDescendants[i].getKeyIndex();
8477
 
        if(thisKey !== null) {
8478
 
            descKeyIndexes[descKeyIndexes.length] = thisKey;
8479
 
        }
8480
 
    }
8481
 
    
8482
 
    if(descKeyIndexes.length > 0) {  
8483
 
        // Sort the indexes
8484
 
        var newIndex = descKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
8485
 
        
8486
 
        // Add COL
8487
 
        for(i=descKeyIndexes.length-1; i>-1; i--) {
8488
 
            this._insertColgroupColEl(descKeyIndexes[i]);
8489
 
        }
8490
 
            
8491
 
        // Add TD
8492
 
        var allRows = this._elTbody.rows;
8493
 
        if(allRows.length > 0) {
8494
 
            var loopN = this.get("renderLoopSize"),
8495
 
                loopEnd = allRows.length;
8496
 
            
8497
 
            // Get templates for each new TD
8498
 
            var aTdTemplates = [],
8499
 
                elTdTemplate;
8500
 
            for(i=0, len=descKeyIndexes.length; i<len; i++) {
8501
 
                var thisKeyIndex = descKeyIndexes[i];
8502
 
                elTdTemplate = this._getTrTemplateEl().childNodes[i].cloneNode(true);
8503
 
                elTdTemplate = this._formatTdEl(this._oColumnSet.keys[thisKeyIndex], elTdTemplate, thisKeyIndex, (thisKeyIndex===this._oColumnSet.keys.length-1));
8504
 
                aTdTemplates[thisKeyIndex] = elTdTemplate;
8505
 
            }
8506
 
            
8507
 
            this._oChainRender.add({
8508
 
                method: function(oArg) {
8509
 
                    if((this instanceof DT) && this._sId) {
8510
 
                        var i = oArg.nCurrentRow, j,
8511
 
                            descKeyIndexes = oArg.descKeyIndexes,
8512
 
                            len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
8513
 
                            nextSibling;
8514
 
                        for(; i < len; ++i) {
8515
 
                            nextSibling = allRows[i].childNodes[newIndex] || null;
8516
 
                            for(j=descKeyIndexes.length-1; j>-1; j--) {
8517
 
                                allRows[i].insertBefore(oArg.aTdTemplates[descKeyIndexes[j]].cloneNode(true), nextSibling);
8518
 
                            }
8519
 
                        }
8520
 
                        oArg.nCurrentRow = i;
8521
 
                    }
8522
 
                },
8523
 
                iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
8524
 
                argument: {nCurrentRow:0,aTdTemplates:aTdTemplates,descKeyIndexes:descKeyIndexes},
8525
 
                scope: this,
8526
 
                timeout: (loopN > 0) ? 0 : -1
8527
 
            });
8528
 
            this._runRenderChain(); 
8529
 
        }
8530
 
 
8531
 
        this.fireEvent("columnInsertEvent",{column:oColumn,index:index});
8532
 
        YAHOO.log("Column \"" + oColumn.key + "\" inserted into index " + index, "info", this.toString());
8533
 
        return oNewColumn;
8534
 
    }
8535
 
},
8536
 
 
8537
 
/**
8538
 
 * Removes given Column and inserts into given tree index. NOTE: You
8539
 
 * can only reorder non-nested Columns and top-level parent Columns. You cannot
8540
 
 * reorder a nested Column to an existing parent.
8541
 
 *
8542
 
 * @method reorderColumn
8543
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
8544
 
 * @param index {Number} New tree index.
8545
 
 * @return oColumn {YAHOO.widget.Column} Reordered Column instance. 
8546
 
 */
8547
 
reorderColumn : function(oColumn, index) {
8548
 
    // Validate Column and new index
8549
 
    if(!(oColumn instanceof YAHOO.widget.Column)) {
8550
 
        oColumn = this.getColumn(oColumn);
8551
 
    }
8552
 
    if(oColumn && YAHOO.lang.isNumber(index)) {
8553
 
        var nOrigTreeIndex = oColumn.getTreeIndex();
8554
 
        if((nOrigTreeIndex !== null) && (nOrigTreeIndex !== index)) {
8555
 
            // Which key index(es)
8556
 
            var i, len,
8557
 
                aOrigKeyIndexes = oColumn.getKeyIndex(),
8558
 
                allDescendants,
8559
 
                descKeyIndexes = [],
8560
 
                thisKey;
8561
 
            // Must be a parent Column...
8562
 
            if(aOrigKeyIndexes === null) {
8563
 
                allDescendants = this._oColumnSet.getDescendants(oColumn);
8564
 
                for(i=0, len=allDescendants.length; i<len; i++) {
8565
 
                    // Is this descendant a key Column?
8566
 
                    thisKey = allDescendants[i].getKeyIndex();
8567
 
                    if(thisKey !== null) {
8568
 
                        descKeyIndexes[descKeyIndexes.length] = thisKey;
8569
 
                    }
8570
 
                }
8571
 
                if(descKeyIndexes.length > 0) {
8572
 
                    aOrigKeyIndexes = descKeyIndexes;
8573
 
                }
8574
 
            }
8575
 
            // ...or else must be a key Column
8576
 
            else {
8577
 
                aOrigKeyIndexes = [aOrigKeyIndexes];
8578
 
            }
8579
 
            
8580
 
            if(aOrigKeyIndexes !== null) {                   
8581
 
                // Sort the indexes
8582
 
                aOrigKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
8583
 
                
8584
 
                // Destroy previous THEAD
8585
 
                this._destroyTheadEl();
8586
 
    
8587
 
                // Create new THEAD
8588
 
                var aColumnDefs = this._oColumnSet.getDefinitions();
8589
 
                var oColumnDef = aColumnDefs.splice(nOrigTreeIndex,1)[0];
8590
 
                aColumnDefs.splice(index, 0, oColumnDef);
8591
 
                this._initColumnSet(aColumnDefs);
8592
 
                this._initTheadEl();
8593
 
                
8594
 
                // Need to refresh the reference
8595
 
                var oNewColumn = this._oColumnSet.tree[0][index];
8596
 
 
8597
 
                // What are new key index(es)
8598
 
                var aNewKeyIndexes = oNewColumn.getKeyIndex();
8599
 
                // Must be a parent Column
8600
 
                if(aNewKeyIndexes === null) {
8601
 
                    descKeyIndexes = [];
8602
 
                    allDescendants = this._oColumnSet.getDescendants(oNewColumn);
8603
 
                    for(i=0, len=allDescendants.length; i<len; i++) {
8604
 
                        // Is this descendant a key Column?
8605
 
                        thisKey = allDescendants[i].getKeyIndex();
8606
 
                        if(thisKey !== null) {
8607
 
                            descKeyIndexes[descKeyIndexes.length] = thisKey;
8608
 
                        }
8609
 
                    }
8610
 
                    if(descKeyIndexes.length > 0) {
8611
 
                        aNewKeyIndexes = descKeyIndexes;
8612
 
                    }
8613
 
                }
8614
 
                // Must be a key Column
8615
 
                else {
8616
 
                    aNewKeyIndexes = [aNewKeyIndexes];
8617
 
                }
8618
 
                
8619
 
                // Sort the new indexes and grab the first one for the new location
8620
 
                var newIndex = aNewKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
8621
 
 
8622
 
                // Reorder COL
8623
 
                this._reorderColgroupColEl(aOrigKeyIndexes, newIndex);
8624
 
                
8625
 
                // Reorder TD
8626
 
                var allRows = this._elTbody.rows;
8627
 
                if(allRows.length > 0) {
8628
 
                    var loopN = this.get("renderLoopSize"),
8629
 
                        loopEnd = allRows.length;
8630
 
                    this._oChainRender.add({
8631
 
                        method: function(oArg) {
8632
 
                            if((this instanceof DT) && this._sId) {
8633
 
                                var i = oArg.nCurrentRow, j, tmpTds, nextSibling,
8634
 
                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
8635
 
                                    aIndexes = oArg.aIndexes, thisTr;
8636
 
                                // For each row
8637
 
                                for(; i < len; ++i) {
8638
 
                                    tmpTds = [];
8639
 
                                    thisTr = allRows[i];
8640
 
                                    
8641
 
                                    // Remove each TD
8642
 
                                    for(j=aIndexes.length-1; j>-1; j--) {
8643
 
                                        tmpTds.push(thisTr.removeChild(thisTr.childNodes[aIndexes[j]]));
8644
 
                                    }
8645
 
                                    
8646
 
                                    // Insert each TD
8647
 
                                    nextSibling = thisTr.childNodes[newIndex] || null;
8648
 
                                    for(j=tmpTds.length-1; j>-1; j--) {
8649
 
                                        thisTr.insertBefore(tmpTds[j], nextSibling);
8650
 
                                    }                                    
8651
 
                                }
8652
 
                                oArg.nCurrentRow = i;
8653
 
                            }
8654
 
                        },
8655
 
                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
8656
 
                        argument: {nCurrentRow:0, aIndexes:aOrigKeyIndexes},
8657
 
                        scope: this,
8658
 
                        timeout: (loopN > 0) ? 0 : -1
8659
 
                    });
8660
 
                    this._runRenderChain();
8661
 
                }
8662
 
        
8663
 
                this.fireEvent("columnReorderEvent",{column:oNewColumn});
8664
 
                YAHOO.log("Column \"" + oNewColumn.key + "\" reordered", "info", this.toString());
8665
 
                return oNewColumn;
8666
 
            }
8667
 
        }
8668
 
    }
8669
 
    YAHOO.log("Could not reorder Column \"" + oColumn.key + "\". Only non-nested Columns can be reordered", "warn", this.toString());
8670
 
},
8671
 
 
8672
 
/**
8673
 
 * Selects given Column. NOTE: You cannot select/unselect nested Columns. You can only
8674
 
 * select/unselect non-nested Columns, and bottom-level key Columns.
8675
 
 *
8676
 
 * @method selectColumn
8677
 
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
8678
 
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8679
 
 */
8680
 
selectColumn : function(oColumn) {
8681
 
    oColumn = this.getColumn(oColumn);
8682
 
    if(oColumn && !oColumn.selected) {
8683
 
        // Only bottom-level Columns can get hidden
8684
 
        if(oColumn.getKeyIndex() !== null) {
8685
 
            oColumn.selected = true;
8686
 
            
8687
 
            // Update head cell
8688
 
            var elTh = oColumn.getThEl();
8689
 
            Dom.addClass(elTh,DT.CLASS_SELECTED);
8690
 
 
8691
 
            // Update body cells
8692
 
            var allRows = this.getTbodyEl().rows;
8693
 
            var oChainRender = this._oChainRender;
8694
 
            oChainRender.add({
8695
 
                method: function(oArg) {
8696
 
                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8697
 
                        Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);                    
8698
 
                    }
8699
 
                    oArg.rowIndex++;
8700
 
                },
8701
 
                scope: this,
8702
 
                iterations: allRows.length,
8703
 
                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
8704
 
            });
8705
 
 
8706
 
            this._clearTrTemplateEl();
8707
 
            
8708
 
            this._elTbody.style.display = "none";
8709
 
            this._runRenderChain();
8710
 
            this._elTbody.style.display = "";      
8711
 
            
8712
 
            this.fireEvent("columnSelectEvent",{column:oColumn});
8713
 
            YAHOO.log("Column \"" + oColumn.key + "\" selected", "info", this.toString());
8714
 
        }
8715
 
        else {
8716
 
            YAHOO.log("Could not select Column \"" + oColumn.key + "\". Only non-nested Columns can be selected", "warn", this.toString());
8717
 
        }
8718
 
    }
8719
 
},
8720
 
 
8721
 
/**
8722
 
 * Unselects given Column. NOTE: You cannot select/unselect nested Columns. You can only
8723
 
 * select/unselect non-nested Columns, and bottom-level key Columns.
8724
 
 *
8725
 
 * @method unselectColumn
8726
 
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
8727
 
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8728
 
 */
8729
 
unselectColumn : function(oColumn) {
8730
 
    oColumn = this.getColumn(oColumn);
8731
 
    if(oColumn && oColumn.selected) {
8732
 
        // Only bottom-level Columns can get hidden
8733
 
        if(oColumn.getKeyIndex() !== null) {
8734
 
            oColumn.selected = false;
8735
 
            
8736
 
            // Update head cell
8737
 
            var elTh = oColumn.getThEl();
8738
 
            Dom.removeClass(elTh,DT.CLASS_SELECTED);
8739
 
 
8740
 
            // Update body cells
8741
 
            var allRows = this.getTbodyEl().rows;
8742
 
            var oChainRender = this._oChainRender;
8743
 
            oChainRender.add({
8744
 
                method: function(oArg) {
8745
 
                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8746
 
                        Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED); 
8747
 
                    }                   
8748
 
                    oArg.rowIndex++;
8749
 
                },
8750
 
                scope: this,
8751
 
                iterations:allRows.length,
8752
 
                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
8753
 
            });
8754
 
            
8755
 
            this._clearTrTemplateEl();
8756
 
 
8757
 
            this._elTbody.style.display = "none";
8758
 
            this._runRenderChain();
8759
 
            this._elTbody.style.display = "";      
8760
 
            
8761
 
            this.fireEvent("columnUnselectEvent",{column:oColumn});
8762
 
            YAHOO.log("Column \"" + oColumn.key + "\" unselected", "info", this.toString());
8763
 
        }
8764
 
        else {
8765
 
            YAHOO.log("Could not unselect Column \"" + oColumn.key + "\". Only non-nested Columns can be unselected", "warn", this.toString());
8766
 
        }
8767
 
    }
8768
 
},
8769
 
 
8770
 
/**
8771
 
 * Returns an array selected Column instances.
8772
 
 *
8773
 
 * @method getSelectedColumns
8774
 
 * @return {YAHOO.widget.Column[]} Array of Column instances.
8775
 
 */
8776
 
getSelectedColumns : function(oColumn) {
8777
 
    var selectedColumns = [];
8778
 
    var aKeys = this._oColumnSet.keys;
8779
 
    for(var i=0,len=aKeys.length; i<len; i++) {
8780
 
        if(aKeys[i].selected) {
8781
 
            selectedColumns[selectedColumns.length] = aKeys[i];
8782
 
        }
8783
 
    }
8784
 
    return selectedColumns;
8785
 
},
8786
 
 
8787
 
/**
8788
 
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
8789
 
 * NOTE: You cannot highlight/unhighlight nested Columns. You can only
8790
 
 * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
8791
 
 *
8792
 
 * @method highlightColumn
8793
 
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
8794
 
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8795
 
 */
8796
 
highlightColumn : function(column) {
8797
 
    var oColumn = this.getColumn(column);
8798
 
    // Only bottom-level Columns can get highlighted
8799
 
    if(oColumn && (oColumn.getKeyIndex() !== null)) {            
8800
 
        // Update head cell
8801
 
        var elTh = oColumn.getThEl();
8802
 
        Dom.addClass(elTh,DT.CLASS_HIGHLIGHTED);
8803
 
 
8804
 
        // Update body cells
8805
 
        var allRows = this.getTbodyEl().rows;
8806
 
        var oChainRender = this._oChainRender;
8807
 
        oChainRender.add({
8808
 
            method: function(oArg) {
8809
 
                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8810
 
                    Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);   
8811
 
                }                 
8812
 
                oArg.rowIndex++;
8813
 
            },
8814
 
            scope: this,
8815
 
            iterations:allRows.length,
8816
 
            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
8817
 
            timeout: -1
8818
 
        });
8819
 
        this._elTbody.style.display = "none";
8820
 
        this._runRenderChain();
8821
 
        this._elTbody.style.display = "";      
8822
 
            
8823
 
        this.fireEvent("columnHighlightEvent",{column:oColumn});
8824
 
        YAHOO.log("Column \"" + oColumn.key + "\" highlighed", "info", this.toString());
8825
 
    }
8826
 
    else {
8827
 
        YAHOO.log("Could not highlight Column \"" + oColumn.key + "\". Only non-nested Columns can be highlighted", "warn", this.toString());
8828
 
    }
8829
 
},
8830
 
 
8831
 
/**
8832
 
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
8833
 
 * NOTE: You cannot highlight/unhighlight nested Columns. You can only
8834
 
 * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
8835
 
 *
8836
 
 * @method unhighlightColumn
8837
 
 * @param column {HTMLElement | String | Number} DOM reference or ID string to a
8838
 
 * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8839
 
 */
8840
 
unhighlightColumn : function(column) {
8841
 
    var oColumn = this.getColumn(column);
8842
 
    // Only bottom-level Columns can get highlighted
8843
 
    if(oColumn && (oColumn.getKeyIndex() !== null)) {
8844
 
        // Update head cell
8845
 
        var elTh = oColumn.getThEl();
8846
 
        Dom.removeClass(elTh,DT.CLASS_HIGHLIGHTED);
8847
 
 
8848
 
        // Update body cells
8849
 
        var allRows = this.getTbodyEl().rows;
8850
 
        var oChainRender = this._oChainRender;
8851
 
        oChainRender.add({
8852
 
            method: function(oArg) {
8853
 
                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8854
 
                    Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
8855
 
                }                 
8856
 
                oArg.rowIndex++;
8857
 
            },
8858
 
            scope: this,
8859
 
            iterations:allRows.length,
8860
 
            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
8861
 
            timeout: -1
8862
 
        });
8863
 
        this._elTbody.style.display = "none";
8864
 
        this._runRenderChain();
8865
 
        this._elTbody.style.display = "";     
8866
 
            
8867
 
        this.fireEvent("columnUnhighlightEvent",{column:oColumn});
8868
 
        YAHOO.log("Column \"" + oColumn.key + "\" unhighlighted", "info", this.toString());
8869
 
    }
8870
 
    else {
8871
 
        YAHOO.log("Could not unhighlight Column \"" + oColumn.key + "\". Only non-nested Columns can be unhighlighted", "warn", this.toString());
8872
 
    }
8873
 
},
8874
 
 
8875
 
 
8876
 
 
8877
 
 
8878
 
 
8879
 
 
8880
 
 
8881
 
 
8882
 
 
8883
 
 
8884
 
 
8885
 
 
8886
 
 
8887
 
 
8888
 
 
8889
 
 
8890
 
 
8891
 
 
8892
 
 
8893
 
 
8894
 
 
8895
 
 
8896
 
 
8897
 
 
8898
 
 
8899
 
 
8900
 
 
8901
 
 
8902
 
 
8903
 
 
8904
 
 
8905
 
 
8906
 
 
8907
 
 
8908
 
 
8909
 
 
8910
 
 
8911
 
 
8912
 
 
8913
 
 
8914
 
 
8915
 
 
8916
 
 
8917
 
 
8918
 
// ROW FUNCTIONS
8919
 
 
8920
 
/**
8921
 
 * Adds one new Record of data into the RecordSet at the index if given,
8922
 
 * otherwise at the end. If the new Record is in page view, the
8923
 
 * corresponding DOM elements are also updated.
8924
 
 *
8925
 
 * @method addRow
8926
 
 * @param oData {Object} Object literal of data for the row.
8927
 
 * @param index {Number} (optional) RecordSet position index at which to add data.
8928
 
 */
8929
 
addRow : function(oData, index) {
8930
 
    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
8931
 
        YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
8932
 
        return;
8933
 
    }
8934
 
 
8935
 
    if(oData && lang.isObject(oData)) {
8936
 
        var oRecord = this._oRecordSet.addRecord(oData, index);
8937
 
        if(oRecord) {
8938
 
            var recIndex;
8939
 
            var oPaginator = this.get('paginator');
8940
 
 
8941
 
            // Paginated
8942
 
            if (oPaginator) {     
8943
 
                // Update the paginator's totalRecords
8944
 
                var totalRecords = oPaginator.get('totalRecords');
8945
 
                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
8946
 
                    oPaginator.set('totalRecords',totalRecords + 1);
8947
 
                }
8948
 
 
8949
 
                recIndex = this.getRecordIndex(oRecord);
8950
 
                var endRecIndex = (oPaginator.getPageRecords())[1];
8951
 
 
8952
 
                // New record affects the view
8953
 
                if (recIndex <= endRecIndex) {
8954
 
                    // Defer UI updates to the render method
8955
 
                    this.render();
8956
 
                }
8957
 
                
8958
 
                this.fireEvent("rowAddEvent", {record:oRecord});
8959
 
                YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString()); 
8960
 
                return;
8961
 
            }
8962
 
            // Not paginated
8963
 
            else {
8964
 
                recIndex = this.getTrIndex(oRecord);
8965
 
                if(lang.isNumber(recIndex)) {
8966
 
                    // Add the TR element
8967
 
                    this._oChainRender.add({
8968
 
                        method: function(oArg) {
8969
 
                            if((this instanceof DT) && this._sId) {
8970
 
                                var oRecord = oArg.record;
8971
 
                                var recIndex = oArg.recIndex;
8972
 
                                var elNewTr = this._addTrEl(oRecord);
8973
 
                                if(elNewTr) {
8974
 
                                    var elNext = (this._elTbody.rows[recIndex]) ? this._elTbody.rows[recIndex] : null;
8975
 
                                    this._elTbody.insertBefore(elNewTr, elNext);
8976
 
 
8977
 
                                    // Set FIRST/LAST
8978
 
                                    if(recIndex === 0) {
8979
 
                                        this._setFirstRow();
8980
 
                                    }
8981
 
                                    if(elNext === null) {
8982
 
                                        this._setLastRow();
8983
 
                                    }
8984
 
                                    // Set EVEN/ODD
8985
 
                                    this._setRowStripes();                           
8986
 
                                    
8987
 
                                    this.hideTableMessage();
8988
 
            
8989
 
                                    this.fireEvent("rowAddEvent", {record:oRecord});
8990
 
                                    YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString());
8991
 
                                }
8992
 
                            }
8993
 
                        },
8994
 
                        argument: {record: oRecord, recIndex: recIndex},
8995
 
                        scope: this,
8996
 
                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
8997
 
                    });
8998
 
                    this._runRenderChain();
8999
 
                    return;
9000
 
                }
9001
 
            }            
9002
 
        }
9003
 
    }
9004
 
    YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
9005
 
},
9006
 
 
9007
 
/**
9008
 
 * Convenience method to add multiple rows.
9009
 
 *
9010
 
 * @method addRows
9011
 
 * @param aData {Object[]} Array of object literal data for the rows.
9012
 
 * @param index {Number} (optional) RecordSet position index at which to add data.
9013
 
 */
9014
 
addRows : function(aData, index) {
9015
 
    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
9016
 
        YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());    
9017
 
        return;
9018
 
    }
9019
 
 
9020
 
    if(lang.isArray(aData)) {
9021
 
        var aRecords = this._oRecordSet.addRecords(aData, index);
9022
 
        if(aRecords) {
9023
 
            var recIndex = this.getRecordIndex(aRecords[0]);
9024
 
            
9025
 
            // Paginated
9026
 
            var oPaginator = this.get('paginator');
9027
 
            if (oPaginator) {
9028
 
                // Update the paginator's totalRecords
9029
 
                var totalRecords = oPaginator.get('totalRecords');
9030
 
                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
9031
 
                    oPaginator.set('totalRecords',totalRecords + aRecords.length);
9032
 
                }
9033
 
    
9034
 
                var endRecIndex = (oPaginator.getPageRecords())[1];
9035
 
 
9036
 
                // At least one of the new records affects the view
9037
 
                if (recIndex <= endRecIndex) {
9038
 
                    this.render();
9039
 
                }
9040
 
                
9041
 
                this.fireEvent("rowsAddEvent", {records:aRecords});
9042
 
                YAHOO.log("Added " + aRecords.length + 
9043
 
                        " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
9044
 
                        " with data " + lang.dump(aData), "info", this.toString());
9045
 
                return;
9046
 
            }
9047
 
            // Not paginated
9048
 
            else {
9049
 
                // Add the TR elements
9050
 
                var loopN = this.get("renderLoopSize");
9051
 
                var loopEnd = recIndex + aData.length;
9052
 
                var nRowsNeeded = (loopEnd - recIndex); // how many needed
9053
 
                var isLast = (recIndex >= this._elTbody.rows.length);
9054
 
                this._oChainRender.add({
9055
 
                    method: function(oArg) {
9056
 
                        if((this instanceof DT) && this._sId) {
9057
 
                            var aRecords = oArg.aRecords,
9058
 
                                i = oArg.nCurrentRow,
9059
 
                                j = oArg.nCurrentRecord,
9060
 
                                len = loopN > 0 ? Math.min(i + loopN,loopEnd) : loopEnd,
9061
 
                                df = document.createDocumentFragment(),
9062
 
                                elNext = (this._elTbody.rows[i]) ? this._elTbody.rows[i] : null;
9063
 
                            for(; i < len; i++, j++) {
9064
 
                                df.appendChild(this._addTrEl(aRecords[j]));
9065
 
                            }
9066
 
                            this._elTbody.insertBefore(df, elNext);
9067
 
                            oArg.nCurrentRow = i;
9068
 
                            oArg.nCurrentRecord = j;
9069
 
                        }
9070
 
                    },
9071
 
                    iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
9072
 
                    argument: {nCurrentRow:recIndex,nCurrentRecord:0,aRecords:aRecords},
9073
 
                    scope: this,
9074
 
                    timeout: (loopN > 0) ? 0 : -1
9075
 
                });
9076
 
                this._oChainRender.add({
9077
 
                    method: function(oArg) {
9078
 
                        var recIndex = oArg.recIndex;
9079
 
                        // Set FIRST/LAST
9080
 
                        if(recIndex === 0) {
9081
 
                            this._setFirstRow();
9082
 
                        }
9083
 
                        if(oArg.isLast) {
9084
 
                            this._setLastRow();
9085
 
                        }
9086
 
                        // Set EVEN/ODD
9087
 
                        this._setRowStripes();                           
9088
 
 
9089
 
                        this.fireEvent("rowsAddEvent", {records:aRecords});
9090
 
                        YAHOO.log("Added " + aRecords.length + 
9091
 
                                " rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
9092
 
                                " with data " + lang.dump(aData), "info", this.toString());
9093
 
                    },
9094
 
                    argument: {recIndex: recIndex, isLast: isLast},
9095
 
                    scope: this,
9096
 
                    timeout: -1 // Needs to run immediately after the DOM insertions above
9097
 
                });
9098
 
                this._runRenderChain();
9099
 
                this.hideTableMessage();                
9100
 
                return;
9101
 
            }            
9102
 
        }
9103
 
    }
9104
 
    YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());    
9105
 
},
9106
 
 
9107
 
/**
9108
 
 * For the given row, updates the associated Record with the given data. If the
9109
 
 * row is on current page, the corresponding DOM elements are also updated.
9110
 
 *
9111
 
 * @method updateRow
9112
 
 * @param row {YAHOO.widget.Record | Number | HTMLElement | String}
9113
 
 * Which row to update: By Record instance, by Record's RecordSet
9114
 
 * position index, by HTMLElement reference to the TR element, or by ID string
9115
 
 * of the TR element.
9116
 
 * @param oData {Object} Object literal of data for the row.
9117
 
 */
9118
 
updateRow : function(row, oData) {
9119
 
    var index = row;
9120
 
    if (!lang.isNumber(index)) {
9121
 
        index = this.getRecordIndex(row);
9122
 
    }
9123
 
 
9124
 
    // Update the Record
9125
 
    if(lang.isNumber(index) && (index >= 0)) {
9126
 
        var oRecordSet = this._oRecordSet,
9127
 
            oldRecord = oRecordSet.getRecord(index);
9128
 
            
9129
 
        
9130
 
        if(oldRecord) {
9131
 
            var updatedRecord = this._oRecordSet.setRecord(oData, index),
9132
 
                elRow = this.getTrEl(oldRecord),
9133
 
                // Copy data from the Record for the event that gets fired later
9134
 
                oldData = oldRecord ? oldRecord.getData() : null;
9135
 
               
9136
 
            if(updatedRecord) {
9137
 
                // Update selected rows as necessary
9138
 
                var tracker = this._aSelections || [],
9139
 
                i=0,
9140
 
                oldId = oldRecord.getId(),
9141
 
                newId = updatedRecord.getId();
9142
 
                for(; i<tracker.length; i++) {
9143
 
                    if((tracker[i] === oldId)) {
9144
 
                        tracker[i] = newId;
9145
 
                    }
9146
 
                    else if(tracker[i].recordId === oldId) {
9147
 
                        tracker[i].recordId = newId;
9148
 
                    }
9149
 
                }
9150
 
 
9151
 
                // Update the TR only if row is on current page
9152
 
                this._oChainRender.add({
9153
 
                    method: function() {
9154
 
                        if((this instanceof DT) && this._sId) {
9155
 
                            // Paginated
9156
 
                            var oPaginator = this.get('paginator');
9157
 
                            if (oPaginator) {
9158
 
                                var pageStartIndex = (oPaginator.getPageRecords())[0],
9159
 
                                    pageLastIndex = (oPaginator.getPageRecords())[1];
9160
 
        
9161
 
                                // At least one of the new records affects the view
9162
 
                                if ((index >= pageStartIndex) || (index <= pageLastIndex)) {
9163
 
                                    this.render();
9164
 
                                }
9165
 
                            }
9166
 
                            else {
9167
 
                                if(elRow) {
9168
 
                                    this._updateTrEl(elRow, updatedRecord);
9169
 
                                }
9170
 
                                else {
9171
 
                                    this.getTbodyEl().appendChild(this._addTrEl(updatedRecord));
9172
 
                                }
9173
 
                            }
9174
 
                            this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData});
9175
 
                            YAHOO.log("DataTable row updated: Record ID = " + updatedRecord.getId() +
9176
 
                                    ", Record index = " + this.getRecordIndex(updatedRecord) +
9177
 
                                    ", page row index = " + this.getTrIndex(updatedRecord), "info", this.toString());
9178
 
                        }
9179
 
                    },
9180
 
                    scope: this,
9181
 
                    timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9182
 
                });
9183
 
                this._runRenderChain();
9184
 
                return;
9185
 
            }
9186
 
        }
9187
 
    }
9188
 
    YAHOO.log("Could not update row " + row + " with the data : " + lang.dump(oData), "warn", this.toString());
9189
 
    return;
9190
 
},
9191
 
 
9192
 
/**
9193
 
 * Starting with the given row, updates associated Records with the given data.
9194
 
 * The number of rows to update are determined by the array of data provided.
9195
 
 * Undefined data (i.e., not an object literal) causes a row to be skipped. If
9196
 
 * any of the rows are on current page, the corresponding DOM elements are also
9197
 
 * updated.
9198
 
 *
9199
 
 * @method updateRows
9200
 
 * @param startrow {YAHOO.widget.Record | Number | HTMLElement | String}
9201
 
 * Starting row to update: By Record instance, by Record's RecordSet
9202
 
 * position index, by HTMLElement reference to the TR element, or by ID string
9203
 
 * of the TR element.
9204
 
 * @param aData {Object[]} Array of object literal of data for the rows.
9205
 
 */
9206
 
updateRows : function(startrow, aData) {
9207
 
    if(lang.isArray(aData)) {
9208
 
        var startIndex = startrow,
9209
 
            oRecordSet = this._oRecordSet;
9210
 
            
9211
 
        if (!lang.isNumber(startrow)) {
9212
 
            startIndex = this.getRecordIndex(startrow);
9213
 
        }
9214
 
            
9215
 
        if(lang.isNumber(startIndex) && (startIndex >= 0) && (startIndex < oRecordSet.getLength())) {
9216
 
            var lastIndex = startIndex + aData.length,
9217
 
                aOldRecords = oRecordSet.getRecords(startIndex, aData.length),
9218
 
                aNewRecords = oRecordSet.setRecords(aData, startIndex);
9219
 
            if(aNewRecords) {
9220
 
                // Update selected rows as necessary
9221
 
                var tracker = this._aSelections || [],
9222
 
                    i=0, j, newId, oldId;
9223
 
                for(; i<tracker.length; i++) {
9224
 
                    for(j=0; j<aOldRecords.length; j++) {
9225
 
                        oldId = aOldRecords[j].getId();
9226
 
                        if((tracker[i] === oldId)) {
9227
 
                            tracker[i] = aNewRecords[j].getId();
9228
 
                        }
9229
 
                        else if(tracker[i].recordId === oldId) {
9230
 
                            tracker[i].recordId = aNewRecords[j].getId();
9231
 
                        }
9232
 
                    }
9233
 
                }
9234
 
            
9235
 
                // Paginated
9236
 
                var oPaginator = this.get('paginator');
9237
 
                if (oPaginator) {
9238
 
                    var pageStartIndex = (oPaginator.getPageRecords())[0],
9239
 
                        pageLastIndex = (oPaginator.getPageRecords())[1];
9240
 
    
9241
 
                    // At least one of the new records affects the view
9242
 
                    if ((startIndex >= pageStartIndex) || (lastIndex <= pageLastIndex)) {
9243
 
                        this.render();
9244
 
                    }
9245
 
                    
9246
 
                    this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
9247
 
                    YAHOO.log("Added " + aNewRecords.length + 
9248
 
                            " rows starting at index " + startIndex +
9249
 
                            " with data " + lang.dump(aData), "info", this.toString());
9250
 
                    return;
9251
 
                }
9252
 
                // Not paginated
9253
 
                else {
9254
 
                    // Update the TR elements
9255
 
                    var loopN = this.get("renderLoopSize"),
9256
 
                        rowCount = aData.length, // how many needed
9257
 
                        lastRowIndex = this._elTbody.rows.length,
9258
 
                        isLast = (lastIndex >= lastRowIndex),
9259
 
                        isAdding = (lastIndex > lastRowIndex);
9260
 
                                           
9261
 
                    this._oChainRender.add({
9262
 
                        method: function(oArg) {
9263
 
                            if((this instanceof DT) && this._sId) {
9264
 
                                var aRecords = oArg.aRecords,
9265
 
                                    i = oArg.nCurrentRow,
9266
 
                                    j = oArg.nDataPointer,
9267
 
                                    len = loopN > 0 ? Math.min(i+loopN, startIndex+aRecords.length) : startIndex+aRecords.length;
9268
 
                                    
9269
 
                                for(; i < len; i++,j++) {
9270
 
                                    if(isAdding && (i>=lastRowIndex)) {
9271
 
                                        this._elTbody.appendChild(this._addTrEl(aRecords[j]));
9272
 
                                    }
9273
 
                                    else {
9274
 
                                        this._updateTrEl(this._elTbody.rows[i], aRecords[j]);
9275
 
                                    }
9276
 
                                }
9277
 
                                oArg.nCurrentRow = i;
9278
 
                                oArg.nDataPointer = j;
9279
 
                            }
9280
 
                        },
9281
 
                        iterations: (loopN > 0) ? Math.ceil(rowCount/loopN) : 1,
9282
 
                        argument: {nCurrentRow:startIndex,aRecords:aNewRecords,nDataPointer:0,isAdding:isAdding},
9283
 
                        scope: this,
9284
 
                        timeout: (loopN > 0) ? 0 : -1
9285
 
                    });
9286
 
                    this._oChainRender.add({
9287
 
                        method: function(oArg) {
9288
 
                            var recIndex = oArg.recIndex;
9289
 
                            // Set FIRST/LAST
9290
 
                            if(recIndex === 0) {
9291
 
                                this._setFirstRow();
9292
 
                            }
9293
 
                            if(oArg.isLast) {
9294
 
                                this._setLastRow();
9295
 
                            }
9296
 
                            // Set EVEN/ODD
9297
 
                            this._setRowStripes();                           
9298
 
    
9299
 
                            this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
9300
 
                            YAHOO.log("Added " + aNewRecords.length + 
9301
 
                                    " rows starting at index " + startIndex +
9302
 
                                    " with data " + lang.dump(aData), "info", this.toString());
9303
 
                        },
9304
 
                        argument: {recIndex: startIndex, isLast: isLast},
9305
 
                        scope: this,
9306
 
                        timeout: -1 // Needs to run immediately after the DOM insertions above
9307
 
                    });
9308
 
                    this._runRenderChain();
9309
 
                    this.hideTableMessage();                
9310
 
                    return;
9311
 
                }            
9312
 
            }
9313
 
        }
9314
 
    }
9315
 
    YAHOO.log("Could not update rows at " + startrow + " with " + lang.dump(aData), "warn", this.toString());
9316
 
},
9317
 
 
9318
 
/**
9319
 
 * Deletes the given row's Record from the RecordSet. If the row is on current page,
9320
 
 * the corresponding DOM elements are also deleted.
9321
 
 *
9322
 
 * @method deleteRow
9323
 
 * @param row {HTMLElement | String | Number} DOM element reference or ID string
9324
 
 * to DataTable page element or RecordSet index.
9325
 
 */
9326
 
deleteRow : function(row) {
9327
 
    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
9328
 
    if(lang.isNumber(nRecordIndex)) {
9329
 
        var oRecord = this.getRecord(nRecordIndex);
9330
 
        if(oRecord) {
9331
 
            var nTrIndex = this.getTrIndex(nRecordIndex);
9332
 
            
9333
 
            // Remove from selection tracker if there
9334
 
            var sRecordId = oRecord.getId();
9335
 
            var tracker = this._aSelections || [];
9336
 
            for(var j=tracker.length-1; j>-1; j--) {
9337
 
                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
9338
 
                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
9339
 
                    tracker.splice(j,1);
9340
 
                }
9341
 
            }
9342
 
    
9343
 
            // Delete Record from RecordSet
9344
 
            var oData = this._oRecordSet.deleteRecord(nRecordIndex);
9345
 
    
9346
 
            // Update the UI
9347
 
            if(oData) {
9348
 
                // If paginated and the deleted row was on this or a prior page, just
9349
 
                // re-render
9350
 
                var oPaginator = this.get('paginator');
9351
 
                if (oPaginator) {
9352
 
                    // Update the paginator's totalRecords
9353
 
                    var totalRecords = oPaginator.get('totalRecords'),
9354
 
                        // must capture before the totalRecords change because
9355
 
                        // Paginator shifts to previous page automatically
9356
 
                        rng = oPaginator.getPageRecords();
9357
 
 
9358
 
                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
9359
 
                        oPaginator.set('totalRecords',totalRecords - 1);
9360
 
                    }
9361
 
    
9362
 
                    // The deleted record was on this or a prior page, re-render
9363
 
                    if (!rng || nRecordIndex <= rng[1]) {
9364
 
                        this.render();
9365
 
                    }
9366
 
 
9367
 
                    this._oChainRender.add({
9368
 
                        method: function() {
9369
 
                            if((this instanceof DT) && this._sId) {
9370
 
                                this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex, oldData:oData, trElIndex:nTrIndex});
9371
 
                                YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());     
9372
 
                            }
9373
 
                        },
9374
 
                        scope: this,
9375
 
                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9376
 
                    });
9377
 
                    this._runRenderChain();
9378
 
                }
9379
 
                // Not paginated
9380
 
                else {
9381
 
                    if(lang.isNumber(nTrIndex)) {
9382
 
                        this._oChainRender.add({
9383
 
                            method: function() {
9384
 
                                if((this instanceof DT) && this._sId) {
9385
 
                                    var isLast = (nTrIndex == this.getLastTrEl().sectionRowIndex);
9386
 
                                    this._deleteTrEl(nTrIndex);
9387
 
                    
9388
 
                                    // Post-delete tasks
9389
 
                                    if(this._elTbody.rows.length > 0) {
9390
 
                                        // Set FIRST/LAST
9391
 
                                        if(nTrIndex === 0) {
9392
 
                                            this._setFirstRow();
9393
 
                                        }
9394
 
                                        if(isLast) {
9395
 
                                            this._setLastRow();
9396
 
                                        }
9397
 
                                        // Set EVEN/ODD
9398
 
                                        if(nTrIndex != this._elTbody.rows.length) {
9399
 
                                            this._setRowStripes(nTrIndex);
9400
 
                                        }                                
9401
 
                                    }
9402
 
                    
9403
 
                                    this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,oldData:oData, trElIndex:nTrIndex});
9404
 
                                    YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());     
9405
 
                                }
9406
 
                            },
9407
 
                            scope: this,
9408
 
                            timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9409
 
                        });
9410
 
                        this._runRenderChain();
9411
 
                        return;
9412
 
                    }
9413
 
                }
9414
 
            }
9415
 
        }
9416
 
    }
9417
 
    YAHOO.log("Could not delete row: " + row, "warn", this.toString());
9418
 
    return null;
9419
 
},
9420
 
 
9421
 
/**
9422
 
 * Convenience method to delete multiple rows.
9423
 
 *
9424
 
 * @method deleteRows
9425
 
 * @param row {HTMLElement | String | Number} DOM element reference or ID string
9426
 
 * to DataTable page element or RecordSet index.
9427
 
 * @param count {Number} (optional) How many rows to delete. A negative value
9428
 
 * will delete towards the beginning.
9429
 
 */
9430
 
deleteRows : function(row, count) {
9431
 
    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
9432
 
    if(lang.isNumber(nRecordIndex)) {
9433
 
        var oRecord = this.getRecord(nRecordIndex);
9434
 
        if(oRecord) {
9435
 
            var nTrIndex = this.getTrIndex(nRecordIndex);
9436
 
            
9437
 
            // Remove from selection tracker if there
9438
 
            var sRecordId = oRecord.getId();
9439
 
            var tracker = this._aSelections || [];
9440
 
            for(var j=tracker.length-1; j>-1; j--) {
9441
 
                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
9442
 
                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
9443
 
                    tracker.splice(j,1);
9444
 
                }
9445
 
            }
9446
 
    
9447
 
            // Delete Record from RecordSet
9448
 
            var highIndex = nRecordIndex;
9449
 
            var lowIndex = nRecordIndex;
9450
 
        
9451
 
            // Validate count and account for negative value
9452
 
            if(count && lang.isNumber(count)) {
9453
 
                highIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex;
9454
 
                lowIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1;
9455
 
                count = (count > 0) ? count : count*-1;
9456
 
                if(lowIndex < 0) {
9457
 
                    lowIndex = 0;
9458
 
                    count = highIndex - lowIndex + 1;
9459
 
                }
9460
 
            }
9461
 
            else {
9462
 
                count = 1;
9463
 
            }
9464
 
            
9465
 
            var aData = this._oRecordSet.deleteRecords(lowIndex, count);
9466
 
    
9467
 
            // Update the UI
9468
 
            if(aData) {
9469
 
                var oPaginator = this.get('paginator'),
9470
 
                    loopN = this.get("renderLoopSize");
9471
 
                // If paginated and the deleted row was on this or a prior page, just
9472
 
                // re-render
9473
 
                if (oPaginator) {
9474
 
                    // Update the paginator's totalRecords
9475
 
                    var totalRecords = oPaginator.get('totalRecords'),
9476
 
                        // must capture before the totalRecords change because
9477
 
                        // Paginator shifts to previous page automatically
9478
 
                        rng = oPaginator.getPageRecords();
9479
 
 
9480
 
                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
9481
 
                        oPaginator.set('totalRecords',totalRecords - aData.length);
9482
 
                    }
9483
 
    
9484
 
                    // The records were on this or a prior page, re-render
9485
 
                    if (!rng || lowIndex <= rng[1]) {
9486
 
                        this.render();
9487
 
                    }
9488
 
 
9489
 
                    this._oChainRender.add({
9490
 
                        method: function(oArg) {
9491
 
                            if((this instanceof DT) && this._sId) {
9492
 
                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
9493
 
                                YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
9494
 
                            }
9495
 
                        },
9496
 
                        scope: this,
9497
 
                        timeout: (loopN > 0) ? 0 : -1
9498
 
                    });
9499
 
                    this._runRenderChain();
9500
 
                    return;
9501
 
                }
9502
 
                // Not paginated
9503
 
                else {
9504
 
                    if(lang.isNumber(nTrIndex)) {
9505
 
                        // Delete the TR elements starting with highest index
9506
 
                        var loopEnd = lowIndex;
9507
 
                        var nRowsNeeded = count; // how many needed
9508
 
                        this._oChainRender.add({
9509
 
                            method: function(oArg) {
9510
 
                                if((this instanceof DT) && this._sId) {
9511
 
                                    var i = oArg.nCurrentRow,
9512
 
                                        len = (loopN > 0) ? (Math.max(i - loopN,loopEnd)-1) : loopEnd-1;
9513
 
                                    for(; i>len; --i) {
9514
 
                                        this._deleteTrEl(i);
9515
 
                                    }
9516
 
                                    oArg.nCurrentRow = i;
9517
 
                                }
9518
 
                            },
9519
 
                            iterations: (loopN > 0) ? Math.ceil(count/loopN) : 1,
9520
 
                            argument: {nCurrentRow:highIndex},
9521
 
                            scope: this,
9522
 
                            timeout: (loopN > 0) ? 0 : -1
9523
 
                        });
9524
 
                        this._oChainRender.add({
9525
 
                            method: function() {    
9526
 
                                // Post-delete tasks
9527
 
                                if(this._elTbody.rows.length > 0) {
9528
 
                                    this._setFirstRow();
9529
 
                                    this._setLastRow();
9530
 
                                    this._setRowStripes();
9531
 
                                }
9532
 
                                
9533
 
                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
9534
 
                                YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
9535
 
                            },
9536
 
                            scope: this,
9537
 
                            timeout: -1 // Needs to run immediately after the DOM deletions above
9538
 
                        });
9539
 
                        this._runRenderChain();
9540
 
                        return;
9541
 
                    }
9542
 
                }
9543
 
            }
9544
 
        }
9545
 
    }
9546
 
    YAHOO.log("Could not delete " + count + " rows at row " + row, "warn", this.toString());
9547
 
    return null;
9548
 
},
9549
 
 
9550
 
 
9551
 
 
9552
 
 
9553
 
 
9554
 
 
9555
 
 
9556
 
 
9557
 
 
9558
 
 
9559
 
 
9560
 
 
9561
 
 
9562
 
 
9563
 
 
9564
 
 
9565
 
 
9566
 
 
9567
 
 
9568
 
 
9569
 
 
9570
 
 
9571
 
 
9572
 
 
9573
 
 
9574
 
 
9575
 
 
9576
 
 
9577
 
 
9578
 
 
9579
 
 
9580
 
 
9581
 
 
9582
 
 
9583
 
 
9584
 
 
9585
 
 
9586
 
 
9587
 
 
9588
 
 
9589
 
 
9590
 
 
9591
 
 
9592
 
 
9593
 
 
9594
 
 
9595
 
// CELL FUNCTIONS
9596
 
 
9597
 
/**
9598
 
 * Outputs markup into the given TD based on given Record.
9599
 
 *
9600
 
 * @method formatCell
9601
 
 * @param elCell {HTMLElement} The liner DIV element within the TD.
9602
 
 * @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
9603
 
 * @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
9604
 
 */
9605
 
formatCell : function(elCell, oRecord, oColumn) {
9606
 
    if(!oRecord) {
9607
 
        oRecord = this.getRecord(elCell);
9608
 
    }
9609
 
    if(!oColumn) {
9610
 
        oColumn = this.getColumn(elCell.parentNode.cellIndex);
9611
 
    }
9612
 
 
9613
 
    if(oRecord && oColumn) {
9614
 
        var sField = oColumn.field;
9615
 
        var oData = oRecord.getData(sField);
9616
 
 
9617
 
        var fnFormatter = typeof oColumn.formatter === 'function' ?
9618
 
                          oColumn.formatter :
9619
 
                          DT.Formatter[oColumn.formatter+''] ||
9620
 
                          DT.Formatter.defaultFormatter;
9621
 
 
9622
 
        // Apply special formatter
9623
 
        if(fnFormatter) {
9624
 
            fnFormatter.call(this, elCell, oRecord, oColumn, oData);
9625
 
        }
9626
 
        else {
9627
 
            elCell.innerHTML = oData;
9628
 
        }
9629
 
 
9630
 
        this.fireEvent("cellFormatEvent", {record:oRecord, column:oColumn, key:oColumn.key, el:elCell});
9631
 
    }
9632
 
    else {
9633
 
        YAHOO.log("Could not format cell " + elCell, "error", this.toString());
9634
 
    }
9635
 
},
9636
 
 
9637
 
/**
9638
 
 * For the given row and column, updates the Record with the given data. If the
9639
 
 * cell is on current page, the corresponding DOM elements are also updated.
9640
 
 *
9641
 
 * @method updateCell
9642
 
 * @param oRecord {YAHOO.widget.Record} Record instance.
9643
 
 * @param oColumn {YAHOO.widget.Column | String | Number} A Column key, or a ColumnSet key index.
9644
 
 * @param oData {Object} New data value for the cell.
9645
 
 */
9646
 
updateCell : function(oRecord, oColumn, oData) {    
9647
 
    // Validate Column and Record
9648
 
    oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn);
9649
 
    if(oColumn && oColumn.getKey() && (oRecord instanceof YAHOO.widget.Record)) {
9650
 
        var sKey = oColumn.getKey(),
9651
 
        
9652
 
        // Copy data from the Record for the event that gets fired later
9653
 
        //var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData());
9654
 
            oldData = oRecord.getData(sKey);
9655
 
 
9656
 
        // Update Record with new data
9657
 
        this._oRecordSet.updateRecordValue(oRecord, sKey, oData);
9658
 
    
9659
 
        // Update the TD only if row is on current page
9660
 
        var elTd = this.getTdEl({record: oRecord, column: oColumn});
9661
 
        if(elTd) {
9662
 
            this._oChainRender.add({
9663
 
                method: function() {
9664
 
                    if((this instanceof DT) && this._sId) {
9665
 
                        this.formatCell(elTd.firstChild);
9666
 
                        this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
9667
 
                        YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
9668
 
                                ", Record index = " + this.getRecordIndex(oRecord) +
9669
 
                                ", page row index = " + this.getTrIndex(oRecord) +
9670
 
                                ", Column key = " + oColumn.getKey(), "info", this.toString());
9671
 
                    }
9672
 
                },
9673
 
                scope: this,
9674
 
                timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9675
 
            });
9676
 
            this._runRenderChain();
9677
 
        }
9678
 
        else {
9679
 
            this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
9680
 
            YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
9681
 
                    ", Record index = " + this.getRecordIndex(oRecord) +
9682
 
                    ", page row index = " + this.getTrIndex(oRecord) +
9683
 
                    ", Column key = " + oColumn.getKey(), "info", this.toString());   
9684
 
        }
9685
 
    }
9686
 
},
9687
 
 
9688
 
 
9689
 
 
9690
 
 
9691
 
 
9692
 
 
9693
 
 
9694
 
 
9695
 
 
9696
 
 
9697
 
 
9698
 
 
9699
 
 
9700
 
 
9701
 
 
9702
 
 
9703
 
 
9704
 
 
9705
 
 
9706
 
 
9707
 
 
9708
 
 
9709
 
 
9710
 
 
9711
 
 
9712
 
 
9713
 
 
9714
 
 
9715
 
 
9716
 
 
9717
 
 
9718
 
 
9719
 
 
9720
 
 
9721
 
 
9722
 
 
9723
 
 
9724
 
 
9725
 
 
9726
 
 
9727
 
 
9728
 
 
9729
 
 
9730
 
 
9731
 
 
9732
 
 
9733
 
 
9734
 
 
9735
 
 
9736
 
 
9737
 
 
9738
 
// PAGINATION
9739
 
/**
9740
 
 * Method executed during set() operation for the "paginator" attribute.
9741
 
 * Adds and/or severs event listeners between DataTable and Paginator
9742
 
 *
9743
 
 * @method _updatePaginator
9744
 
 * @param newPag {Paginator} Paginator instance (or null) for DataTable to use
9745
 
 * @private
9746
 
 */
9747
 
_updatePaginator : function (newPag) {
9748
 
    var oldPag = this.get('paginator');
9749
 
    if (oldPag && newPag !== oldPag) {
9750
 
        oldPag.unsubscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
9751
 
    }
9752
 
    if (newPag) {
9753
 
        newPag.subscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
9754
 
    }
9755
 
},
9756
 
 
9757
 
/**
9758
 
 * Update the UI infrastructure in response to a "paginator" attribute change.
9759
 
 *
9760
 
 * @method _handlePaginatorChange
9761
 
 * @param e {Object} Change event object containing keys 'type','newValue',
9762
 
 *                   and 'prevValue'
9763
 
 * @private
9764
 
 */
9765
 
_handlePaginatorChange : function (e) {
9766
 
    if (e.prevValue === e.newValue) { return; }
9767
 
 
9768
 
    var newPag     = e.newValue,
9769
 
        oldPag     = e.prevValue,
9770
 
        containers = this._defaultPaginatorContainers();
9771
 
 
9772
 
    if (oldPag) {
9773
 
        if (oldPag.getContainerNodes()[0] == containers[0]) {
9774
 
            oldPag.set('containers',[]);
9775
 
        }
9776
 
        oldPag.destroy();
9777
 
 
9778
 
        // Convenience: share the default containers if possible.
9779
 
        // Otherwise, remove the default containers from the DOM.
9780
 
        if (containers[0]) {
9781
 
            if (newPag && !newPag.getContainerNodes().length) {
9782
 
                newPag.set('containers',containers);
9783
 
            } else {
9784
 
                // No new Paginator to use existing containers, OR new
9785
 
                // Paginator has configured containers.
9786
 
                for (var i = containers.length - 1; i >= 0; --i) {
9787
 
                    if (containers[i]) {
9788
 
                        containers[i].parentNode.removeChild(containers[i]);
9789
 
                    }
9790
 
                }
9791
 
            }
9792
 
        }
9793
 
    }
9794
 
 
9795
 
    if (!this._bInit) {
9796
 
        this.render();
9797
 
 
9798
 
    }
9799
 
 
9800
 
    if (newPag) {
9801
 
        this.renderPaginator();
9802
 
    }
9803
 
 
9804
 
},
9805
 
 
9806
 
/**
9807
 
 * Returns the default containers used for Paginators.  If create param is
9808
 
 * passed, the containers will be created and added to the DataTable container.
9809
 
 *
9810
 
 * @method _defaultPaginatorContainers
9811
 
 * @param create {boolean} Create the default containers if not found
9812
 
 * @private
9813
 
 */
9814
 
_defaultPaginatorContainers : function (create) {
9815
 
    var above_id = this._sId + '-paginator0',
9816
 
        below_id = this._sId + '-paginator1',
9817
 
        above    = Dom.get(above_id),
9818
 
        below    = Dom.get(below_id);
9819
 
 
9820
 
    if (create && (!above || !below)) {
9821
 
        // One above and one below the table
9822
 
        if (!above) {
9823
 
            above    = document.createElement('div');
9824
 
            above.id = above_id;
9825
 
            Dom.addClass(above, DT.CLASS_PAGINATOR);
9826
 
 
9827
 
            this._elContainer.insertBefore(above,this._elContainer.firstChild);
9828
 
        }
9829
 
 
9830
 
        if (!below) {
9831
 
            below    = document.createElement('div');
9832
 
            below.id = below_id;
9833
 
            Dom.addClass(below, DT.CLASS_PAGINATOR);
9834
 
 
9835
 
            this._elContainer.appendChild(below);
9836
 
        }
9837
 
    }
9838
 
 
9839
 
    return [above,below];
9840
 
},
9841
 
 
9842
 
/**
9843
 
 * Renders the Paginator to the DataTable UI
9844
 
 *
9845
 
 * @method renderPaginator
9846
 
 */
9847
 
renderPaginator : function () {
9848
 
    var pag = this.get("paginator");
9849
 
    if (!pag) { return; }
9850
 
 
9851
 
    // Add the containers if the Paginator is not configured with containers
9852
 
    if (!pag.getContainerNodes().length) {
9853
 
        pag.set('containers',this._defaultPaginatorContainers(true));
9854
 
    }
9855
 
 
9856
 
    pag.render();
9857
 
},
9858
 
 
9859
 
/**
9860
 
 * Overridable method gives implementers a hook to show loading message before
9861
 
 * changing Paginator value.
9862
 
 *
9863
 
 * @method doBeforePaginatorChange
9864
 
 * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
9865
 
 * @return {Boolean} Return true to continue changing Paginator value.
9866
 
 */
9867
 
doBeforePaginatorChange : function(oPaginatorState) {
9868
 
    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
9869
 
    return true;
9870
 
},
9871
 
 
9872
 
/**
9873
 
 * Responds to new Pagination states. By default, updates the UI to reflect the
9874
 
 * new state. If "dynamicData" is true, current selections are purged before
9875
 
 * a request is sent to the DataSource for data for the new state (using the
9876
 
 * request returned by "generateRequest()").
9877
 
 *  
9878
 
 * @method onPaginatorChangeRequest
9879
 
 * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
9880
 
 */
9881
 
onPaginatorChangeRequest : function (oPaginatorState) {
9882
 
    var ok = this.doBeforePaginatorChange(oPaginatorState);
9883
 
    if(ok) {
9884
 
        // Server-side pagination
9885
 
        if(this.get("dynamicData")) {
9886
 
            // Get the current state
9887
 
            var oState = this.getState();
9888
 
            
9889
 
            // Update pagination values
9890
 
            oState.pagination = oPaginatorState;
9891
 
    
9892
 
            // Get the request for the new state
9893
 
            var request = this.get("generateRequest")(oState, this);
9894
 
            
9895
 
            // Purge selections
9896
 
            this.unselectAllRows();
9897
 
            this.unselectAllCells();
9898
 
            
9899
 
            // Get the new data from the server
9900
 
            var callback = {
9901
 
                success : this.onDataReturnSetRows,
9902
 
                failure : this.onDataReturnSetRows,
9903
 
                argument : oState, // Pass along the new state to the callback
9904
 
                scope : this
9905
 
            };
9906
 
            this._oDataSource.sendRequest(request, callback);
9907
 
        }
9908
 
        // Client-side pagination
9909
 
        else {
9910
 
            // Set the core pagination values silently (the second param)
9911
 
            // to avoid looping back through the changeRequest mechanism
9912
 
            oPaginatorState.paginator.setStartIndex(oPaginatorState.recordOffset,true);
9913
 
            oPaginatorState.paginator.setRowsPerPage(oPaginatorState.rowsPerPage,true);
9914
 
    
9915
 
            // Update the UI
9916
 
            this.render();
9917
 
        }
9918
 
    }
9919
 
    else {
9920
 
        YAHOO.log("Could not change Paginator value \"" + oPaginatorState + "\"", "warn", this.toString());
9921
 
    }
9922
 
},
9923
 
 
9924
 
 
9925
 
 
9926
 
 
9927
 
 
9928
 
 
9929
 
 
9930
 
 
9931
 
 
9932
 
 
9933
 
 
9934
 
 
9935
 
 
9936
 
 
9937
 
 
9938
 
 
9939
 
 
9940
 
 
9941
 
 
9942
 
 
9943
 
 
9944
 
 
9945
 
 
9946
 
 
9947
 
 
9948
 
 
9949
 
 
9950
 
 
9951
 
 
9952
 
 
9953
 
 
9954
 
 
9955
 
 
9956
 
 
9957
 
 
9958
 
 
9959
 
 
9960
 
 
9961
 
 
9962
 
 
9963
 
 
9964
 
 
9965
 
 
9966
 
 
9967
 
 
9968
 
 
9969
 
 
9970
 
 
9971
 
 
9972
 
 
9973
 
// SELECTION/HIGHLIGHTING
9974
 
 
9975
 
/*
9976
 
 * Reference to last highlighted cell element
9977
 
 *
9978
 
 * @property _elLastHighlightedTd
9979
 
 * @type HTMLElement
9980
 
 * @private
9981
 
 */
9982
 
_elLastHighlightedTd : null,
9983
 
 
9984
 
/*
9985
 
 * ID string of last highlighted row element
9986
 
 *
9987
 
 * @property _sLastHighlightedTrElId
9988
 
 * @type String
9989
 
 * @private
9990
 
 */
9991
 
//_sLastHighlightedTrElId : null,
9992
 
 
9993
 
/**
9994
 
 * Array to track row selections (by sRecordId) and/or cell selections
9995
 
 * (by {recordId:sRecordId, columnKey:sColumnKey})
9996
 
 *
9997
 
 * @property _aSelections
9998
 
 * @type Object[]
9999
 
 * @private
10000
 
 */
10001
 
_aSelections : null,
10002
 
 
10003
 
/**
10004
 
 * Record instance of the row selection anchor.
10005
 
 *
10006
 
 * @property _oAnchorRecord
10007
 
 * @type YAHOO.widget.Record
10008
 
 * @private
10009
 
 */
10010
 
_oAnchorRecord : null,
10011
 
 
10012
 
/**
10013
 
 * Object literal representing cell selection anchor:
10014
 
 * {recordId:sRecordId, columnKey:sColumnKey}.
10015
 
 *
10016
 
 * @property _oAnchorCell
10017
 
 * @type Object
10018
 
 * @private
10019
 
 */
10020
 
_oAnchorCell : null,
10021
 
 
10022
 
/**
10023
 
 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
10024
 
 * from all TR elements on the page.
10025
 
 *
10026
 
 * @method _unselectAllTrEls
10027
 
 * @private
10028
 
 */
10029
 
_unselectAllTrEls : function() {
10030
 
    var selectedRows = Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
10031
 
    Dom.removeClass(selectedRows, DT.CLASS_SELECTED);
10032
 
},
10033
 
 
10034
 
/**
10035
 
 * Returns object literal of values that represent the selection trigger. Used
10036
 
 * to determine selection behavior resulting from a key event.
10037
 
 *
10038
 
 * @method _getSelectionTrigger
10039
 
 * @private
10040
 
 */
10041
 
_getSelectionTrigger : function() {
10042
 
    var sMode = this.get("selectionMode");
10043
 
    var oTrigger = {};
10044
 
    var oTriggerCell, oTriggerRecord, nTriggerRecordIndex, elTriggerRow, nTriggerTrIndex;
10045
 
 
10046
 
    // Cell mode
10047
 
    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
10048
 
        oTriggerCell = this.getLastSelectedCell();
10049
 
        // No selected cells found
10050
 
        if(!oTriggerCell) {
10051
 
            return null;
10052
 
        }
10053
 
        else {
10054
 
            oTriggerRecord = this.getRecord(oTriggerCell.recordId);
10055
 
            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
10056
 
            elTriggerRow = this.getTrEl(oTriggerRecord);
10057
 
            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
10058
 
 
10059
 
            // Selected cell not found on this page
10060
 
            if(nTriggerTrIndex === null) {
10061
 
                return null;
10062
 
            }
10063
 
            else {
10064
 
                oTrigger.record = oTriggerRecord;
10065
 
                oTrigger.recordIndex = nTriggerRecordIndex;
10066
 
                oTrigger.el = this.getTdEl(oTriggerCell);
10067
 
                oTrigger.trIndex = nTriggerTrIndex;
10068
 
                oTrigger.column = this.getColumn(oTriggerCell.columnKey);
10069
 
                oTrigger.colKeyIndex = oTrigger.column.getKeyIndex();
10070
 
                oTrigger.cell = oTriggerCell;
10071
 
                return oTrigger;
10072
 
            }
10073
 
        }
10074
 
    }
10075
 
    // Row mode
10076
 
    else {
10077
 
        oTriggerRecord = this.getLastSelectedRecord();
10078
 
        // No selected rows found
10079
 
        if(!oTriggerRecord) {
10080
 
                return null;
10081
 
        }
10082
 
        else {
10083
 
            // Selected row found, but is it on current page?
10084
 
            oTriggerRecord = this.getRecord(oTriggerRecord);
10085
 
            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
10086
 
            elTriggerRow = this.getTrEl(oTriggerRecord);
10087
 
            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
10088
 
 
10089
 
            // Selected row not found on this page
10090
 
            if(nTriggerTrIndex === null) {
10091
 
                return null;
10092
 
            }
10093
 
            else {
10094
 
                oTrigger.record = oTriggerRecord;
10095
 
                oTrigger.recordIndex = nTriggerRecordIndex;
10096
 
                oTrigger.el = elTriggerRow;
10097
 
                oTrigger.trIndex = nTriggerTrIndex;
10098
 
                return oTrigger;
10099
 
            }
10100
 
        }
10101
 
    }
10102
 
},
10103
 
 
10104
 
/**
10105
 
 * Returns object literal of values that represent the selection anchor. Used
10106
 
 * to determine selection behavior resulting from a user event.
10107
 
 *
10108
 
 * @method _getSelectionAnchor
10109
 
 * @param oTrigger {Object} (Optional) Object literal of selection trigger values
10110
 
 * (for key events).
10111
 
 * @private
10112
 
 */
10113
 
_getSelectionAnchor : function(oTrigger) {
10114
 
    var sMode = this.get("selectionMode");
10115
 
    var oAnchor = {};
10116
 
    var oAnchorRecord, nAnchorRecordIndex, nAnchorTrIndex;
10117
 
 
10118
 
    // Cell mode
10119
 
    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
10120
 
        // Validate anchor cell
10121
 
        var oAnchorCell = this._oAnchorCell;
10122
 
        if(!oAnchorCell) {
10123
 
            if(oTrigger) {
10124
 
                oAnchorCell = this._oAnchorCell = oTrigger.cell;
10125
 
            }
10126
 
            else {
10127
 
                return null;
10128
 
            }
10129
 
        }
10130
 
        oAnchorRecord = this._oAnchorCell.record;
10131
 
        nAnchorRecordIndex = this._oRecordSet.getRecordIndex(oAnchorRecord);
10132
 
        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
10133
 
        // If anchor cell is not on this page...
10134
 
        if(nAnchorTrIndex === null) {
10135
 
            // ...set TR index equal to top TR
10136
 
            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
10137
 
                nAnchorTrIndex = 0;
10138
 
            }
10139
 
            // ...set TR index equal to bottom TR
10140
 
            else {
10141
 
                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
10142
 
            }
10143
 
        }
10144
 
 
10145
 
        oAnchor.record = oAnchorRecord;
10146
 
        oAnchor.recordIndex = nAnchorRecordIndex;
10147
 
        oAnchor.trIndex = nAnchorTrIndex;
10148
 
        oAnchor.column = this._oAnchorCell.column;
10149
 
        oAnchor.colKeyIndex = oAnchor.column.getKeyIndex();
10150
 
        oAnchor.cell = oAnchorCell;
10151
 
        return oAnchor;
10152
 
    }
10153
 
    // Row mode
10154
 
    else {
10155
 
        oAnchorRecord = this._oAnchorRecord;
10156
 
        if(!oAnchorRecord) {
10157
 
            if(oTrigger) {
10158
 
                oAnchorRecord = this._oAnchorRecord = oTrigger.record;
10159
 
            }
10160
 
            else {
10161
 
                return null;
10162
 
            }
10163
 
        }
10164
 
 
10165
 
        nAnchorRecordIndex = this.getRecordIndex(oAnchorRecord);
10166
 
        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
10167
 
        // If anchor row is not on this page...
10168
 
        if(nAnchorTrIndex === null) {
10169
 
            // ...set TR index equal to top TR
10170
 
            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
10171
 
                nAnchorTrIndex = 0;
10172
 
            }
10173
 
            // ...set TR index equal to bottom TR
10174
 
            else {
10175
 
                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
10176
 
            }
10177
 
        }
10178
 
 
10179
 
        oAnchor.record = oAnchorRecord;
10180
 
        oAnchor.recordIndex = nAnchorRecordIndex;
10181
 
        oAnchor.trIndex = nAnchorTrIndex;
10182
 
        return oAnchor;
10183
 
    }
10184
 
},
10185
 
 
10186
 
/**
10187
 
 * Determines selection behavior resulting from a mouse event when selection mode
10188
 
 * is set to "standard".
10189
 
 *
10190
 
 * @method _handleStandardSelectionByMouse
10191
 
 * @param oArgs.event {HTMLEvent} Event object.
10192
 
 * @param oArgs.target {HTMLElement} Target element.
10193
 
 * @private
10194
 
 */
10195
 
_handleStandardSelectionByMouse : function(oArgs) {
10196
 
    var elTarget = oArgs.target;
10197
 
 
10198
 
    // Validate target row
10199
 
    var elTargetRow = this.getTrEl(elTarget);
10200
 
    if(elTargetRow) {
10201
 
        var e = oArgs.event;
10202
 
        var bSHIFT = e.shiftKey;
10203
 
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
10204
 
 
10205
 
        var oTargetRecord = this.getRecord(elTargetRow);
10206
 
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
10207
 
 
10208
 
        var oAnchor = this._getSelectionAnchor();
10209
 
 
10210
 
        var i;
10211
 
 
10212
 
        // Both SHIFT and CTRL
10213
 
        if(bSHIFT && bCTRL) {
10214
 
            // Validate anchor
10215
 
            if(oAnchor) {
10216
 
                if(this.isSelected(oAnchor.record)) {
10217
 
                    // Select all rows between anchor row and target row, including target row
10218
 
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
10219
 
                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex; i++) {
10220
 
                            if(!this.isSelected(i)) {
10221
 
                                this.selectRow(i);
10222
 
                            }
10223
 
                        }
10224
 
                    }
10225
 
                    // Select all rows between target row and anchor row, including target row
10226
 
                    else {
10227
 
                        for(i=oAnchor.recordIndex-1; i>=nTargetRecordIndex; i--) {
10228
 
                            if(!this.isSelected(i)) {
10229
 
                                this.selectRow(i);
10230
 
                            }
10231
 
                        }
10232
 
                    }
10233
 
                }
10234
 
                else {
10235
 
                    // Unselect all rows between anchor row and target row
10236
 
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
10237
 
                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex-1; i++) {
10238
 
                            if(this.isSelected(i)) {
10239
 
                                this.unselectRow(i);
10240
 
                            }
10241
 
                        }
10242
 
                    }
10243
 
                    // Unselect all rows between target row and anchor row
10244
 
                    else {
10245
 
                        for(i=nTargetRecordIndex+1; i<=oAnchor.recordIndex-1; i++) {
10246
 
                            if(this.isSelected(i)) {
10247
 
                                this.unselectRow(i);
10248
 
                            }
10249
 
                        }
10250
 
                    }
10251
 
                    // Select the target row
10252
 
                    this.selectRow(oTargetRecord);
10253
 
                }
10254
 
            }
10255
 
            // Invalid anchor
10256
 
            else {
10257
 
                // Set anchor
10258
 
                this._oAnchorRecord = oTargetRecord;
10259
 
 
10260
 
                // Toggle selection of target
10261
 
                if(this.isSelected(oTargetRecord)) {
10262
 
                    this.unselectRow(oTargetRecord);
10263
 
                }
10264
 
                else {
10265
 
                    this.selectRow(oTargetRecord);
10266
 
                }
10267
 
            }
10268
 
        }
10269
 
         // Only SHIFT
10270
 
        else if(bSHIFT) {
10271
 
            this.unselectAllRows();
10272
 
 
10273
 
            // Validate anchor
10274
 
            if(oAnchor) {
10275
 
                // Select all rows between anchor row and target row,
10276
 
                // including the anchor row and target row
10277
 
                if(oAnchor.recordIndex < nTargetRecordIndex) {
10278
 
                    for(i=oAnchor.recordIndex; i<=nTargetRecordIndex; i++) {
10279
 
                        this.selectRow(i);
10280
 
                    }
10281
 
                }
10282
 
                // Select all rows between target row and anchor row,
10283
 
                // including the target row and anchor row
10284
 
                else {
10285
 
                    for(i=oAnchor.recordIndex; i>=nTargetRecordIndex; i--) {
10286
 
                        this.selectRow(i);
10287
 
                    }
10288
 
                }
10289
 
            }
10290
 
            // Invalid anchor
10291
 
            else {
10292
 
                // Set anchor
10293
 
                this._oAnchorRecord = oTargetRecord;
10294
 
 
10295
 
                // Select target row only
10296
 
                this.selectRow(oTargetRecord);
10297
 
            }
10298
 
        }
10299
 
        // Only CTRL
10300
 
        else if(bCTRL) {
10301
 
            // Set anchor
10302
 
            this._oAnchorRecord = oTargetRecord;
10303
 
 
10304
 
            // Toggle selection of target
10305
 
            if(this.isSelected(oTargetRecord)) {
10306
 
                this.unselectRow(oTargetRecord);
10307
 
            }
10308
 
            else {
10309
 
                this.selectRow(oTargetRecord);
10310
 
            }
10311
 
        }
10312
 
        // Neither SHIFT nor CTRL
10313
 
        else {
10314
 
            this._handleSingleSelectionByMouse(oArgs);
10315
 
            return;
10316
 
        }
10317
 
    }
10318
 
},
10319
 
 
10320
 
/**
10321
 
 * Determines selection behavior resulting from a key event when selection mode
10322
 
 * is set to "standard".
10323
 
 *
10324
 
 * @method _handleStandardSelectionByKey
10325
 
 * @param e {HTMLEvent} Event object.
10326
 
 * @private
10327
 
 */
10328
 
_handleStandardSelectionByKey : function(e) {
10329
 
    var nKey = Ev.getCharCode(e);
10330
 
 
10331
 
    if((nKey == 38) || (nKey == 40)) {
10332
 
        var bSHIFT = e.shiftKey;
10333
 
 
10334
 
        // Validate trigger
10335
 
        var oTrigger = this._getSelectionTrigger();
10336
 
        // Arrow selection only works if last selected row is on current page
10337
 
        if(!oTrigger) {
10338
 
            return null;
10339
 
        }
10340
 
 
10341
 
        Ev.stopEvent(e);
10342
 
 
10343
 
        // Validate anchor
10344
 
        var oAnchor = this._getSelectionAnchor(oTrigger);
10345
 
 
10346
 
        // Determine which direction we're going to
10347
 
        if(bSHIFT) {
10348
 
            // Selecting down away from anchor row
10349
 
            if((nKey == 40) && (oAnchor.recordIndex <= oTrigger.trIndex)) {
10350
 
                this.selectRow(this.getNextTrEl(oTrigger.el));
10351
 
            }
10352
 
            // Selecting up away from anchor row
10353
 
            else if((nKey == 38) && (oAnchor.recordIndex >= oTrigger.trIndex)) {
10354
 
                this.selectRow(this.getPreviousTrEl(oTrigger.el));
10355
 
            }
10356
 
            // Unselect trigger
10357
 
            else {
10358
 
                this.unselectRow(oTrigger.el);
10359
 
            }
10360
 
        }
10361
 
        else {
10362
 
            this._handleSingleSelectionByKey(e);
10363
 
        }
10364
 
    }
10365
 
},
10366
 
 
10367
 
/**
10368
 
 * Determines selection behavior resulting from a mouse event when selection mode
10369
 
 * is set to "single".
10370
 
 *
10371
 
 * @method _handleSingleSelectionByMouse
10372
 
 * @param oArgs.event {HTMLEvent} Event object.
10373
 
 * @param oArgs.target {HTMLElement} Target element.
10374
 
 * @private
10375
 
 */
10376
 
_handleSingleSelectionByMouse : function(oArgs) {
10377
 
    var elTarget = oArgs.target;
10378
 
 
10379
 
    // Validate target row
10380
 
    var elTargetRow = this.getTrEl(elTarget);
10381
 
    if(elTargetRow) {
10382
 
        var oTargetRecord = this.getRecord(elTargetRow);
10383
 
 
10384
 
        // Set anchor
10385
 
        this._oAnchorRecord = oTargetRecord;
10386
 
 
10387
 
        // Select only target
10388
 
        this.unselectAllRows();
10389
 
        this.selectRow(oTargetRecord);
10390
 
    }
10391
 
},
10392
 
 
10393
 
/**
10394
 
 * Determines selection behavior resulting from a key event when selection mode
10395
 
 * is set to "single".
10396
 
 *
10397
 
 * @method _handleSingleSelectionByKey
10398
 
 * @param e {HTMLEvent} Event object.
10399
 
 * @private
10400
 
 */
10401
 
_handleSingleSelectionByKey : function(e) {
10402
 
    var nKey = Ev.getCharCode(e);
10403
 
 
10404
 
    if((nKey == 38) || (nKey == 40)) {
10405
 
        // Validate trigger
10406
 
        var oTrigger = this._getSelectionTrigger();
10407
 
        // Arrow selection only works if last selected row is on current page
10408
 
        if(!oTrigger) {
10409
 
            return null;
10410
 
        }
10411
 
 
10412
 
        Ev.stopEvent(e);
10413
 
 
10414
 
        // Determine the new row to select
10415
 
        var elNew;
10416
 
        if(nKey == 38) { // arrow up
10417
 
            elNew = this.getPreviousTrEl(oTrigger.el);
10418
 
 
10419
 
            // Validate new row
10420
 
            if(elNew === null) {
10421
 
                //TODO: wrap around to last tr on current page
10422
 
                //elNew = this.getLastTrEl();
10423
 
 
10424
 
                //TODO: wrap back to last tr of previous page
10425
 
 
10426
 
                // Top row selection is sticky
10427
 
                elNew = this.getFirstTrEl();
10428
 
            }
10429
 
        }
10430
 
        else if(nKey == 40) { // arrow down
10431
 
            elNew = this.getNextTrEl(oTrigger.el);
10432
 
 
10433
 
            // Validate new row
10434
 
            if(elNew === null) {
10435
 
                //TODO: wrap around to first tr on current page
10436
 
                //elNew = this.getFirstTrEl();
10437
 
 
10438
 
                //TODO: wrap forward to first tr of previous page
10439
 
 
10440
 
                // Bottom row selection is sticky
10441
 
                elNew = this.getLastTrEl();
10442
 
            }
10443
 
        }
10444
 
 
10445
 
        // Unselect all rows
10446
 
        this.unselectAllRows();
10447
 
 
10448
 
        // Select the new row
10449
 
        this.selectRow(elNew);
10450
 
 
10451
 
        // Set new anchor
10452
 
        this._oAnchorRecord = this.getRecord(elNew);
10453
 
    }
10454
 
},
10455
 
 
10456
 
/**
10457
 
 * Determines selection behavior resulting from a mouse event when selection mode
10458
 
 * is set to "cellblock".
10459
 
 *
10460
 
 * @method _handleCellBlockSelectionByMouse
10461
 
 * @param oArgs.event {HTMLEvent} Event object.
10462
 
 * @param oArgs.target {HTMLElement} Target element.
10463
 
 * @private
10464
 
 */
10465
 
_handleCellBlockSelectionByMouse : function(oArgs) {
10466
 
    var elTarget = oArgs.target;
10467
 
 
10468
 
    // Validate target cell
10469
 
    var elTargetCell = this.getTdEl(elTarget);
10470
 
    if(elTargetCell) {
10471
 
        var e = oArgs.event;
10472
 
        var bSHIFT = e.shiftKey;
10473
 
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
10474
 
 
10475
 
        var elTargetRow = this.getTrEl(elTargetCell);
10476
 
        var nTargetTrIndex = this.getTrIndex(elTargetRow);
10477
 
        var oTargetColumn = this.getColumn(elTargetCell);
10478
 
        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
10479
 
        var oTargetRecord = this.getRecord(elTargetRow);
10480
 
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
10481
 
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
10482
 
 
10483
 
        var oAnchor = this._getSelectionAnchor();
10484
 
 
10485
 
        var allRows = this.getTbodyEl().rows;
10486
 
        var startIndex, endIndex, currentRow, i, j;
10487
 
 
10488
 
        // Both SHIFT and CTRL
10489
 
        if(bSHIFT && bCTRL) {
10490
 
 
10491
 
            // Validate anchor
10492
 
            if(oAnchor) {
10493
 
                // Anchor is selected
10494
 
                if(this.isSelected(oAnchor.cell)) {
10495
 
                    // All cells are on the same row
10496
 
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
10497
 
                        // Select all cells between anchor cell and target cell, including target cell
10498
 
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10499
 
                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
10500
 
                                this.selectCell(elTargetRow.cells[i]);
10501
 
                            }
10502
 
                        }
10503
 
                        // Select all cells between target cell and anchor cell, including target cell
10504
 
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10505
 
                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
10506
 
                                this.selectCell(elTargetRow.cells[i]);
10507
 
                            }
10508
 
                        }
10509
 
                    }
10510
 
                    // Anchor row is above target row
10511
 
                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
10512
 
                        startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
10513
 
                        endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
10514
 
 
10515
 
                        // Select all cells from startIndex to endIndex on rows between anchor row and target row
10516
 
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10517
 
                            for(j=startIndex; j<=endIndex; j++) {
10518
 
                                this.selectCell(allRows[i].cells[j]);
10519
 
                            }
10520
 
                        }
10521
 
                    }
10522
 
                    // Anchor row is below target row
10523
 
                    else {
10524
 
                        startIndex = Math.min(oAnchor.trIndex, nTargetColKeyIndex);
10525
 
                        endIndex = Math.max(oAnchor.trIndex, nTargetColKeyIndex);
10526
 
 
10527
 
                        // Select all cells from startIndex to endIndex on rows between target row and anchor row
10528
 
                        for(i=oAnchor.trIndex; i>=nTargetTrIndex; i--) {
10529
 
                            for(j=endIndex; j>=startIndex; j--) {
10530
 
                                this.selectCell(allRows[i].cells[j]);
10531
 
                            }
10532
 
                        }
10533
 
                    }
10534
 
                }
10535
 
                // Anchor cell is unselected
10536
 
                else {
10537
 
                    // All cells are on the same row
10538
 
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
10539
 
                        // Unselect all cells between anchor cell and target cell
10540
 
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10541
 
                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
10542
 
                                this.unselectCell(elTargetRow.cells[i]);
10543
 
                            }
10544
 
                        }
10545
 
                        // Select all cells between target cell and anchor cell
10546
 
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10547
 
                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
10548
 
                                this.unselectCell(elTargetRow.cells[i]);
10549
 
                            }
10550
 
                        }
10551
 
                    }
10552
 
                    // Anchor row is above target row
10553
 
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
10554
 
                        // Unselect all cells from anchor cell to target cell
10555
 
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10556
 
                            currentRow = allRows[i];
10557
 
                            for(j=0; j<currentRow.cells.length; j++) {
10558
 
                                // This is the anchor row, only unselect cells after the anchor cell
10559
 
                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
10560
 
                                    if(j>oAnchor.colKeyIndex) {
10561
 
                                        this.unselectCell(currentRow.cells[j]);
10562
 
                                    }
10563
 
                                }
10564
 
                                // This is the target row, only unelect cells before the target cell
10565
 
                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
10566
 
                                    if(j<nTargetColKeyIndex) {
10567
 
                                        this.unselectCell(currentRow.cells[j]);
10568
 
                                    }
10569
 
                                }
10570
 
                                // Unselect all cells on this row
10571
 
                                else {
10572
 
                                    this.unselectCell(currentRow.cells[j]);
10573
 
                                }
10574
 
                            }
10575
 
                        }
10576
 
                    }
10577
 
                    // Anchor row is below target row
10578
 
                    else {
10579
 
                        // Unselect all cells from target cell to anchor cell
10580
 
                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
10581
 
                            currentRow = allRows[i];
10582
 
                            for(j=0; j<currentRow.cells.length; j++) {
10583
 
                                // This is the target row, only unselect cells after the target cell
10584
 
                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
10585
 
                                    if(j>nTargetColKeyIndex) {
10586
 
                                        this.unselectCell(currentRow.cells[j]);
10587
 
                                    }
10588
 
                                }
10589
 
                                // This is the anchor row, only unselect cells before the anchor cell
10590
 
                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
10591
 
                                    if(j<oAnchor.colKeyIndex) {
10592
 
                                        this.unselectCell(currentRow.cells[j]);
10593
 
                                    }
10594
 
                                }
10595
 
                                // Unselect all cells on this row
10596
 
                                else {
10597
 
                                    this.unselectCell(currentRow.cells[j]);
10598
 
                                }
10599
 
                            }
10600
 
                        }
10601
 
                    }
10602
 
 
10603
 
                    // Select the target cell
10604
 
                    this.selectCell(elTargetCell);
10605
 
                }
10606
 
            }
10607
 
            // Invalid anchor
10608
 
            else {
10609
 
                // Set anchor
10610
 
                this._oAnchorCell = oTargetCell;
10611
 
 
10612
 
                // Toggle selection of target
10613
 
                if(this.isSelected(oTargetCell)) {
10614
 
                    this.unselectCell(oTargetCell);
10615
 
                }
10616
 
                else {
10617
 
                    this.selectCell(oTargetCell);
10618
 
                }
10619
 
            }
10620
 
 
10621
 
        }
10622
 
         // Only SHIFT
10623
 
        else if(bSHIFT) {
10624
 
            this.unselectAllCells();
10625
 
 
10626
 
            // Validate anchor
10627
 
            if(oAnchor) {
10628
 
                // All cells are on the same row
10629
 
                if(oAnchor.recordIndex === nTargetRecordIndex) {
10630
 
                    // Select all cells between anchor cell and target cell,
10631
 
                    // including the anchor cell and target cell
10632
 
                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10633
 
                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
10634
 
                            this.selectCell(elTargetRow.cells[i]);
10635
 
                        }
10636
 
                    }
10637
 
                    // Select all cells between target cell and anchor cell
10638
 
                    // including the target cell and anchor cell
10639
 
                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10640
 
                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
10641
 
                            this.selectCell(elTargetRow.cells[i]);
10642
 
                        }
10643
 
                    }
10644
 
                }
10645
 
                // Anchor row is above target row
10646
 
                else if(oAnchor.recordIndex < nTargetRecordIndex) {
10647
 
                    // Select the cellblock from anchor cell to target cell
10648
 
                    // including the anchor cell and the target cell
10649
 
                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
10650
 
                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
10651
 
 
10652
 
                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10653
 
                        for(j=startIndex; j<=endIndex; j++) {
10654
 
                            this.selectCell(allRows[i].cells[j]);
10655
 
                        }
10656
 
                    }
10657
 
                }
10658
 
                // Anchor row is below target row
10659
 
                else {
10660
 
                    // Select the cellblock from target cell to anchor cell
10661
 
                    // including the target cell and the anchor cell
10662
 
                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
10663
 
                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
10664
 
 
10665
 
                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
10666
 
                        for(j=startIndex; j<=endIndex; j++) {
10667
 
                            this.selectCell(allRows[i].cells[j]);
10668
 
                        }
10669
 
                    }
10670
 
                }
10671
 
            }
10672
 
            // Invalid anchor
10673
 
            else {
10674
 
                // Set anchor
10675
 
                this._oAnchorCell = oTargetCell;
10676
 
 
10677
 
                // Select target only
10678
 
                this.selectCell(oTargetCell);
10679
 
            }
10680
 
        }
10681
 
        // Only CTRL
10682
 
        else if(bCTRL) {
10683
 
 
10684
 
            // Set anchor
10685
 
            this._oAnchorCell = oTargetCell;
10686
 
 
10687
 
            // Toggle selection of target
10688
 
            if(this.isSelected(oTargetCell)) {
10689
 
                this.unselectCell(oTargetCell);
10690
 
            }
10691
 
            else {
10692
 
                this.selectCell(oTargetCell);
10693
 
            }
10694
 
 
10695
 
        }
10696
 
        // Neither SHIFT nor CTRL
10697
 
        else {
10698
 
            this._handleSingleCellSelectionByMouse(oArgs);
10699
 
        }
10700
 
    }
10701
 
},
10702
 
 
10703
 
/**
10704
 
 * Determines selection behavior resulting from a key event when selection mode
10705
 
 * is set to "cellblock".
10706
 
 *
10707
 
 * @method _handleCellBlockSelectionByKey
10708
 
 * @param e {HTMLEvent} Event object.
10709
 
 * @private
10710
 
 */
10711
 
_handleCellBlockSelectionByKey : function(e) {
10712
 
    var nKey = Ev.getCharCode(e);
10713
 
    var bSHIFT = e.shiftKey;
10714
 
    if((nKey == 9) || !bSHIFT) {
10715
 
        this._handleSingleCellSelectionByKey(e);
10716
 
        return;
10717
 
    }
10718
 
 
10719
 
    if((nKey > 36) && (nKey < 41)) {
10720
 
        // Validate trigger
10721
 
        var oTrigger = this._getSelectionTrigger();
10722
 
        // Arrow selection only works if last selected row is on current page
10723
 
        if(!oTrigger) {
10724
 
            return null;
10725
 
        }
10726
 
 
10727
 
        Ev.stopEvent(e);
10728
 
 
10729
 
        // Validate anchor
10730
 
        var oAnchor = this._getSelectionAnchor(oTrigger);
10731
 
 
10732
 
        var i, startIndex, endIndex, elNew, elNewRow;
10733
 
        var allRows = this.getTbodyEl().rows;
10734
 
        var elThisRow = oTrigger.el.parentNode;
10735
 
 
10736
 
        // Determine which direction we're going to
10737
 
 
10738
 
        if(nKey == 40) { // arrow down
10739
 
            // Selecting away from anchor cell
10740
 
            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
10741
 
                // Select the horiz block on the next row...
10742
 
                // ...making sure there is room below the trigger row
10743
 
                elNewRow = this.getNextTrEl(oTrigger.el);
10744
 
                if(elNewRow) {
10745
 
                    startIndex = oAnchor.colKeyIndex;
10746
 
                    endIndex = oTrigger.colKeyIndex;
10747
 
                    // ...going left
10748
 
                    if(startIndex > endIndex) {
10749
 
                        for(i=startIndex; i>=endIndex; i--) {
10750
 
                            elNew = elNewRow.cells[i];
10751
 
                            this.selectCell(elNew);
10752
 
                        }
10753
 
                    }
10754
 
                    // ... going right
10755
 
                    else {
10756
 
                        for(i=startIndex; i<=endIndex; i++) {
10757
 
                            elNew = elNewRow.cells[i];
10758
 
                            this.selectCell(elNew);
10759
 
                        }
10760
 
                    }
10761
 
                }
10762
 
            }
10763
 
            // Unselecting towards anchor cell
10764
 
            else {
10765
 
                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10766
 
                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10767
 
                // Unselect the horiz block on this row towards the next row
10768
 
                for(i=startIndex; i<=endIndex; i++) {
10769
 
                    this.unselectCell(elThisRow.cells[i]);
10770
 
                }
10771
 
            }
10772
 
        }
10773
 
        // Arrow up
10774
 
        else if(nKey == 38) {
10775
 
            // Selecting away from anchor cell
10776
 
            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
10777
 
                // Select the horiz block on the previous row...
10778
 
                // ...making sure there is room
10779
 
                elNewRow = this.getPreviousTrEl(oTrigger.el);
10780
 
                if(elNewRow) {
10781
 
                    // Select in order from anchor to trigger...
10782
 
                    startIndex = oAnchor.colKeyIndex;
10783
 
                    endIndex = oTrigger.colKeyIndex;
10784
 
                    // ...going left
10785
 
                    if(startIndex > endIndex) {
10786
 
                        for(i=startIndex; i>=endIndex; i--) {
10787
 
                            elNew = elNewRow.cells[i];
10788
 
                            this.selectCell(elNew);
10789
 
                        }
10790
 
                    }
10791
 
                    // ... going right
10792
 
                    else {
10793
 
                        for(i=startIndex; i<=endIndex; i++) {
10794
 
                            elNew = elNewRow.cells[i];
10795
 
                            this.selectCell(elNew);
10796
 
                        }
10797
 
                    }
10798
 
                }
10799
 
            }
10800
 
            // Unselecting towards anchor cell
10801
 
            else {
10802
 
                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10803
 
                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10804
 
                // Unselect the horiz block on this row towards the previous row
10805
 
                for(i=startIndex; i<=endIndex; i++) {
10806
 
                    this.unselectCell(elThisRow.cells[i]);
10807
 
                }
10808
 
            }
10809
 
        }
10810
 
        // Arrow right
10811
 
        else if(nKey == 39) {
10812
 
            // Selecting away from anchor cell
10813
 
            if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
10814
 
                // Select the next vert block to the right...
10815
 
                // ...making sure there is room
10816
 
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
10817
 
                    // Select in order from anchor to trigger...
10818
 
                    startIndex = oAnchor.trIndex;
10819
 
                    endIndex = oTrigger.trIndex;
10820
 
                    // ...going up
10821
 
                    if(startIndex > endIndex) {
10822
 
                        for(i=startIndex; i>=endIndex; i--) {
10823
 
                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
10824
 
                            this.selectCell(elNew);
10825
 
                        }
10826
 
                    }
10827
 
                    // ... going down
10828
 
                    else {
10829
 
                        for(i=startIndex; i<=endIndex; i++) {
10830
 
                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
10831
 
                            this.selectCell(elNew);
10832
 
                        }
10833
 
                    }
10834
 
                }
10835
 
            }
10836
 
            // Unselecting towards anchor cell
10837
 
            else {
10838
 
                // Unselect the vert block on this column towards the right
10839
 
                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
10840
 
                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
10841
 
                for(i=startIndex; i<=endIndex; i++) {
10842
 
                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
10843
 
                }
10844
 
            }
10845
 
        }
10846
 
        // Arrow left
10847
 
        else if(nKey == 37) {
10848
 
            // Selecting away from anchor cell
10849
 
            if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
10850
 
                //Select the previous vert block to the left
10851
 
                if(oTrigger.colKeyIndex > 0) {
10852
 
                    // Select in order from anchor to trigger...
10853
 
                    startIndex = oAnchor.trIndex;
10854
 
                    endIndex = oTrigger.trIndex;
10855
 
                    // ...going up
10856
 
                    if(startIndex > endIndex) {
10857
 
                        for(i=startIndex; i>=endIndex; i--) {
10858
 
                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
10859
 
                            this.selectCell(elNew);
10860
 
                        }
10861
 
                    }
10862
 
                    // ... going down
10863
 
                    else {
10864
 
                        for(i=startIndex; i<=endIndex; i++) {
10865
 
                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
10866
 
                            this.selectCell(elNew);
10867
 
                        }
10868
 
                    }
10869
 
                }
10870
 
            }
10871
 
            // Unselecting towards anchor cell
10872
 
            else {
10873
 
                // Unselect the vert block on this column towards the left
10874
 
                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
10875
 
                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
10876
 
                for(i=startIndex; i<=endIndex; i++) {
10877
 
                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
10878
 
                }
10879
 
            }
10880
 
        }
10881
 
    }
10882
 
},
10883
 
 
10884
 
/**
10885
 
 * Determines selection behavior resulting from a mouse event when selection mode
10886
 
 * is set to "cellrange".
10887
 
 *
10888
 
 * @method _handleCellRangeSelectionByMouse
10889
 
 * @param oArgs.event {HTMLEvent} Event object.
10890
 
 * @param oArgs.target {HTMLElement} Target element.
10891
 
 * @private
10892
 
 */
10893
 
_handleCellRangeSelectionByMouse : function(oArgs) {
10894
 
    var elTarget = oArgs.target;
10895
 
 
10896
 
    // Validate target cell
10897
 
    var elTargetCell = this.getTdEl(elTarget);
10898
 
    if(elTargetCell) {
10899
 
        var e = oArgs.event;
10900
 
        var bSHIFT = e.shiftKey;
10901
 
        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
10902
 
 
10903
 
        var elTargetRow = this.getTrEl(elTargetCell);
10904
 
        var nTargetTrIndex = this.getTrIndex(elTargetRow);
10905
 
        var oTargetColumn = this.getColumn(elTargetCell);
10906
 
        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
10907
 
        var oTargetRecord = this.getRecord(elTargetRow);
10908
 
        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
10909
 
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
10910
 
 
10911
 
        var oAnchor = this._getSelectionAnchor();
10912
 
 
10913
 
        var allRows = this.getTbodyEl().rows;
10914
 
        var currentRow, i, j;
10915
 
 
10916
 
        // Both SHIFT and CTRL
10917
 
        if(bSHIFT && bCTRL) {
10918
 
 
10919
 
            // Validate anchor
10920
 
            if(oAnchor) {
10921
 
                // Anchor is selected
10922
 
                if(this.isSelected(oAnchor.cell)) {
10923
 
                    // All cells are on the same row
10924
 
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
10925
 
                        // Select all cells between anchor cell and target cell, including target cell
10926
 
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10927
 
                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
10928
 
                                this.selectCell(elTargetRow.cells[i]);
10929
 
                            }
10930
 
                        }
10931
 
                        // Select all cells between target cell and anchor cell, including target cell
10932
 
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10933
 
                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
10934
 
                                this.selectCell(elTargetRow.cells[i]);
10935
 
                            }
10936
 
                        }
10937
 
                    }
10938
 
                    // Anchor row is above target row
10939
 
                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
10940
 
                        // Select all cells on anchor row from anchor cell to the end of the row
10941
 
                        for(i=oAnchor.colKeyIndex+1; i<elTargetRow.cells.length; i++) {
10942
 
                            this.selectCell(elTargetRow.cells[i]);
10943
 
                        }
10944
 
 
10945
 
                        // Select all cells on all rows between anchor row and target row
10946
 
                        for(i=oAnchor.trIndex+1; i<nTargetTrIndex; i++) {
10947
 
                            for(j=0; j<allRows[i].cells.length; j++){
10948
 
                                this.selectCell(allRows[i].cells[j]);
10949
 
                            }
10950
 
                        }
10951
 
 
10952
 
                        // Select all cells on target row from first cell to the target cell
10953
 
                        for(i=0; i<=nTargetColKeyIndex; i++) {
10954
 
                            this.selectCell(elTargetRow.cells[i]);
10955
 
                        }
10956
 
                    }
10957
 
                    // Anchor row is below target row
10958
 
                    else {
10959
 
                        // Select all cells on target row from target cell to the end of the row
10960
 
                        for(i=nTargetColKeyIndex; i<elTargetRow.cells.length; i++) {
10961
 
                            this.selectCell(elTargetRow.cells[i]);
10962
 
                        }
10963
 
 
10964
 
                        // Select all cells on all rows between target row and anchor row
10965
 
                        for(i=nTargetTrIndex+1; i<oAnchor.trIndex; i++) {
10966
 
                            for(j=0; j<allRows[i].cells.length; j++){
10967
 
                                this.selectCell(allRows[i].cells[j]);
10968
 
                            }
10969
 
                        }
10970
 
 
10971
 
                        // Select all cells on anchor row from first cell to the anchor cell
10972
 
                        for(i=0; i<oAnchor.colKeyIndex; i++) {
10973
 
                            this.selectCell(elTargetRow.cells[i]);
10974
 
                        }
10975
 
                    }
10976
 
                }
10977
 
                // Anchor cell is unselected
10978
 
                else {
10979
 
                    // All cells are on the same row
10980
 
                    if(oAnchor.recordIndex === nTargetRecordIndex) {
10981
 
                        // Unselect all cells between anchor cell and target cell
10982
 
                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10983
 
                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
10984
 
                                this.unselectCell(elTargetRow.cells[i]);
10985
 
                            }
10986
 
                        }
10987
 
                        // Select all cells between target cell and anchor cell
10988
 
                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10989
 
                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
10990
 
                                this.unselectCell(elTargetRow.cells[i]);
10991
 
                            }
10992
 
                        }
10993
 
                    }
10994
 
                    // Anchor row is above target row
10995
 
                    if(oAnchor.recordIndex < nTargetRecordIndex) {
10996
 
                        // Unselect all cells from anchor cell to target cell
10997
 
                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10998
 
                            currentRow = allRows[i];
10999
 
                            for(j=0; j<currentRow.cells.length; j++) {
11000
 
                                // This is the anchor row, only unselect cells after the anchor cell
11001
 
                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
11002
 
                                    if(j>oAnchor.colKeyIndex) {
11003
 
                                        this.unselectCell(currentRow.cells[j]);
11004
 
                                    }
11005
 
                                }
11006
 
                                // This is the target row, only unelect cells before the target cell
11007
 
                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
11008
 
                                    if(j<nTargetColKeyIndex) {
11009
 
                                        this.unselectCell(currentRow.cells[j]);
11010
 
                                    }
11011
 
                                }
11012
 
                                // Unselect all cells on this row
11013
 
                                else {
11014
 
                                    this.unselectCell(currentRow.cells[j]);
11015
 
                                }
11016
 
                            }
11017
 
                        }
11018
 
                    }
11019
 
                    // Anchor row is below target row
11020
 
                    else {
11021
 
                        // Unselect all cells from target cell to anchor cell
11022
 
                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
11023
 
                            currentRow = allRows[i];
11024
 
                            for(j=0; j<currentRow.cells.length; j++) {
11025
 
                                // This is the target row, only unselect cells after the target cell
11026
 
                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
11027
 
                                    if(j>nTargetColKeyIndex) {
11028
 
                                        this.unselectCell(currentRow.cells[j]);
11029
 
                                    }
11030
 
                                }
11031
 
                                // This is the anchor row, only unselect cells before the anchor cell
11032
 
                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
11033
 
                                    if(j<oAnchor.colKeyIndex) {
11034
 
                                        this.unselectCell(currentRow.cells[j]);
11035
 
                                    }
11036
 
                                }
11037
 
                                // Unselect all cells on this row
11038
 
                                else {
11039
 
                                    this.unselectCell(currentRow.cells[j]);
11040
 
                                }
11041
 
                            }
11042
 
                        }
11043
 
                    }
11044
 
 
11045
 
                    // Select the target cell
11046
 
                    this.selectCell(elTargetCell);
11047
 
                }
11048
 
            }
11049
 
            // Invalid anchor
11050
 
            else {
11051
 
                // Set anchor
11052
 
                this._oAnchorCell = oTargetCell;
11053
 
 
11054
 
                // Toggle selection of target
11055
 
                if(this.isSelected(oTargetCell)) {
11056
 
                    this.unselectCell(oTargetCell);
11057
 
                }
11058
 
                else {
11059
 
                    this.selectCell(oTargetCell);
11060
 
                }
11061
 
            }
11062
 
        }
11063
 
         // Only SHIFT
11064
 
        else if(bSHIFT) {
11065
 
 
11066
 
            this.unselectAllCells();
11067
 
 
11068
 
            // Validate anchor
11069
 
            if(oAnchor) {
11070
 
                // All cells are on the same row
11071
 
                if(oAnchor.recordIndex === nTargetRecordIndex) {
11072
 
                    // Select all cells between anchor cell and target cell,
11073
 
                    // including the anchor cell and target cell
11074
 
                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
11075
 
                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
11076
 
                            this.selectCell(elTargetRow.cells[i]);
11077
 
                        }
11078
 
                    }
11079
 
                    // Select all cells between target cell and anchor cell
11080
 
                    // including the target cell and anchor cell
11081
 
                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
11082
 
                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
11083
 
                            this.selectCell(elTargetRow.cells[i]);
11084
 
                        }
11085
 
                    }
11086
 
                }
11087
 
                // Anchor row is above target row
11088
 
                else if(oAnchor.recordIndex < nTargetRecordIndex) {
11089
 
                    // Select all cells from anchor cell to target cell
11090
 
                    // including the anchor cell and target cell
11091
 
                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
11092
 
                        currentRow = allRows[i];
11093
 
                        for(j=0; j<currentRow.cells.length; j++) {
11094
 
                            // This is the anchor row, only select the anchor cell and after
11095
 
                            if(currentRow.sectionRowIndex == oAnchor.trIndex) {
11096
 
                                if(j>=oAnchor.colKeyIndex) {
11097
 
                                    this.selectCell(currentRow.cells[j]);
11098
 
                                }
11099
 
                            }
11100
 
                            // This is the target row, only select the target cell and before
11101
 
                            else if(currentRow.sectionRowIndex == nTargetTrIndex) {
11102
 
                                if(j<=nTargetColKeyIndex) {
11103
 
                                    this.selectCell(currentRow.cells[j]);
11104
 
                                }
11105
 
                            }
11106
 
                            // Select all cells on this row
11107
 
                            else {
11108
 
                                this.selectCell(currentRow.cells[j]);
11109
 
                            }
11110
 
                        }
11111
 
                    }
11112
 
                }
11113
 
                // Anchor row is below target row
11114
 
                else {
11115
 
                    // Select all cells from target cell to anchor cell,
11116
 
                    // including the target cell and anchor cell
11117
 
                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
11118
 
                        currentRow = allRows[i];
11119
 
                        for(j=0; j<currentRow.cells.length; j++) {
11120
 
                            // This is the target row, only select the target cell and after
11121
 
                            if(currentRow.sectionRowIndex == nTargetTrIndex) {
11122
 
                                if(j>=nTargetColKeyIndex) {
11123
 
                                    this.selectCell(currentRow.cells[j]);
11124
 
                                }
11125
 
                            }
11126
 
                            // This is the anchor row, only select the anchor cell and before
11127
 
                            else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
11128
 
                                if(j<=oAnchor.colKeyIndex) {
11129
 
                                    this.selectCell(currentRow.cells[j]);
11130
 
                                }
11131
 
                            }
11132
 
                            // Select all cells on this row
11133
 
                            else {
11134
 
                                this.selectCell(currentRow.cells[j]);
11135
 
                            }
11136
 
                        }
11137
 
                    }
11138
 
                }
11139
 
            }
11140
 
            // Invalid anchor
11141
 
            else {
11142
 
                // Set anchor
11143
 
                this._oAnchorCell = oTargetCell;
11144
 
 
11145
 
                // Select target only
11146
 
                this.selectCell(oTargetCell);
11147
 
            }
11148
 
 
11149
 
 
11150
 
        }
11151
 
        // Only CTRL
11152
 
        else if(bCTRL) {
11153
 
 
11154
 
            // Set anchor
11155
 
            this._oAnchorCell = oTargetCell;
11156
 
 
11157
 
            // Toggle selection of target
11158
 
            if(this.isSelected(oTargetCell)) {
11159
 
                this.unselectCell(oTargetCell);
11160
 
            }
11161
 
            else {
11162
 
                this.selectCell(oTargetCell);
11163
 
            }
11164
 
 
11165
 
        }
11166
 
        // Neither SHIFT nor CTRL
11167
 
        else {
11168
 
            this._handleSingleCellSelectionByMouse(oArgs);
11169
 
        }
11170
 
    }
11171
 
},
11172
 
 
11173
 
/**
11174
 
 * Determines selection behavior resulting from a key event when selection mode
11175
 
 * is set to "cellrange".
11176
 
 *
11177
 
 * @method _handleCellRangeSelectionByKey
11178
 
 * @param e {HTMLEvent} Event object.
11179
 
 * @private
11180
 
 */
11181
 
_handleCellRangeSelectionByKey : function(e) {
11182
 
    var nKey = Ev.getCharCode(e);
11183
 
    var bSHIFT = e.shiftKey;
11184
 
    if((nKey == 9) || !bSHIFT) {
11185
 
        this._handleSingleCellSelectionByKey(e);
11186
 
        return;
11187
 
    }
11188
 
 
11189
 
    if((nKey > 36) && (nKey < 41)) {
11190
 
        // Validate trigger
11191
 
        var oTrigger = this._getSelectionTrigger();
11192
 
        // Arrow selection only works if last selected row is on current page
11193
 
        if(!oTrigger) {
11194
 
            return null;
11195
 
        }
11196
 
 
11197
 
        Ev.stopEvent(e);
11198
 
 
11199
 
        // Validate anchor
11200
 
        var oAnchor = this._getSelectionAnchor(oTrigger);
11201
 
 
11202
 
        var i, elNewRow, elNew;
11203
 
        var allRows = this.getTbodyEl().rows;
11204
 
        var elThisRow = oTrigger.el.parentNode;
11205
 
 
11206
 
        // Arrow down
11207
 
        if(nKey == 40) {
11208
 
            elNewRow = this.getNextTrEl(oTrigger.el);
11209
 
 
11210
 
            // Selecting away from anchor cell
11211
 
            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
11212
 
                // Select all cells to the end of this row
11213
 
                for(i=oTrigger.colKeyIndex+1; i<elThisRow.cells.length; i++){
11214
 
                    elNew = elThisRow.cells[i];
11215
 
                    this.selectCell(elNew);
11216
 
                }
11217
 
 
11218
 
                // Select some of the cells on the next row down
11219
 
                if(elNewRow) {
11220
 
                    for(i=0; i<=oTrigger.colKeyIndex; i++){
11221
 
                        elNew = elNewRow.cells[i];
11222
 
                        this.selectCell(elNew);
11223
 
                    }
11224
 
                }
11225
 
            }
11226
 
            // Unselecting towards anchor cell
11227
 
            else {
11228
 
                // Unselect all cells to the end of this row
11229
 
                for(i=oTrigger.colKeyIndex; i<elThisRow.cells.length; i++){
11230
 
                    this.unselectCell(elThisRow.cells[i]);
11231
 
                }
11232
 
 
11233
 
                // Unselect some of the cells on the next row down
11234
 
                if(elNewRow) {
11235
 
                    for(i=0; i<oTrigger.colKeyIndex; i++){
11236
 
                        this.unselectCell(elNewRow.cells[i]);
11237
 
                    }
11238
 
                }
11239
 
            }
11240
 
        }
11241
 
        // Arrow up
11242
 
        else if(nKey == 38) {
11243
 
            elNewRow = this.getPreviousTrEl(oTrigger.el);
11244
 
 
11245
 
            // Selecting away from anchor cell
11246
 
            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
11247
 
                // Select all the cells to the beginning of this row
11248
 
                for(i=oTrigger.colKeyIndex-1; i>-1; i--){
11249
 
                    elNew = elThisRow.cells[i];
11250
 
                    this.selectCell(elNew);
11251
 
                }
11252
 
 
11253
 
                // Select some of the cells from the end of the previous row
11254
 
                if(elNewRow) {
11255
 
                    for(i=elThisRow.cells.length-1; i>=oTrigger.colKeyIndex; i--){
11256
 
                        elNew = elNewRow.cells[i];
11257
 
                        this.selectCell(elNew);
11258
 
                    }
11259
 
                }
11260
 
            }
11261
 
            // Unselecting towards anchor cell
11262
 
            else {
11263
 
                // Unselect all the cells to the beginning of this row
11264
 
                for(i=oTrigger.colKeyIndex; i>-1; i--){
11265
 
                    this.unselectCell(elThisRow.cells[i]);
11266
 
                }
11267
 
 
11268
 
                // Unselect some of the cells from the end of the previous row
11269
 
                if(elNewRow) {
11270
 
                    for(i=elThisRow.cells.length-1; i>oTrigger.colKeyIndex; i--){
11271
 
                        this.unselectCell(elNewRow.cells[i]);
11272
 
                    }
11273
 
                }
11274
 
            }
11275
 
        }
11276
 
        // Arrow right
11277
 
        else if(nKey == 39) {
11278
 
            elNewRow = this.getNextTrEl(oTrigger.el);
11279
 
 
11280
 
            // Selecting away from anchor cell
11281
 
            if(oAnchor.recordIndex < oTrigger.recordIndex) {
11282
 
                // Select the next cell to the right
11283
 
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
11284
 
                    elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
11285
 
                    this.selectCell(elNew);
11286
 
                }
11287
 
                // Select the first cell of the next row
11288
 
                else if(elNewRow) {
11289
 
                    elNew = elNewRow.cells[0];
11290
 
                    this.selectCell(elNew);
11291
 
                }
11292
 
            }
11293
 
            // Unselecting towards anchor cell
11294
 
            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
11295
 
                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11296
 
 
11297
 
                // Unselect this cell towards the right
11298
 
                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
11299
 
                }
11300
 
                // Unselect this cells towards the first cell of the next row
11301
 
                else {
11302
 
                }
11303
 
            }
11304
 
            // Anchor is on this row
11305
 
            else {
11306
 
                // Selecting away from anchor
11307
 
                if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
11308
 
                    // Select the next cell to the right
11309
 
                    if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
11310
 
                        elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
11311
 
                        this.selectCell(elNew);
11312
 
                    }
11313
 
                    // Select the first cell on the next row
11314
 
                    else if(oTrigger.trIndex < allRows.length-1){
11315
 
                        elNew = elNewRow.cells[0];
11316
 
                        this.selectCell(elNew);
11317
 
                    }
11318
 
                }
11319
 
                // Unselecting towards anchor
11320
 
                else {
11321
 
                    // Unselect this cell towards the right
11322
 
                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11323
 
                }
11324
 
            }
11325
 
        }
11326
 
        // Arrow left
11327
 
        else if(nKey == 37) {
11328
 
            elNewRow = this.getPreviousTrEl(oTrigger.el);
11329
 
 
11330
 
            // Unselecting towards the anchor
11331
 
            if(oAnchor.recordIndex < oTrigger.recordIndex) {
11332
 
                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11333
 
 
11334
 
                // Unselect this cell towards the left
11335
 
                if(oTrigger.colKeyIndex > 0) {
11336
 
                }
11337
 
                // Unselect this cell towards the last cell of the previous row
11338
 
                else {
11339
 
                }
11340
 
            }
11341
 
            // Selecting towards the anchor
11342
 
            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
11343
 
                // Select the next cell to the left
11344
 
                if(oTrigger.colKeyIndex > 0) {
11345
 
                    elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
11346
 
                    this.selectCell(elNew);
11347
 
                }
11348
 
                // Select the last cell of the previous row
11349
 
                else if(oTrigger.trIndex > 0){
11350
 
                    elNew = elNewRow.cells[elNewRow.cells.length-1];
11351
 
                    this.selectCell(elNew);
11352
 
                }
11353
 
            }
11354
 
            // Anchor is on this row
11355
 
            else {
11356
 
                // Selecting away from anchor cell
11357
 
                if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
11358
 
                    // Select the next cell to the left
11359
 
                    if(oTrigger.colKeyIndex > 0) {
11360
 
                        elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
11361
 
                        this.selectCell(elNew);
11362
 
                    }
11363
 
                    // Select the last cell of the previous row
11364
 
                    else if(oTrigger.trIndex > 0){
11365
 
                        elNew = elNewRow.cells[elNewRow.cells.length-1];
11366
 
                        this.selectCell(elNew);
11367
 
                    }
11368
 
                }
11369
 
                // Unselecting towards anchor cell
11370
 
                else {
11371
 
                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11372
 
 
11373
 
                    // Unselect this cell towards the left
11374
 
                    if(oTrigger.colKeyIndex > 0) {
11375
 
                    }
11376
 
                    // Unselect this cell towards the last cell of the previous row
11377
 
                    else {
11378
 
                    }
11379
 
                }
11380
 
            }
11381
 
        }
11382
 
    }
11383
 
},
11384
 
 
11385
 
/**
11386
 
 * Determines selection behavior resulting from a mouse event when selection mode
11387
 
 * is set to "singlecell".
11388
 
 *
11389
 
 * @method _handleSingleCellSelectionByMouse
11390
 
 * @param oArgs.event {HTMLEvent} Event object.
11391
 
 * @param oArgs.target {HTMLElement} Target element.
11392
 
 * @private
11393
 
 */
11394
 
_handleSingleCellSelectionByMouse : function(oArgs) {
11395
 
    var elTarget = oArgs.target;
11396
 
 
11397
 
    // Validate target cell
11398
 
    var elTargetCell = this.getTdEl(elTarget);
11399
 
    if(elTargetCell) {
11400
 
        var elTargetRow = this.getTrEl(elTargetCell);
11401
 
        var oTargetRecord = this.getRecord(elTargetRow);
11402
 
        var oTargetColumn = this.getColumn(elTargetCell);
11403
 
        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
11404
 
 
11405
 
        // Set anchor
11406
 
        this._oAnchorCell = oTargetCell;
11407
 
 
11408
 
        // Select only target
11409
 
        this.unselectAllCells();
11410
 
        this.selectCell(oTargetCell);
11411
 
    }
11412
 
},
11413
 
 
11414
 
/**
11415
 
 * Determines selection behavior resulting from a key event when selection mode
11416
 
 * is set to "singlecell".
11417
 
 *
11418
 
 * @method _handleSingleCellSelectionByKey
11419
 
 * @param e {HTMLEvent} Event object.
11420
 
 * @private
11421
 
 */
11422
 
_handleSingleCellSelectionByKey : function(e) {
11423
 
    var nKey = Ev.getCharCode(e);
11424
 
    if((nKey == 9) || ((nKey > 36) && (nKey < 41))) {
11425
 
        var bSHIFT = e.shiftKey;
11426
 
 
11427
 
        // Validate trigger
11428
 
        var oTrigger = this._getSelectionTrigger();
11429
 
        // Arrow selection only works if last selected row is on current page
11430
 
        if(!oTrigger) {
11431
 
            return null;
11432
 
        }
11433
 
 
11434
 
        // Determine the new cell to select
11435
 
        var elNew;
11436
 
        if(nKey == 40) { // Arrow down
11437
 
            elNew = this.getBelowTdEl(oTrigger.el);
11438
 
 
11439
 
            // Validate new cell
11440
 
            if(elNew === null) {
11441
 
                //TODO: wrap around to first tr on current page
11442
 
 
11443
 
                //TODO: wrap forward to first tr of next page
11444
 
 
11445
 
                // Bottom selection is sticky
11446
 
                elNew = oTrigger.el;
11447
 
            }
11448
 
        }
11449
 
        else if(nKey == 38) { // Arrow up
11450
 
            elNew = this.getAboveTdEl(oTrigger.el);
11451
 
 
11452
 
            // Validate new cell
11453
 
            if(elNew === null) {
11454
 
                //TODO: wrap around to last tr on current page
11455
 
 
11456
 
                //TODO: wrap back to last tr of previous page
11457
 
 
11458
 
                // Top selection is sticky
11459
 
                elNew = oTrigger.el;
11460
 
            }
11461
 
        }
11462
 
        else if((nKey == 39) || (!bSHIFT && (nKey == 9))) { // Arrow right or tab
11463
 
            elNew = this.getNextTdEl(oTrigger.el);
11464
 
 
11465
 
            // Validate new cell
11466
 
            if(elNew === null) {
11467
 
                //TODO: wrap around to first td on current page
11468
 
 
11469
 
                //TODO: wrap forward to first td of next page
11470
 
 
11471
 
                // Top-left selection is sticky, and release TAB focus
11472
 
                //elNew = oTrigger.el;
11473
 
                return;
11474
 
            }
11475
 
        }
11476
 
        else if((nKey == 37) || (bSHIFT && (nKey == 9))) { // Arrow left or shift-tab
11477
 
            elNew = this.getPreviousTdEl(oTrigger.el);
11478
 
 
11479
 
            // Validate new cell
11480
 
            if(elNew === null) {
11481
 
                //TODO: wrap around to last td on current page
11482
 
 
11483
 
                //TODO: wrap back to last td of previous page
11484
 
 
11485
 
                // Bottom-right selection is sticky, and release TAB focus
11486
 
                //elNew = oTrigger.el;
11487
 
                return;
11488
 
            }
11489
 
        }
11490
 
 
11491
 
        Ev.stopEvent(e);
11492
 
        
11493
 
        // Unselect all cells
11494
 
        this.unselectAllCells();
11495
 
 
11496
 
        // Select the new cell
11497
 
        this.selectCell(elNew);
11498
 
 
11499
 
        // Set new anchor
11500
 
        this._oAnchorCell = {record:this.getRecord(elNew), column:this.getColumn(elNew)};
11501
 
    }
11502
 
},
11503
 
 
11504
 
/**
11505
 
 * Returns array of selected TR elements on the page.
11506
 
 *
11507
 
 * @method getSelectedTrEls
11508
 
 * @return {HTMLElement[]} Array of selected TR elements.
11509
 
 */
11510
 
getSelectedTrEls : function() {
11511
 
    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
11512
 
},
11513
 
 
11514
 
/**
11515
 
 * Sets given row to the selected state.
11516
 
 *
11517
 
 * @method selectRow
11518
 
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
11519
 
 * reference or ID string, Record instance, or RecordSet position index.
11520
 
 */
11521
 
selectRow : function(row) {
11522
 
    var oRecord, elRow;
11523
 
 
11524
 
    if(row instanceof YAHOO.widget.Record) {
11525
 
        oRecord = this._oRecordSet.getRecord(row);
11526
 
        elRow = this.getTrEl(oRecord);
11527
 
    }
11528
 
    else if(lang.isNumber(row)) {
11529
 
        oRecord = this.getRecord(row);
11530
 
        elRow = this.getTrEl(oRecord);
11531
 
    }
11532
 
    else {
11533
 
        elRow = this.getTrEl(row);
11534
 
        oRecord = this.getRecord(elRow);
11535
 
    }
11536
 
 
11537
 
    if(oRecord) {
11538
 
        // Update selection trackers
11539
 
        var tracker = this._aSelections || [];
11540
 
        var sRecordId = oRecord.getId();
11541
 
        var index = -1;
11542
 
 
11543
 
        // Remove if already there:
11544
 
        // Use Array.indexOf if available...
11545
 
        /*if(tracker.indexOf && (tracker.indexOf(sRecordId) >  -1)) {
11546
 
            tracker.splice(tracker.indexOf(sRecordId),1);
11547
 
        }*/
11548
 
        if(tracker.indexOf) {
11549
 
            index = tracker.indexOf(sRecordId);
11550
 
            
11551
 
        }
11552
 
        // ...or do it the old-fashioned way
11553
 
        else {
11554
 
            for(var j=tracker.length-1; j>-1; j--) {
11555
 
                if(tracker[j] === sRecordId){
11556
 
                    index = j;
11557
 
                    break;
11558
 
                }
11559
 
            }
11560
 
        }
11561
 
        if(index > -1) {
11562
 
            tracker.splice(index,1);
11563
 
        }
11564
 
        
11565
 
        // Add to the end
11566
 
        tracker.push(sRecordId);
11567
 
        this._aSelections = tracker;
11568
 
 
11569
 
        // Update trackers
11570
 
        if(!this._oAnchorRecord) {
11571
 
            this._oAnchorRecord = oRecord;
11572
 
        }
11573
 
 
11574
 
        // Update UI
11575
 
        if(elRow) {
11576
 
            Dom.addClass(elRow, DT.CLASS_SELECTED);
11577
 
        }
11578
 
 
11579
 
        this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
11580
 
        YAHOO.log("Selected " + elRow, "info", this.toString());
11581
 
    }
11582
 
    else {
11583
 
        YAHOO.log("Could not select row " + row, "warn", this.toString());
11584
 
    }
11585
 
},
11586
 
 
11587
 
/**
11588
 
 * Sets given row to the unselected state.
11589
 
 *
11590
 
 * @method unselectRow
11591
 
 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
11592
 
 * reference or ID string, Record instance, or RecordSet position index.
11593
 
 */
11594
 
unselectRow : function(row) {
11595
 
    var elRow = this.getTrEl(row);
11596
 
 
11597
 
    var oRecord;
11598
 
    if(row instanceof YAHOO.widget.Record) {
11599
 
        oRecord = this._oRecordSet.getRecord(row);
11600
 
    }
11601
 
    else if(lang.isNumber(row)) {
11602
 
        oRecord = this.getRecord(row);
11603
 
    }
11604
 
    else {
11605
 
        oRecord = this.getRecord(elRow);
11606
 
    }
11607
 
 
11608
 
    if(oRecord) {
11609
 
        // Update selection trackers
11610
 
        var tracker = this._aSelections || [];
11611
 
        var sRecordId = oRecord.getId();
11612
 
        var index = -1;
11613
 
 
11614
 
        // Use Array.indexOf if available...
11615
 
        if(tracker.indexOf) {
11616
 
            index = tracker.indexOf(sRecordId);
11617
 
        }
11618
 
        // ...or do it the old-fashioned way
11619
 
        else {
11620
 
            for(var j=tracker.length-1; j>-1; j--) {
11621
 
                if(tracker[j] === sRecordId){
11622
 
                    index = j;
11623
 
                    break;
11624
 
                }
11625
 
            }
11626
 
        }
11627
 
        if(index > -1) {
11628
 
            // Update tracker
11629
 
            tracker.splice(index,1);
11630
 
            this._aSelections = tracker;
11631
 
 
11632
 
            // Update the UI
11633
 
            Dom.removeClass(elRow, DT.CLASS_SELECTED);
11634
 
 
11635
 
            this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
11636
 
            YAHOO.log("Unselected " + elRow, "info", this.toString());
11637
 
 
11638
 
            return;
11639
 
        }
11640
 
    }
11641
 
    YAHOO.log("Could not unselect row " + row, "warn", this.toString());
11642
 
},
11643
 
 
11644
 
/**
11645
 
 * Clears out all row selections.
11646
 
 *
11647
 
 * @method unselectAllRows
11648
 
 */
11649
 
unselectAllRows : function() {
11650
 
    // Remove all rows from tracker
11651
 
    var tracker = this._aSelections || [],
11652
 
        recId,
11653
 
        removed = [];
11654
 
    for(var j=tracker.length-1; j>-1; j--) {
11655
 
       if(lang.isString(tracker[j])){
11656
 
            recId = tracker.splice(j,1);
11657
 
            removed[removed.length] = this.getRecord(lang.isArray(recId) ? recId[0] : recId);
11658
 
        }
11659
 
    }
11660
 
 
11661
 
    // Update tracker
11662
 
    this._aSelections = tracker;
11663
 
 
11664
 
    // Update UI
11665
 
    this._unselectAllTrEls();
11666
 
 
11667
 
    this.fireEvent("unselectAllRowsEvent", {records: removed});
11668
 
    YAHOO.log("Unselected all rows", "info", this.toString());
11669
 
},
11670
 
 
11671
 
/**
11672
 
 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
11673
 
 * from all TD elements in the internal tracker.
11674
 
 *
11675
 
 * @method _unselectAllTdEls
11676
 
 * @private
11677
 
 */
11678
 
_unselectAllTdEls : function() {
11679
 
    var selectedCells = Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
11680
 
    Dom.removeClass(selectedCells, DT.CLASS_SELECTED);
11681
 
},
11682
 
 
11683
 
/**
11684
 
 * Returns array of selected TD elements on the page.
11685
 
 *
11686
 
 * @method getSelectedTdEls
11687
 
 * @return {HTMLElement[]} Array of selected TD elements.
11688
 
 */
11689
 
getSelectedTdEls : function() {
11690
 
    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
11691
 
},
11692
 
 
11693
 
/**
11694
 
 * Sets given cell to the selected state.
11695
 
 *
11696
 
 * @method selectCell
11697
 
 * @param cell {HTMLElement | String} DOM element reference or ID string
11698
 
 * to DataTable page element or RecordSet index.
11699
 
 */
11700
 
selectCell : function(cell) {
11701
 
//TODO: accept {record} in selectCell()
11702
 
    var elCell = this.getTdEl(cell);
11703
 
 
11704
 
    if(elCell) {
11705
 
        var oRecord = this.getRecord(elCell);
11706
 
        var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
11707
 
 
11708
 
        if(oRecord && sColumnKey) {
11709
 
            // Get Record ID
11710
 
            var tracker = this._aSelections || [];
11711
 
            var sRecordId = oRecord.getId();
11712
 
 
11713
 
            // Remove if there
11714
 
            for(var j=tracker.length-1; j>-1; j--) {
11715
 
               if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
11716
 
                    tracker.splice(j,1);
11717
 
                    break;
11718
 
                }
11719
 
            }
11720
 
 
11721
 
            // Add to the end
11722
 
            tracker.push({recordId:sRecordId, columnKey:sColumnKey});
11723
 
 
11724
 
            // Update trackers
11725
 
            this._aSelections = tracker;
11726
 
            if(!this._oAnchorCell) {
11727
 
                this._oAnchorCell = {record:oRecord, column:this.getColumn(sColumnKey)};
11728
 
            }
11729
 
 
11730
 
            // Update the UI
11731
 
            Dom.addClass(elCell, DT.CLASS_SELECTED);
11732
 
 
11733
 
            this.fireEvent("cellSelectEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key: this.getColumn(elCell.cellIndex).getKey(), el:elCell});
11734
 
            YAHOO.log("Selected " + elCell, "info", this.toString());
11735
 
            return;
11736
 
        }
11737
 
    }
11738
 
    YAHOO.log("Could not select cell " + cell, "warn", this.toString());
11739
 
},
11740
 
 
11741
 
/**
11742
 
 * Sets given cell to the unselected state.
11743
 
 *
11744
 
 * @method unselectCell
11745
 
 * @param cell {HTMLElement | String} DOM element reference or ID string
11746
 
 * to DataTable page element or RecordSet index.
11747
 
 */
11748
 
unselectCell : function(cell) {
11749
 
    var elCell = this.getTdEl(cell);
11750
 
 
11751
 
    if(elCell) {
11752
 
        var oRecord = this.getRecord(elCell);
11753
 
        var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
11754
 
 
11755
 
        if(oRecord && sColumnKey) {
11756
 
            // Get Record ID
11757
 
            var tracker = this._aSelections || [];
11758
 
            var id = oRecord.getId();
11759
 
 
11760
 
            // Is it selected?
11761
 
            for(var j=tracker.length-1; j>-1; j--) {
11762
 
                if((tracker[j].recordId === id) && (tracker[j].columnKey === sColumnKey)){
11763
 
                    // Remove from tracker
11764
 
                    tracker.splice(j,1);
11765
 
 
11766
 
                    // Update tracker
11767
 
                    this._aSelections = tracker;
11768
 
 
11769
 
                    // Update the UI
11770
 
                    Dom.removeClass(elCell, DT.CLASS_SELECTED);
11771
 
 
11772
 
                    this.fireEvent("cellUnselectEvent", {record:oRecord, column: this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
11773
 
                    YAHOO.log("Unselected " + elCell, "info", this.toString());
11774
 
                    return;
11775
 
                }
11776
 
            }
11777
 
        }
11778
 
    }
11779
 
    YAHOO.log("Could not unselect cell " + cell, "warn", this.toString());
11780
 
},
11781
 
 
11782
 
/**
11783
 
 * Clears out all cell selections.
11784
 
 *
11785
 
 * @method unselectAllCells
11786
 
 */
11787
 
unselectAllCells : function() {
11788
 
    // Remove all cells from tracker
11789
 
    var tracker = this._aSelections || [];
11790
 
    for(var j=tracker.length-1; j>-1; j--) {
11791
 
       if(lang.isObject(tracker[j])){
11792
 
            tracker.splice(j,1);
11793
 
        }
11794
 
    }
11795
 
 
11796
 
    // Update tracker
11797
 
    this._aSelections = tracker;
11798
 
 
11799
 
    // Update UI
11800
 
    this._unselectAllTdEls();
11801
 
 
11802
 
    //TODO: send data to unselectAllCellsEvent handler
11803
 
    this.fireEvent("unselectAllCellsEvent");
11804
 
    YAHOO.log("Unselected all cells", "info", this.toString());
11805
 
},
11806
 
 
11807
 
/**
11808
 
 * Returns true if given item is selected, false otherwise.
11809
 
 *
11810
 
 * @method isSelected
11811
 
 * @param o {String | HTMLElement | YAHOO.widget.Record | Number
11812
 
 * {record:YAHOO.widget.Record, column:YAHOO.widget.Column} } TR or TD element by
11813
 
 * reference or ID string, a Record instance, a RecordSet position index,
11814
 
 * or an object literal representation
11815
 
 * of a cell.
11816
 
 * @return {Boolean} True if item is selected.
11817
 
 */
11818
 
isSelected : function(o) {
11819
 
    if(o && (o.ownerDocument == document)) {
11820
 
        return (Dom.hasClass(this.getTdEl(o),DT.CLASS_SELECTED) || Dom.hasClass(this.getTrEl(o),DT.CLASS_SELECTED));
11821
 
    }
11822
 
    else {
11823
 
        var oRecord, sRecordId, j;
11824
 
        var tracker = this._aSelections;
11825
 
        if(tracker && tracker.length > 0) {
11826
 
            // Looking for a Record?
11827
 
            if(o instanceof YAHOO.widget.Record) {
11828
 
                oRecord = o;
11829
 
            }
11830
 
            else if(lang.isNumber(o)) {
11831
 
                oRecord = this.getRecord(o);
11832
 
            }
11833
 
            if(oRecord) {
11834
 
                sRecordId = oRecord.getId();
11835
 
 
11836
 
                // Is it there?
11837
 
                // Use Array.indexOf if available...
11838
 
                if(tracker.indexOf) {
11839
 
                    if(tracker.indexOf(sRecordId) >  -1) {
11840
 
                        return true;
11841
 
                    }
11842
 
                }
11843
 
                // ...or do it the old-fashioned way
11844
 
                else {
11845
 
                    for(j=tracker.length-1; j>-1; j--) {
11846
 
                       if(tracker[j] === sRecordId){
11847
 
                        return true;
11848
 
                       }
11849
 
                    }
11850
 
                }
11851
 
            }
11852
 
            // Looking for a cell
11853
 
            else if(o.record && o.column){
11854
 
                sRecordId = o.record.getId();
11855
 
                var sColumnKey = o.column.getKey();
11856
 
 
11857
 
                for(j=tracker.length-1; j>-1; j--) {
11858
 
                    if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
11859
 
                        return true;
11860
 
                    }
11861
 
                }
11862
 
            }
11863
 
        }
11864
 
    }
11865
 
    return false;
11866
 
},
11867
 
 
11868
 
/**
11869
 
 * Returns selected rows as an array of Record IDs.
11870
 
 *
11871
 
 * @method getSelectedRows
11872
 
 * @return {String[]} Array of selected rows by Record ID.
11873
 
 */
11874
 
getSelectedRows : function() {
11875
 
    var aSelectedRows = [];
11876
 
    var tracker = this._aSelections || [];
11877
 
    for(var j=0; j<tracker.length; j++) {
11878
 
       if(lang.isString(tracker[j])){
11879
 
            aSelectedRows.push(tracker[j]);
11880
 
        }
11881
 
    }
11882
 
    return aSelectedRows;
11883
 
},
11884
 
 
11885
 
/**
11886
 
 * Returns selected cells as an array of object literals:
11887
 
 *     {recordId:sRecordId, columnKey:sColumnKey}.
11888
 
 *
11889
 
 * @method getSelectedCells
11890
 
 * @return {Object[]} Array of selected cells by Record ID and Column ID.
11891
 
 */
11892
 
getSelectedCells : function() {
11893
 
    var aSelectedCells = [];
11894
 
    var tracker = this._aSelections || [];
11895
 
    for(var j=0; j<tracker.length; j++) {
11896
 
       if(tracker[j] && lang.isObject(tracker[j])){
11897
 
            aSelectedCells.push(tracker[j]);
11898
 
        }
11899
 
    }
11900
 
    return aSelectedCells;
11901
 
},
11902
 
 
11903
 
/**
11904
 
 * Returns last selected Record ID.
11905
 
 *
11906
 
 * @method getLastSelectedRecord
11907
 
 * @return {String} Record ID of last selected row.
11908
 
 */
11909
 
getLastSelectedRecord : function() {
11910
 
    var tracker = this._aSelections;
11911
 
    if(tracker && tracker.length > 0) {
11912
 
        for(var i=tracker.length-1; i>-1; i--) {
11913
 
           if(lang.isString(tracker[i])){
11914
 
                return tracker[i];
11915
 
            }
11916
 
        }
11917
 
    }
11918
 
},
11919
 
 
11920
 
/**
11921
 
 * Returns last selected cell as an object literal:
11922
 
 *     {recordId:sRecordId, columnKey:sColumnKey}.
11923
 
 *
11924
 
 * @method getLastSelectedCell
11925
 
 * @return {Object} Object literal representation of a cell.
11926
 
 */
11927
 
getLastSelectedCell : function() {
11928
 
    var tracker = this._aSelections;
11929
 
    if(tracker && tracker.length > 0) {
11930
 
        for(var i=tracker.length-1; i>-1; i--) {
11931
 
           if(tracker[i].recordId && tracker[i].columnKey){
11932
 
                return tracker[i];
11933
 
            }
11934
 
        }
11935
 
    }
11936
 
},
11937
 
 
11938
 
/**
11939
 
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row.
11940
 
 *
11941
 
 * @method highlightRow
11942
 
 * @param row {HTMLElement | String} DOM element reference or ID string.
11943
 
 */
11944
 
highlightRow : function(row) {
11945
 
    var elRow = this.getTrEl(row);
11946
 
 
11947
 
    if(elRow) {
11948
 
        // Make sure previous row is unhighlighted
11949
 
/*        if(this._sLastHighlightedTrElId) {
11950
 
            Dom.removeClass(this._sLastHighlightedTrElId,DT.CLASS_HIGHLIGHTED);
11951
 
        }*/
11952
 
        var oRecord = this.getRecord(elRow);
11953
 
        Dom.addClass(elRow,DT.CLASS_HIGHLIGHTED);
11954
 
        //this._sLastHighlightedTrElId = elRow.id;
11955
 
        this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow});
11956
 
        YAHOO.log("Highlighted " + elRow, "info", this.toString());
11957
 
        return;
11958
 
    }
11959
 
    YAHOO.log("Could not highlight row " + row, "warn", this.toString());
11960
 
},
11961
 
 
11962
 
/**
11963
 
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row.
11964
 
 *
11965
 
 * @method unhighlightRow
11966
 
 * @param row {HTMLElement | String} DOM element reference or ID string.
11967
 
 */
11968
 
unhighlightRow : function(row) {
11969
 
    var elRow = this.getTrEl(row);
11970
 
 
11971
 
    if(elRow) {
11972
 
        var oRecord = this.getRecord(elRow);
11973
 
        Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED);
11974
 
        this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
11975
 
        YAHOO.log("Unhighlighted " + elRow, "info", this.toString());
11976
 
        return;
11977
 
    }
11978
 
    YAHOO.log("Could not unhighlight row " + row, "warn", this.toString());
11979
 
},
11980
 
 
11981
 
/**
11982
 
 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell.
11983
 
 *
11984
 
 * @method highlightCell
11985
 
 * @param cell {HTMLElement | String} DOM element reference or ID string.
11986
 
 */
11987
 
highlightCell : function(cell) {
11988
 
    var elCell = this.getTdEl(cell);
11989
 
 
11990
 
    if(elCell) {
11991
 
        // Make sure previous cell is unhighlighted
11992
 
        if(this._elLastHighlightedTd) {
11993
 
            this.unhighlightCell(this._elLastHighlightedTd);
11994
 
        }
11995
 
 
11996
 
        var oRecord = this.getRecord(elCell);
11997
 
        var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
11998
 
        Dom.addClass(elCell,DT.CLASS_HIGHLIGHTED);
11999
 
        this._elLastHighlightedTd = elCell;
12000
 
        this.fireEvent("cellHighlightEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
12001
 
        YAHOO.log("Highlighted " + elCell, "info", this.toString());
12002
 
        return;
12003
 
    }
12004
 
    YAHOO.log("Could not highlight cell " + cell, "warn", this.toString());
12005
 
},
12006
 
 
12007
 
/**
12008
 
 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell.
12009
 
 *
12010
 
 * @method unhighlightCell
12011
 
 * @param cell {HTMLElement | String} DOM element reference or ID string.
12012
 
 */
12013
 
unhighlightCell : function(cell) {
12014
 
    var elCell = this.getTdEl(cell);
12015
 
 
12016
 
    if(elCell) {
12017
 
        var oRecord = this.getRecord(elCell);
12018
 
        Dom.removeClass(elCell,DT.CLASS_HIGHLIGHTED);
12019
 
        this._elLastHighlightedTd = null;
12020
 
        this.fireEvent("cellUnhighlightEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
12021
 
        YAHOO.log("Unhighlighted " + elCell, "info", this.toString());
12022
 
        return;
12023
 
    }
12024
 
    YAHOO.log("Could not unhighlight cell " + cell, "warn", this.toString());
12025
 
},
12026
 
 
12027
 
 
12028
 
 
12029
 
 
12030
 
 
12031
 
 
12032
 
 
12033
 
 
12034
 
 
12035
 
 
12036
 
 
12037
 
 
12038
 
 
12039
 
 
12040
 
 
12041
 
 
12042
 
 
12043
 
 
12044
 
 
12045
 
 
12046
 
 
12047
 
 
12048
 
 
12049
 
 
12050
 
 
12051
 
 
12052
 
 
12053
 
 
12054
 
 
12055
 
 
12056
 
 
12057
 
 
12058
 
 
12059
 
 
12060
 
 
12061
 
 
12062
 
 
12063
 
 
12064
 
 
12065
 
 
12066
 
 
12067
 
 
12068
 
 
12069
 
 
12070
 
 
12071
 
// INLINE EDITING
12072
 
 
12073
 
/**
12074
 
 * Returns current CellEditor instance, or null.
12075
 
 * @method getCellEditor
12076
 
 * @return {YAHOO.widget.CellEditor} CellEditor instance.
12077
 
 */
12078
 
getCellEditor : function() {
12079
 
    return this._oCellEditor;
12080
 
},
12081
 
 
12082
 
 
12083
 
/**
12084
 
 * Activates and shows CellEditor instance for the given cell while deactivating and
12085
 
 * canceling previous CellEditor. It is baked into DataTable that only one CellEditor
12086
 
 * can be active at any given time. 
12087
 
 *
12088
 
 * @method showCellEditor
12089
 
 * @param elCell {HTMLElement | String} Cell to edit.
12090
 
 */
12091
 
showCellEditor : function(elCell, oRecord, oColumn) {
12092
 
    // Get a particular CellEditor
12093
 
    elCell = this.getTdEl(elCell);
12094
 
    if(elCell) {
12095
 
        oColumn = this.getColumn(elCell);
12096
 
        if(oColumn && oColumn.editor) {
12097
 
            var oCellEditor = this._oCellEditor;
12098
 
            // Clean up active CellEditor
12099
 
            if(oCellEditor) {
12100
 
                if(this._oCellEditor.cancel) {
12101
 
                    this._oCellEditor.cancel();
12102
 
                }
12103
 
                else if(oCellEditor.isActive) {
12104
 
                    this.cancelCellEditor();
12105
 
                }
12106
 
            }
12107
 
            
12108
 
            if(oColumn.editor instanceof YAHOO.widget.BaseCellEditor) {
12109
 
                // Get CellEditor
12110
 
                oCellEditor = oColumn.editor;
12111
 
                var ok = oCellEditor.attach(this, elCell);
12112
 
                if(ok) {
12113
 
                    oCellEditor.move();
12114
 
                    ok = this.doBeforeShowCellEditor(oCellEditor);
12115
 
                    if(ok) {
12116
 
                        oCellEditor.show();
12117
 
                        this._oCellEditor = oCellEditor;
12118
 
                    }
12119
 
                }
12120
 
            }
12121
 
            // Backward compatibility
12122
 
            else {
12123
 
                    if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) {
12124
 
                        oRecord = this.getRecord(elCell);
12125
 
                    }
12126
 
                    if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) {
12127
 
                        oColumn = this.getColumn(elCell);
12128
 
                    }
12129
 
                    if(oRecord && oColumn) {
12130
 
                        if(!this._oCellEditor || this._oCellEditor.container) {
12131
 
                            this._initCellEditorEl();
12132
 
                        }
12133
 
                        
12134
 
                        // Update Editor values
12135
 
                        oCellEditor = this._oCellEditor;
12136
 
                        oCellEditor.cell = elCell;
12137
 
                        oCellEditor.record = oRecord;
12138
 
                        oCellEditor.column = oColumn;
12139
 
                        oCellEditor.validator = (oColumn.editorOptions &&
12140
 
                                lang.isFunction(oColumn.editorOptions.validator)) ?
12141
 
                                oColumn.editorOptions.validator : null;
12142
 
                        oCellEditor.value = oRecord.getData(oColumn.key);
12143
 
                        oCellEditor.defaultValue = null;
12144
 
            
12145
 
                        // Move Editor
12146
 
                        var elContainer = oCellEditor.container;
12147
 
                        var x = Dom.getX(elCell);
12148
 
                        var y = Dom.getY(elCell);
12149
 
            
12150
 
                        // SF doesn't get xy for cells in scrolling table
12151
 
                        // when tbody display is set to block
12152
 
                        if(isNaN(x) || isNaN(y)) {
12153
 
                            x = elCell.offsetLeft + // cell pos relative to table
12154
 
                                    Dom.getX(this._elTbody.parentNode) - // plus table pos relative to document
12155
 
                                    this._elTbody.scrollLeft; // minus tbody scroll
12156
 
                            y = elCell.offsetTop + // cell pos relative to table
12157
 
                                    Dom.getY(this._elTbody.parentNode) - // plus table pos relative to document
12158
 
                                    this._elTbody.scrollTop + // minus tbody scroll
12159
 
                                    this._elThead.offsetHeight; // account for fixed THEAD cells
12160
 
                        }
12161
 
            
12162
 
                        elContainer.style.left = x + "px";
12163
 
                        elContainer.style.top = y + "px";
12164
 
            
12165
 
                        // Hook to customize the UI
12166
 
                        this.doBeforeShowCellEditor(this._oCellEditor);
12167
 
            
12168
 
                        //TODO: This is temporarily up here due so elements can be focused
12169
 
                        // Show Editor
12170
 
                        elContainer.style.display = "";
12171
 
            
12172
 
                        // Handle ESC key
12173
 
                        Ev.addListener(elContainer, "keydown", function(e, oSelf) {
12174
 
                            // ESC hides Cell Editor
12175
 
                            if((e.keyCode == 27)) {
12176
 
                                oSelf.cancelCellEditor();
12177
 
                                oSelf.focusTbodyEl();
12178
 
                            }
12179
 
                            else {
12180
 
                                oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e});
12181
 
                            }
12182
 
                        }, this);
12183
 
            
12184
 
                        // Render Editor markup
12185
 
                        var fnEditor;
12186
 
                        if(lang.isString(oColumn.editor)) {
12187
 
                            switch(oColumn.editor) {
12188
 
                                case "checkbox":
12189
 
                                    fnEditor = DT.editCheckbox;
12190
 
                                    break;
12191
 
                                case "date":
12192
 
                                    fnEditor = DT.editDate;
12193
 
                                    break;
12194
 
                                case "dropdown":
12195
 
                                    fnEditor = DT.editDropdown;
12196
 
                                    break;
12197
 
                                case "radio":
12198
 
                                    fnEditor = DT.editRadio;
12199
 
                                    break;
12200
 
                                case "textarea":
12201
 
                                    fnEditor = DT.editTextarea;
12202
 
                                    break;
12203
 
                                case "textbox":
12204
 
                                    fnEditor = DT.editTextbox;
12205
 
                                    break;
12206
 
                                default:
12207
 
                                    fnEditor = null;
12208
 
                            }
12209
 
                        }
12210
 
                        else if(lang.isFunction(oColumn.editor)) {
12211
 
                            fnEditor = oColumn.editor;
12212
 
                        }
12213
 
            
12214
 
                        if(fnEditor) {
12215
 
                            // Create DOM input elements
12216
 
                            fnEditor(this._oCellEditor, this);
12217
 
            
12218
 
                            // Show Save/Cancel buttons
12219
 
                            if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) {
12220
 
                                this.showCellEditorBtns(elContainer);
12221
 
                            }
12222
 
            
12223
 
                            oCellEditor.isActive = true;
12224
 
            
12225
 
                            //TODO: verify which args to pass
12226
 
                            this.fireEvent("editorShowEvent", {editor:oCellEditor});
12227
 
                            YAHOO.log("Cell Editor shown for " + elCell, "info", this.toString());
12228
 
                            return;
12229
 
                        }
12230
 
                    }
12231
 
 
12232
 
 
12233
 
 
12234
 
            
12235
 
            }
12236
 
        }
12237
 
    }
12238
 
},
12239
 
 
12240
 
/**
12241
 
 * Backward compatibility.
12242
 
 *
12243
 
 * @method _initCellEditorEl
12244
 
 * @private
12245
 
 * @deprecated 
12246
 
 */
12247
 
_initCellEditorEl : function() {
12248
 
    // Attach Cell Editor container element as first child of body
12249
 
    var elCellEditor = document.createElement("div");
12250
 
    elCellEditor.id = this._sId + "-celleditor";
12251
 
    elCellEditor.style.display = "none";
12252
 
    elCellEditor.tabIndex = 0;
12253
 
    Dom.addClass(elCellEditor, DT.CLASS_EDITOR);
12254
 
    var elFirstChild = Dom.getFirstChild(document.body);
12255
 
    if(elFirstChild) {
12256
 
        elCellEditor = Dom.insertBefore(elCellEditor, elFirstChild);
12257
 
    }
12258
 
    else {
12259
 
        elCellEditor = document.body.appendChild(elCellEditor);
12260
 
    }
12261
 
    
12262
 
    // Internal tracker of Cell Editor values
12263
 
    var oCellEditor = {};
12264
 
    oCellEditor.container = elCellEditor;
12265
 
    oCellEditor.value = null;
12266
 
    oCellEditor.isActive = false;
12267
 
    this._oCellEditor = oCellEditor;
12268
 
},
12269
 
 
12270
 
/**
12271
 
 * Overridable abstract method to customize CellEditor before showing.
12272
 
 *
12273
 
 * @method doBeforeShowCellEditor
12274
 
 * @param oCellEditor {YAHOO.widget.CellEditor} The CellEditor instance.
12275
 
 * @return {Boolean} Return true to continue showing CellEditor.
12276
 
 */
12277
 
doBeforeShowCellEditor : function(oCellEditor) {
12278
 
    return true;
12279
 
},
12280
 
 
12281
 
/**
12282
 
 * Saves active CellEditor input to Record and upates DOM UI.
12283
 
 *
12284
 
 * @method saveCellEditor
12285
 
 */
12286
 
saveCellEditor : function() {
12287
 
    if(this._oCellEditor) {
12288
 
        if(this._oCellEditor.save) {
12289
 
            this._oCellEditor.save();
12290
 
        }
12291
 
        // Backward compatibility
12292
 
        else if(this._oCellEditor.isActive) {
12293
 
            var newData = this._oCellEditor.value;
12294
 
            // Copy the data to pass to the event
12295
 
            //var oldData = YAHOO.widget.DataTable._cloneObject(this._oCellEditor.record.getData(this._oCellEditor.column.key));
12296
 
            var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key);
12297
 
    
12298
 
            // Validate input data
12299
 
            if(this._oCellEditor.validator) {
12300
 
                newData = this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData, this._oCellEditor);
12301
 
                if(newData === null ) {
12302
 
                    this.resetCellEditor();
12303
 
                    this.fireEvent("editorRevertEvent",
12304
 
                            {editor:this._oCellEditor, oldData:oldData, newData:newData});
12305
 
                    YAHOO.log("Could not save Cell Editor input due to invalid data " +
12306
 
                            lang.dump(newData), "warn", this.toString());
12307
 
                    return;
12308
 
                }
12309
 
            }
12310
 
            // Update the Record
12311
 
            this._oRecordSet.updateRecordValue(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
12312
 
            // Update the UI
12313
 
            this.formatCell(this._oCellEditor.cell.firstChild);
12314
 
            
12315
 
            // Bug fix 1764044
12316
 
            this._oChainRender.add({
12317
 
                method: function() {
12318
 
                    this.validateColumnWidths();
12319
 
                },
12320
 
                scope: this
12321
 
            });
12322
 
            this._oChainRender.run();
12323
 
            // Clear out the Cell Editor
12324
 
            this.resetCellEditor();
12325
 
    
12326
 
            this.fireEvent("editorSaveEvent",
12327
 
                    {editor:this._oCellEditor, oldData:oldData, newData:newData});
12328
 
            YAHOO.log("Cell Editor input saved", "info", this.toString());
12329
 
        }
12330
 
    }   
12331
 
},
12332
 
 
12333
 
/**
12334
 
 * Cancels active CellEditor.
12335
 
 *
12336
 
 * @method cancelCellEditor
12337
 
 */
12338
 
cancelCellEditor : function() {
12339
 
    if(this._oCellEditor) {
12340
 
        if(this._oCellEditor.cancel) {
12341
 
            this._oCellEditor.cancel();
12342
 
        }
12343
 
        // Backward compatibility
12344
 
        else if(this._oCellEditor.isActive) {
12345
 
            this.resetCellEditor();
12346
 
            //TODO: preserve values for the event?
12347
 
            this.fireEvent("editorCancelEvent", {editor:this._oCellEditor});
12348
 
            YAHOO.log("Cell Editor input canceled", "info", this.toString());
12349
 
        }
12350
 
 
12351
 
        YAHOO.log("CellEditor input canceled", "info", this.toString());
12352
 
    }
12353
 
},
12354
 
 
12355
 
/**
12356
 
 * Destroys active CellEditor instance and UI.
12357
 
 *
12358
 
 * @method destroyCellEditor
12359
 
 */
12360
 
destroyCellEditor : function() {
12361
 
    if(this._oCellEditor) {
12362
 
        this._oCellEditor.destroy();
12363
 
        this._oCellEditor = null;
12364
 
    }   
12365
 
},
12366
 
 
12367
 
/**
12368
 
 * Passes through showEvent of the active CellEditor.
12369
 
 *
12370
 
 * @method _onEditorShowEvent
12371
 
 * @param oArgs {Object}  Custom Event args.
12372
 
 * @private 
12373
 
 */
12374
 
_onEditorShowEvent : function(oArgs) {
12375
 
    this.fireEvent("editorShowEvent", oArgs);
12376
 
},
12377
 
 
12378
 
/**
12379
 
 * Passes through keydownEvent of the active CellEditor.
12380
 
 * @param oArgs {Object}  Custom Event args. 
12381
 
 *
12382
 
 * @method _onEditorKeydownEvent
12383
 
 * @private 
12384
 
 */
12385
 
_onEditorKeydownEvent : function(oArgs) {
12386
 
    this.fireEvent("editorKeydownEvent", oArgs);
12387
 
},
12388
 
 
12389
 
/**
12390
 
 * Passes through revertEvent of the active CellEditor.
12391
 
 *
12392
 
 * @method _onEditorRevertEvent
12393
 
 * @param oArgs {Object}  Custom Event args. 
12394
 
 * @private  
12395
 
 */
12396
 
_onEditorRevertEvent : function(oArgs) {
12397
 
    this.fireEvent("editorRevertEvent", oArgs);
12398
 
},
12399
 
 
12400
 
/**
12401
 
 * Passes through saveEvent of the active CellEditor.
12402
 
 *
12403
 
 * @method _onEditorSaveEvent
12404
 
 * @param oArgs {Object}  Custom Event args.  
12405
 
 * @private 
12406
 
 */
12407
 
_onEditorSaveEvent : function(oArgs) {
12408
 
    this.fireEvent("editorSaveEvent", oArgs);
12409
 
},
12410
 
 
12411
 
/**
12412
 
 * Passes through cancelEvent of the active CellEditor.
12413
 
 *
12414
 
 * @method _onEditorCancelEvent
12415
 
 * @param oArgs {Object}  Custom Event args.
12416
 
 * @private   
12417
 
 */
12418
 
_onEditorCancelEvent : function(oArgs) {
12419
 
    this.fireEvent("editorCancelEvent", oArgs);
12420
 
},
12421
 
 
12422
 
/**
12423
 
 * Passes through blurEvent of the active CellEditor.
12424
 
 *
12425
 
 * @method _onEditorBlurEvent
12426
 
 * @param oArgs {Object}  Custom Event args. 
12427
 
 * @private  
12428
 
 */
12429
 
_onEditorBlurEvent : function(oArgs) {
12430
 
    this.fireEvent("editorBlurEvent", oArgs);
12431
 
},
12432
 
 
12433
 
/**
12434
 
 * Passes through blockEvent of the active CellEditor.
12435
 
 *
12436
 
 * @method _onEditorBlockEvent
12437
 
 * @param oArgs {Object}  Custom Event args. 
12438
 
 * @private  
12439
 
 */
12440
 
_onEditorBlockEvent : function(oArgs) {
12441
 
    this.fireEvent("editorBlockEvent", oArgs);
12442
 
},
12443
 
 
12444
 
/**
12445
 
 * Passes through unblockEvent of the active CellEditor.
12446
 
 *
12447
 
 * @method _onEditorUnblockEvent
12448
 
 * @param oArgs {Object}  Custom Event args. 
12449
 
 * @private  
12450
 
 */
12451
 
_onEditorUnblockEvent : function(oArgs) {
12452
 
    this.fireEvent("editorUnblockEvent", oArgs);
12453
 
},
12454
 
 
12455
 
/**
12456
 
 * Public handler of the editorBlurEvent. By default, saves on blur if
12457
 
 * disableBtns is true, otherwise cancels on blur. 
12458
 
 *
12459
 
 * @method onEditorBlurEvent
12460
 
 * @param oArgs {Object}  Custom Event args.  
12461
 
 */
12462
 
onEditorBlurEvent : function(oArgs) {
12463
 
    if(oArgs.editor.disableBtns) {
12464
 
        // Save on blur
12465
 
        if(oArgs.editor.save) { // Backward incompatible
12466
 
            oArgs.editor.save();
12467
 
        }
12468
 
    }      
12469
 
    else if(oArgs.editor.cancel) { // Backward incompatible
12470
 
        // Cancel on blur
12471
 
        oArgs.editor.cancel();
12472
 
    }      
12473
 
},
12474
 
 
12475
 
/**
12476
 
 * Public handler of the editorBlockEvent. By default, disables DataTable UI.
12477
 
 *
12478
 
 * @method onEditorBlockEvent
12479
 
 * @param oArgs {Object}  Custom Event args.  
12480
 
 */
12481
 
onEditorBlockEvent : function(oArgs) {
12482
 
    this.disable();
12483
 
},
12484
 
 
12485
 
/**
12486
 
 * Public handler of the editorUnblockEvent. By default, undisables DataTable UI.
12487
 
 *
12488
 
 * @method onEditorUnblockEvent
12489
 
 * @param oArgs {Object}  Custom Event args.  
12490
 
 */
12491
 
onEditorUnblockEvent : function(oArgs) {
12492
 
    this.undisable();
12493
 
},
12494
 
 
12495
 
 
12496
 
 
12497
 
 
12498
 
 
12499
 
 
12500
 
 
12501
 
 
12502
 
 
12503
 
 
12504
 
 
12505
 
 
12506
 
 
12507
 
 
12508
 
 
12509
 
 
12510
 
 
12511
 
 
12512
 
 
12513
 
 
12514
 
 
12515
 
 
12516
 
 
12517
 
 
12518
 
 
12519
 
 
12520
 
 
12521
 
 
12522
 
 
12523
 
 
12524
 
 
12525
 
 
12526
 
 
12527
 
 
12528
 
 
12529
 
 
12530
 
 
12531
 
 
12532
 
// ABSTRACT METHODS
12533
 
 
12534
 
/**
12535
 
 * Overridable method gives implementers a hook to access data before
12536
 
 * it gets added to RecordSet and rendered to the TBODY.
12537
 
 *
12538
 
 * @method doBeforeLoadData
12539
 
 * @param sRequest {String} Original request.
12540
 
 * @param oResponse {Object} Response object.
12541
 
 * @param oPayload {MIXED} additional arguments
12542
 
 * @return {Boolean} Return true to continue loading data into RecordSet and
12543
 
 * updating DataTable with new Records, false to cancel.
12544
 
 */
12545
 
doBeforeLoadData : function(sRequest, oResponse, oPayload) {
12546
 
    return true;
12547
 
},
12548
 
 
12549
 
 
12550
 
 
12551
 
 
12552
 
 
12553
 
 
12554
 
 
12555
 
 
12556
 
 
12557
 
 
12558
 
 
12559
 
 
12560
 
 
12561
 
 
12562
 
 
12563
 
 
12564
 
 
12565
 
 
12566
 
 
12567
 
 
12568
 
 
12569
 
 
12570
 
 
12571
 
 
12572
 
 
12573
 
 
12574
 
 
12575
 
 
12576
 
 
12577
 
 
12578
 
 
12579
 
 
12580
 
 
12581
 
 
12582
 
 
12583
 
 
12584
 
 
12585
 
 
12586
 
 
12587
 
 
12588
 
 
12589
 
 
12590
 
 
12591
 
 
12592
 
 
12593
 
 
12594
 
 
12595
 
 
12596
 
 
12597
 
 
12598
 
 
12599
 
 
12600
 
 
12601
 
 
12602
 
 
12603
 
 
12604
 
 
12605
 
 
12606
 
 
12607
 
 
12608
 
 
12609
 
 
12610
 
 
12611
 
/////////////////////////////////////////////////////////////////////////////
12612
 
//
12613
 
// Public Custom Event Handlers
12614
 
//
12615
 
/////////////////////////////////////////////////////////////////////////////
12616
 
 
12617
 
/**
12618
 
 * Overridable custom event handler to sort Column.
12619
 
 *
12620
 
 * @method onEventSortColumn
12621
 
 * @param oArgs.event {HTMLEvent} Event object.
12622
 
 * @param oArgs.target {HTMLElement} Target element.
12623
 
 */
12624
 
onEventSortColumn : function(oArgs) {
12625
 
//TODO: support form elements in sortable columns
12626
 
    var evt = oArgs.event;
12627
 
    var target = oArgs.target;
12628
 
 
12629
 
    var el = this.getThEl(target) || this.getTdEl(target);
12630
 
    if(el) {
12631
 
        var oColumn = this.getColumn(el);
12632
 
        if(oColumn.sortable) {
12633
 
            Ev.stopEvent(evt);
12634
 
            this.sortColumn(oColumn);
12635
 
        }
12636
 
    }
12637
 
    else {
12638
 
        YAHOO.log("Could not find Column for " + target, "warn", this.toString());
12639
 
    }
12640
 
},
12641
 
 
12642
 
/**
12643
 
 * Overridable custom event handler to select Column.
12644
 
 *
12645
 
 * @method onEventSelectColumn
12646
 
 * @param oArgs.event {HTMLEvent} Event object.
12647
 
 * @param oArgs.target {HTMLElement} Target element.
12648
 
 */
12649
 
onEventSelectColumn : function(oArgs) {
12650
 
    this.selectColumn(oArgs.target);
12651
 
},
12652
 
 
12653
 
/**
12654
 
 * Overridable custom event handler to highlight Column. Accounts for spurious
12655
 
 * caused-by-child events. 
12656
 
 *
12657
 
 * @method onEventHighlightColumn
12658
 
 * @param oArgs.event {HTMLEvent} Event object.
12659
 
 * @param oArgs.target {HTMLElement} Target element.
12660
 
 */
12661
 
onEventHighlightColumn : function(oArgs) {
12662
 
    //TODO: filter for all spurious events at a lower level
12663
 
    if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12664
 
        this.highlightColumn(oArgs.target);
12665
 
    }
12666
 
},
12667
 
 
12668
 
/**
12669
 
 * Overridable custom event handler to unhighlight Column. Accounts for spurious
12670
 
 * caused-by-child events. 
12671
 
 *
12672
 
 * @method onEventUnhighlightColumn
12673
 
 * @param oArgs.event {HTMLEvent} Event object.
12674
 
 * @param oArgs.target {HTMLElement} Target element.
12675
 
 */
12676
 
onEventUnhighlightColumn : function(oArgs) {
12677
 
    //TODO: filter for all spurious events at a lower level
12678
 
    if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12679
 
        this.unhighlightColumn(oArgs.target);
12680
 
    }
12681
 
},
12682
 
 
12683
 
/**
12684
 
 * Overridable custom event handler to manage selection according to desktop paradigm.
12685
 
 *
12686
 
 * @method onEventSelectRow
12687
 
 * @param oArgs.event {HTMLEvent} Event object.
12688
 
 * @param oArgs.target {HTMLElement} Target element.
12689
 
 */
12690
 
onEventSelectRow : function(oArgs) {
12691
 
    var sMode = this.get("selectionMode");
12692
 
    if(sMode == "single") {
12693
 
        this._handleSingleSelectionByMouse(oArgs);
12694
 
    }
12695
 
    else {
12696
 
        this._handleStandardSelectionByMouse(oArgs);
12697
 
    }
12698
 
},
12699
 
 
12700
 
/**
12701
 
 * Overridable custom event handler to select cell.
12702
 
 *
12703
 
 * @method onEventSelectCell
12704
 
 * @param oArgs.event {HTMLEvent} Event object.
12705
 
 * @param oArgs.target {HTMLElement} Target element.
12706
 
 */
12707
 
onEventSelectCell : function(oArgs) {
12708
 
    var sMode = this.get("selectionMode");
12709
 
    if(sMode == "cellblock") {
12710
 
        this._handleCellBlockSelectionByMouse(oArgs);
12711
 
    }
12712
 
    else if(sMode == "cellrange") {
12713
 
        this._handleCellRangeSelectionByMouse(oArgs);
12714
 
    }
12715
 
    else {
12716
 
        this._handleSingleCellSelectionByMouse(oArgs);
12717
 
    }
12718
 
},
12719
 
 
12720
 
/**
12721
 
 * Overridable custom event handler to highlight row. Accounts for spurious
12722
 
 * caused-by-child events. 
12723
 
 *
12724
 
 * @method onEventHighlightRow
12725
 
 * @param oArgs.event {HTMLEvent} Event object.
12726
 
 * @param oArgs.target {HTMLElement} Target element.
12727
 
 */
12728
 
onEventHighlightRow : function(oArgs) {
12729
 
    //TODO: filter for all spurious events at a lower level
12730
 
    if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12731
 
        this.highlightRow(oArgs.target);
12732
 
    }
12733
 
},
12734
 
 
12735
 
/**
12736
 
 * Overridable custom event handler to unhighlight row. Accounts for spurious
12737
 
 * caused-by-child events. 
12738
 
 *
12739
 
 * @method onEventUnhighlightRow
12740
 
 * @param oArgs.event {HTMLEvent} Event object.
12741
 
 * @param oArgs.target {HTMLElement} Target element.
12742
 
 */
12743
 
onEventUnhighlightRow : function(oArgs) {
12744
 
    //TODO: filter for all spurious events at a lower level
12745
 
    if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12746
 
        this.unhighlightRow(oArgs.target);
12747
 
    }
12748
 
},
12749
 
 
12750
 
/**
12751
 
 * Overridable custom event handler to highlight cell. Accounts for spurious
12752
 
 * caused-by-child events. 
12753
 
 *
12754
 
 * @method onEventHighlightCell
12755
 
 * @param oArgs.event {HTMLEvent} Event object.
12756
 
 * @param oArgs.target {HTMLElement} Target element.
12757
 
 */
12758
 
onEventHighlightCell : function(oArgs) {
12759
 
    //TODO: filter for all spurious events at a lower level
12760
 
    if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12761
 
        this.highlightCell(oArgs.target);
12762
 
    }
12763
 
},
12764
 
 
12765
 
/**
12766
 
 * Overridable custom event handler to unhighlight cell. Accounts for spurious
12767
 
 * caused-by-child events. 
12768
 
 *
12769
 
 * @method onEventUnhighlightCell
12770
 
 * @param oArgs.event {HTMLEvent} Event object.
12771
 
 * @param oArgs.target {HTMLElement} Target element.
12772
 
 */
12773
 
onEventUnhighlightCell : function(oArgs) {
12774
 
    //TODO: filter for all spurious events at a lower level
12775
 
    if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12776
 
        this.unhighlightCell(oArgs.target);
12777
 
    }
12778
 
},
12779
 
 
12780
 
/**
12781
 
 * Overridable custom event handler to format cell.
12782
 
 *
12783
 
 * @method onEventFormatCell
12784
 
 * @param oArgs.event {HTMLEvent} Event object.
12785
 
 * @param oArgs.target {HTMLElement} Target element.
12786
 
 */
12787
 
onEventFormatCell : function(oArgs) {
12788
 
    var target = oArgs.target;
12789
 
 
12790
 
    var elCell = this.getTdEl(target);
12791
 
    if(elCell) {
12792
 
        var oColumn = this.getColumn(elCell.cellIndex);
12793
 
        this.formatCell(elCell.firstChild, this.getRecord(elCell), oColumn);
12794
 
    }
12795
 
    else {
12796
 
        YAHOO.log("Could not format cell " + target, "warn", this.toString());
12797
 
    }
12798
 
},
12799
 
 
12800
 
/**
12801
 
 * Overridable custom event handler to edit cell.
12802
 
 *
12803
 
 * @method onEventShowCellEditor
12804
 
 * @param oArgs.event {HTMLEvent} Event object.
12805
 
 * @param oArgs.target {HTMLElement} Target element.
12806
 
 */
12807
 
onEventShowCellEditor : function(oArgs) {
12808
 
    this.showCellEditor(oArgs.target);
12809
 
},
12810
 
 
12811
 
/**
12812
 
 * Overridable custom event handler to save active CellEditor input.
12813
 
 *
12814
 
 * @method onEventSaveCellEditor
12815
 
 */
12816
 
onEventSaveCellEditor : function(oArgs) {
12817
 
    if(this._oCellEditor) {
12818
 
        if(this._oCellEditor.save) {
12819
 
            this._oCellEditor.save();
12820
 
        }
12821
 
        // Backward compatibility
12822
 
        else {
12823
 
            this.saveCellEditor();
12824
 
        }
12825
 
    }
12826
 
},
12827
 
 
12828
 
/**
12829
 
 * Overridable custom event handler to cancel active CellEditor.
12830
 
 *
12831
 
 * @method onEventCancelCellEditor
12832
 
 */
12833
 
onEventCancelCellEditor : function(oArgs) {
12834
 
    if(this._oCellEditor) {
12835
 
        if(this._oCellEditor.cancel) {
12836
 
            this._oCellEditor.cancel();
12837
 
        }
12838
 
        // Backward compatibility
12839
 
        else {
12840
 
            this.cancelCellEditor();
12841
 
        }
12842
 
    }
12843
 
},
12844
 
 
12845
 
/**
12846
 
 * Callback function receives data from DataSource and populates an entire
12847
 
 * DataTable with Records and TR elements, clearing previous Records, if any.
12848
 
 *
12849
 
 * @method onDataReturnInitializeTable
12850
 
 * @param sRequest {String} Original request.
12851
 
 * @param oResponse {Object} Response object.
12852
 
 * @param oPayload {MIXED} (optional) Additional argument(s)
12853
 
 */
12854
 
onDataReturnInitializeTable : function(sRequest, oResponse, oPayload) {
12855
 
    if((this instanceof DT) && this._sId) {
12856
 
        this.initializeTable();
12857
 
    
12858
 
        this.onDataReturnSetRows(sRequest,oResponse,oPayload);
12859
 
    }
12860
 
},
12861
 
 
12862
 
/**
12863
 
 * Callback function receives reponse from DataSource, replaces all existing
12864
 
 * Records in  RecordSet, updates TR elements with new data, and updates state
12865
 
 * UI for pagination and sorting from payload data, if necessary. 
12866
 
 *  
12867
 
 * @method onDataReturnReplaceRows
12868
 
 * @param oRequest {MIXED} Original generated request.
12869
 
 * @param oResponse {Object} Response object.
12870
 
 * @param oPayload {MIXED} (optional) Additional argument(s)
12871
 
 */
12872
 
onDataReturnReplaceRows : function(oRequest, oResponse, oPayload) {
12873
 
    if((this instanceof DT) && this._sId) {
12874
 
        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
12875
 
    
12876
 
        // Pass data through abstract method for any transformations
12877
 
        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
12878
 
            pag   = this.get('paginator'),
12879
 
            index = 0;
12880
 
    
12881
 
        // Data ok to set
12882
 
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
12883
 
            // Update Records
12884
 
            this._oRecordSet.reset();
12885
 
    
12886
 
            if (this.get('dynamicData')) {
12887
 
                if (oPayload && oPayload.pagination &&
12888
 
                    lang.isNumber(oPayload.pagination.recordOffset)) {
12889
 
                    index = oPayload.pagination.recordOffset;
12890
 
                } else if (pag) {
12891
 
                    index = pag.getStartIndex();
12892
 
                }
12893
 
            }
12894
 
    
12895
 
            this._oRecordSet.setRecords(oResponse.results, index | 0);
12896
 
            
12897
 
            // Update state
12898
 
            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
12899
 
            
12900
 
            // Update UI
12901
 
            this.render();    
12902
 
        }
12903
 
        // Error
12904
 
        else if(ok && oResponse.error) {
12905
 
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
12906
 
        }
12907
 
    }
12908
 
},
12909
 
 
12910
 
/**
12911
 
 * Callback function receives data from DataSource and appends to an existing
12912
 
 * DataTable new Records and, if applicable, creates or updates
12913
 
 * corresponding TR elements.
12914
 
 *
12915
 
 * @method onDataReturnAppendRows
12916
 
 * @param sRequest {String} Original request.
12917
 
 * @param oResponse {Object} Response object.
12918
 
 * @param oPayload {MIXED} (optional) Additional argument(s)
12919
 
 */
12920
 
onDataReturnAppendRows : function(sRequest, oResponse, oPayload) {
12921
 
    if((this instanceof DT) && this._sId) {
12922
 
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
12923
 
    
12924
 
        // Pass data through abstract method for any transformations
12925
 
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
12926
 
    
12927
 
        // Data ok to append
12928
 
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {        
12929
 
            // Append rows
12930
 
            this.addRows(oResponse.results);
12931
 
    
12932
 
            // Update state
12933
 
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
12934
 
        }
12935
 
        // Error
12936
 
        else if(ok && oResponse.error) {
12937
 
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
12938
 
        }
12939
 
    }
12940
 
},
12941
 
 
12942
 
/**
12943
 
 * Callback function receives data from DataSource and inserts new records
12944
 
 * starting at the index specified in oPayload.insertIndex. The value for
12945
 
 * oPayload.insertIndex can be populated when sending the request to the DataSource,
12946
 
 * or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime.
12947
 
 * If applicable, creates or updates corresponding TR elements.
12948
 
 *
12949
 
 * @method onDataReturnInsertRows
12950
 
 * @param sRequest {String} Original request.
12951
 
 * @param oResponse {Object} Response object.
12952
 
 * @param oPayload {MIXED} Argument payload, looks in oPayload.insertIndex.
12953
 
 */
12954
 
onDataReturnInsertRows : function(sRequest, oResponse, oPayload) {
12955
 
    if((this instanceof DT) && this._sId) {
12956
 
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
12957
 
    
12958
 
        // Pass data through abstract method for any transformations
12959
 
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
12960
 
    
12961
 
        // Data ok to append
12962
 
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
12963
 
            // Insert rows
12964
 
            this.addRows(oResponse.results, (oPayload ? oPayload.insertIndex : 0));
12965
 
    
12966
 
            // Update state
12967
 
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
12968
 
        }
12969
 
        // Error
12970
 
        else if(ok && oResponse.error) {
12971
 
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
12972
 
        }
12973
 
    }
12974
 
},
12975
 
 
12976
 
/**
12977
 
 * Callback function receives data from DataSource and incrementally updates Records
12978
 
 * starting at the index specified in oPayload.updateIndex. The value for
12979
 
 * oPayload.updateIndex can be populated when sending the request to the DataSource,
12980
 
 * or by accessing oPayload.updateIndex with the doBeforeLoadData() method at runtime.
12981
 
 * If applicable, creates or updates corresponding TR elements.
12982
 
 *
12983
 
 * @method onDataReturnUpdateRows
12984
 
 * @param sRequest {String} Original request.
12985
 
 * @param oResponse {Object} Response object.
12986
 
 * @param oPayload {MIXED} Argument payload, looks in oPayload.updateIndex.
12987
 
 */
12988
 
onDataReturnUpdateRows : function(sRequest, oResponse, oPayload) {
12989
 
    if((this instanceof DT) && this._sId) {
12990
 
        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
12991
 
    
12992
 
        // Pass data through abstract method for any transformations
12993
 
        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
12994
 
    
12995
 
        // Data ok to append
12996
 
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
12997
 
            // Insert rows
12998
 
            this.updateRows((oPayload ? oPayload.updateIndex : 0), oResponse.results);
12999
 
    
13000
 
            // Update state
13001
 
            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
13002
 
        }
13003
 
        // Error
13004
 
        else if(ok && oResponse.error) {
13005
 
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
13006
 
        }
13007
 
    }
13008
 
},
13009
 
 
13010
 
/**
13011
 
 * Callback function receives reponse from DataSource and populates the
13012
 
 * RecordSet with the results.
13013
 
 *  
13014
 
 * @method onDataReturnSetRows
13015
 
 * @param oRequest {MIXED} Original generated request.
13016
 
 * @param oResponse {Object} Response object.
13017
 
 * @param oPayload {MIXED} (optional) Additional argument(s)
13018
 
 */
13019
 
onDataReturnSetRows : function(oRequest, oResponse, oPayload) {
13020
 
    if((this instanceof DT) && this._sId) {
13021
 
        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
13022
 
    
13023
 
        // Pass data through abstract method for any transformations
13024
 
        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
13025
 
            pag   = this.get('paginator'),
13026
 
            index = 0;
13027
 
    
13028
 
        // Data ok to set
13029
 
        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
13030
 
            // Update Records
13031
 
            if (this.get('dynamicData')) {
13032
 
                if (oPayload && oPayload.pagination &&
13033
 
                    lang.isNumber(oPayload.pagination.recordOffset)) {
13034
 
                    index = oPayload.pagination.recordOffset;
13035
 
                } else if (pag) {
13036
 
                    index = pag.getStartIndex();
13037
 
                }
13038
 
                
13039
 
                this._oRecordSet.reset(); // Bug 2290604: dyanmic data shouldn't keep accumulating by default
13040
 
            }
13041
 
    
13042
 
            this._oRecordSet.setRecords(oResponse.results, index | 0);
13043
 
    
13044
 
            // Update state
13045
 
            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
13046
 
            
13047
 
            // Update UI
13048
 
            this.render();
13049
 
        }
13050
 
        // Error
13051
 
        else if(ok && oResponse.error) {
13052
 
            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
13053
 
        }
13054
 
    }
13055
 
    else {
13056
 
        YAHOO.log("Instance destroyed before data returned.","info",this.toString());
13057
 
    }
13058
 
},
13059
 
 
13060
 
/**
13061
 
 * Hook to update oPayload before consumption.
13062
 
 *  
13063
 
 * @method handleDataReturnPayload
13064
 
 * @param oRequest {MIXED} Original generated request.
13065
 
 * @param oResponse {Object} Response object.
13066
 
 * @param oPayload {MIXED} State values.
13067
 
 * @return oPayload {MIXED} State values.
13068
 
 */
13069
 
handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
13070
 
    return oPayload;
13071
 
},
13072
 
 
13073
 
/**
13074
 
 * Updates the DataTable with state data sent in an onDataReturn* payload.
13075
 
 *  
13076
 
 * @method handleDataReturnPayload
13077
 
 * @param oRequest {MIXED} Original generated request.
13078
 
 * @param oResponse {Object} Response object.
13079
 
 * @param oPayload {MIXED} State values
13080
 
 */
13081
 
_handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
13082
 
    oPayload = this.handleDataReturnPayload(oRequest, oResponse, oPayload);
13083
 
    if(oPayload) {
13084
 
        // Update pagination
13085
 
        var oPaginator = this.get('paginator');
13086
 
        if (oPaginator) {
13087
 
            // Update totalRecords
13088
 
            if(this.get("dynamicData")) {
13089
 
                if (widget.Paginator.isNumeric(oPayload.totalRecords)) {
13090
 
                    oPaginator.set('totalRecords',oPayload.totalRecords);
13091
 
                }
13092
 
            }
13093
 
            else {
13094
 
                oPaginator.set('totalRecords',this._oRecordSet.getLength());
13095
 
            }
13096
 
            // Update other paginator values
13097
 
            if (lang.isObject(oPayload.pagination)) {
13098
 
                oPaginator.set('rowsPerPage',oPayload.pagination.rowsPerPage);
13099
 
                oPaginator.set('recordOffset',oPayload.pagination.recordOffset);
13100
 
            }
13101
 
        }
13102
 
 
13103
 
        // Update sorting
13104
 
        if (oPayload.sortedBy) {
13105
 
            // Set the sorting values in preparation for refresh
13106
 
            this.set('sortedBy', oPayload.sortedBy);
13107
 
        }
13108
 
        // Backwards compatibility for sorting
13109
 
        else if (oPayload.sorting) {
13110
 
            // Set the sorting values in preparation for refresh
13111
 
            this.set('sortedBy', oPayload.sorting);
13112
 
        }
13113
 
    }
13114
 
},
13115
 
 
13116
 
 
13117
 
 
13118
 
 
13119
 
 
13120
 
 
13121
 
 
13122
 
 
13123
 
 
13124
 
 
13125
 
 
13126
 
 
13127
 
 
13128
 
 
13129
 
 
13130
 
 
13131
 
 
13132
 
 
13133
 
 
13134
 
 
13135
 
 
13136
 
 
13137
 
 
13138
 
 
13139
 
 
13140
 
 
13141
 
 
13142
 
 
13143
 
 
13144
 
 
13145
 
 
13146
 
 
13147
 
 
13148
 
    /////////////////////////////////////////////////////////////////////////////
13149
 
    //
13150
 
    // Custom Events
13151
 
    //
13152
 
    /////////////////////////////////////////////////////////////////////////////
13153
 
 
13154
 
    /**
13155
 
     * Fired when the DataTable's rows are rendered from an initialized state.
13156
 
     *
13157
 
     * @event initEvent
13158
 
     */
13159
 
 
13160
 
    /**
13161
 
     * Fired when the DataTable's DOM is rendered or modified.
13162
 
     *
13163
 
     * @event renderEvent
13164
 
     */
13165
 
 
13166
 
    /**
13167
 
     * Fired when the DataTable's post-render routine is complete, including
13168
 
     * Column width validations.
13169
 
     *
13170
 
     * @event postRenderEvent
13171
 
     */
13172
 
 
13173
 
    /**
13174
 
     * Fired when the DataTable is disabled.
13175
 
     *
13176
 
     * @event disableEvent
13177
 
     */
13178
 
 
13179
 
    /**
13180
 
     * Fired when the DataTable is undisabled.
13181
 
     *
13182
 
     * @event undisableEvent
13183
 
     */
13184
 
 
13185
 
    /**
13186
 
     * Fired when data is returned from DataSource but before it is consumed by
13187
 
     * DataTable.
13188
 
     *
13189
 
     * @event dataReturnEvent
13190
 
     * @param oArgs.request {String} Original request.
13191
 
     * @param oArgs.response {Object} Response object.
13192
 
     */
13193
 
 
13194
 
    /**
13195
 
     * Fired when the DataTable has a focus event.
13196
 
     *
13197
 
     * @event tableFocusEvent
13198
 
     */
13199
 
 
13200
 
    /**
13201
 
     * Fired when the DataTable THEAD element has a focus event.
13202
 
     *
13203
 
     * @event theadFocusEvent
13204
 
     */
13205
 
 
13206
 
    /**
13207
 
     * Fired when the DataTable TBODY element has a focus event.
13208
 
     *
13209
 
     * @event tbodyFocusEvent
13210
 
     */
13211
 
 
13212
 
    /**
13213
 
     * Fired when the DataTable has a blur event.
13214
 
     *
13215
 
     * @event tableBlurEvent
13216
 
     */
13217
 
 
13218
 
    /*TODO implement theadBlurEvent
13219
 
     * Fired when the DataTable THEAD element has a blur event.
13220
 
     *
13221
 
     * @event theadBlurEvent
13222
 
     */
13223
 
 
13224
 
    /*TODO: implement tbodyBlurEvent
13225
 
     * Fired when the DataTable TBODY element has a blur event.
13226
 
     *
13227
 
     * @event tbodyBlurEvent
13228
 
     */
13229
 
 
13230
 
    /**
13231
 
     * Fired when the DataTable has a key event.
13232
 
     *
13233
 
     * @event tableKeyEvent
13234
 
     * @param oArgs.event {HTMLEvent} The event object.
13235
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13236
 
     */
13237
 
 
13238
 
    /**
13239
 
     * Fired when the DataTable THEAD element has a key event.
13240
 
     *
13241
 
     * @event theadKeyEvent
13242
 
     * @param oArgs.event {HTMLEvent} The event object.
13243
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13244
 
     */
13245
 
 
13246
 
    /**
13247
 
     * Fired when the DataTable TBODY element has a key event.
13248
 
     *
13249
 
     * @event tbodyKeyEvent
13250
 
     * @param oArgs.event {HTMLEvent} The event object.
13251
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13252
 
     */
13253
 
 
13254
 
    /**
13255
 
     * Fired when the DataTable has a mouseover.
13256
 
     *
13257
 
     * @event tableMouseoverEvent
13258
 
     * @param oArgs.event {HTMLEvent} The event object.
13259
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13260
 
     *
13261
 
     */
13262
 
 
13263
 
    /**
13264
 
     * Fired when the DataTable has a mouseout.
13265
 
     *
13266
 
     * @event tableMouseoutEvent
13267
 
     * @param oArgs.event {HTMLEvent} The event object.
13268
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13269
 
     *
13270
 
     */
13271
 
 
13272
 
    /**
13273
 
     * Fired when the DataTable has a mousedown.
13274
 
     *
13275
 
     * @event tableMousedownEvent
13276
 
     * @param oArgs.event {HTMLEvent} The event object.
13277
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13278
 
     *
13279
 
     */
13280
 
 
13281
 
    /**
13282
 
     * Fired when the DataTable has a mouseup.
13283
 
     *
13284
 
     * @event tableMouseupEvent
13285
 
     * @param oArgs.event {HTMLEvent} The event object.
13286
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13287
 
     *
13288
 
     */
13289
 
 
13290
 
    /**
13291
 
     * Fired when the DataTable has a click.
13292
 
     *
13293
 
     * @event tableClickEvent
13294
 
     * @param oArgs.event {HTMLEvent} The event object.
13295
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13296
 
     *
13297
 
     */
13298
 
 
13299
 
    /**
13300
 
     * Fired when the DataTable has a dblclick.
13301
 
     *
13302
 
     * @event tableDblclickEvent
13303
 
     * @param oArgs.event {HTMLEvent} The event object.
13304
 
     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13305
 
     *
13306
 
     */
13307
 
 
13308
 
    /**
13309
 
     * Fired when a message is shown in the DataTable's message element.
13310
 
     *
13311
 
     * @event tableMsgShowEvent
13312
 
     * @param oArgs.html {String} The HTML displayed.
13313
 
     * @param oArgs.className {String} The className assigned.
13314
 
     *
13315
 
     */
13316
 
 
13317
 
    /**
13318
 
     * Fired when the DataTable's message element is hidden.
13319
 
     *
13320
 
     * @event tableMsgHideEvent
13321
 
     */
13322
 
 
13323
 
    /**
13324
 
     * Fired when a THEAD row has a mouseover.
13325
 
     *
13326
 
     * @event theadRowMouseoverEvent
13327
 
     * @param oArgs.event {HTMLEvent} The event object.
13328
 
     * @param oArgs.target {HTMLElement} The TR element.
13329
 
     */
13330
 
 
13331
 
    /**
13332
 
     * Fired when a THEAD row has a mouseout.
13333
 
     *
13334
 
     * @event theadRowMouseoutEvent
13335
 
     * @param oArgs.event {HTMLEvent} The event object.
13336
 
     * @param oArgs.target {HTMLElement} The TR element.
13337
 
     */
13338
 
 
13339
 
    /**
13340
 
     * Fired when a THEAD row has a mousedown.
13341
 
     *
13342
 
     * @event theadRowMousedownEvent
13343
 
     * @param oArgs.event {HTMLEvent} The event object.
13344
 
     * @param oArgs.target {HTMLElement} The TR element.
13345
 
     */
13346
 
 
13347
 
    /**
13348
 
     * Fired when a THEAD row has a mouseup.
13349
 
     *
13350
 
     * @event theadRowMouseupEvent
13351
 
     * @param oArgs.event {HTMLEvent} The event object.
13352
 
     * @param oArgs.target {HTMLElement} The TR element.
13353
 
     */
13354
 
 
13355
 
    /**
13356
 
     * Fired when a THEAD row has a click.
13357
 
     *
13358
 
     * @event theadRowClickEvent
13359
 
     * @param oArgs.event {HTMLEvent} The event object.
13360
 
     * @param oArgs.target {HTMLElement} The TR element.
13361
 
     */
13362
 
 
13363
 
    /**
13364
 
     * Fired when a THEAD row has a dblclick.
13365
 
     *
13366
 
     * @event theadRowDblclickEvent
13367
 
     * @param oArgs.event {HTMLEvent} The event object.
13368
 
     * @param oArgs.target {HTMLElement} The TR element.
13369
 
     */
13370
 
 
13371
 
    /**
13372
 
     * Fired when a THEAD cell has a mouseover.
13373
 
     *
13374
 
     * @event theadCellMouseoverEvent
13375
 
     * @param oArgs.event {HTMLEvent} The event object.
13376
 
     * @param oArgs.target {HTMLElement} The TH element.
13377
 
     *
13378
 
     */
13379
 
 
13380
 
    /**
13381
 
     * Fired when a THEAD cell has a mouseout.
13382
 
     *
13383
 
     * @event theadCellMouseoutEvent
13384
 
     * @param oArgs.event {HTMLEvent} The event object.
13385
 
     * @param oArgs.target {HTMLElement} The TH element.
13386
 
     *
13387
 
     */
13388
 
 
13389
 
    /**
13390
 
     * Fired when a THEAD cell has a mousedown.
13391
 
     *
13392
 
     * @event theadCellMousedownEvent
13393
 
     * @param oArgs.event {HTMLEvent} The event object.
13394
 
     * @param oArgs.target {HTMLElement} The TH element.
13395
 
     */
13396
 
 
13397
 
    /**
13398
 
     * Fired when a THEAD cell has a mouseup.
13399
 
     *
13400
 
     * @event theadCellMouseupEvent
13401
 
     * @param oArgs.event {HTMLEvent} The event object.
13402
 
     * @param oArgs.target {HTMLElement} The TH element.
13403
 
     */
13404
 
 
13405
 
    /**
13406
 
     * Fired when a THEAD cell has a click.
13407
 
     *
13408
 
     * @event theadCellClickEvent
13409
 
     * @param oArgs.event {HTMLEvent} The event object.
13410
 
     * @param oArgs.target {HTMLElement} The TH element.
13411
 
     */
13412
 
 
13413
 
    /**
13414
 
     * Fired when a THEAD cell has a dblclick.
13415
 
     *
13416
 
     * @event theadCellDblclickEvent
13417
 
     * @param oArgs.event {HTMLEvent} The event object.
13418
 
     * @param oArgs.target {HTMLElement} The TH element.
13419
 
     */
13420
 
 
13421
 
    /**
13422
 
     * Fired when a THEAD label has a mouseover.
13423
 
     *
13424
 
     * @event theadLabelMouseoverEvent
13425
 
     * @param oArgs.event {HTMLEvent} The event object.
13426
 
     * @param oArgs.target {HTMLElement} The SPAN element.
13427
 
     *
13428
 
     */
13429
 
 
13430
 
    /**
13431
 
     * Fired when a THEAD label has a mouseout.
13432
 
     *
13433
 
     * @event theadLabelMouseoutEvent
13434
 
     * @param oArgs.event {HTMLEvent} The event object.
13435
 
     * @param oArgs.target {HTMLElement} The SPAN element.
13436
 
     *
13437
 
     */
13438
 
 
13439
 
    /**
13440
 
     * Fired when a THEAD label has a mousedown.
13441
 
     *
13442
 
     * @event theadLabelMousedownEvent
13443
 
     * @param oArgs.event {HTMLEvent} The event object.
13444
 
     * @param oArgs.target {HTMLElement} The SPAN element.
13445
 
     */
13446
 
 
13447
 
    /**
13448
 
     * Fired when a THEAD label has a mouseup.
13449
 
     *
13450
 
     * @event theadLabelMouseupEvent
13451
 
     * @param oArgs.event {HTMLEvent} The event object.
13452
 
     * @param oArgs.target {HTMLElement} The SPAN element.
13453
 
     */
13454
 
 
13455
 
    /**
13456
 
     * Fired when a THEAD label has a click.
13457
 
     *
13458
 
     * @event theadLabelClickEvent
13459
 
     * @param oArgs.event {HTMLEvent} The event object.
13460
 
     * @param oArgs.target {HTMLElement} The SPAN element.
13461
 
     */
13462
 
 
13463
 
    /**
13464
 
     * Fired when a THEAD label has a dblclick.
13465
 
     *
13466
 
     * @event theadLabelDblclickEvent
13467
 
     * @param oArgs.event {HTMLEvent} The event object.
13468
 
     * @param oArgs.target {HTMLElement} The SPAN element.
13469
 
     */
13470
 
 
13471
 
    /**
13472
 
     * Fired when a column is sorted.
13473
 
     *
13474
 
     * @event columnSortEvent
13475
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13476
 
     * @param oArgs.dir {String} Sort direction: YAHOO.widget.DataTable.CLASS_ASC
13477
 
     * or YAHOO.widget.DataTable.CLASS_DESC.
13478
 
     */
13479
 
 
13480
 
    /**
13481
 
     * Fired when a column width is set.
13482
 
     *
13483
 
     * @event columnSetWidthEvent
13484
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13485
 
     * @param oArgs.width {Number} The width in pixels.
13486
 
     */
13487
 
 
13488
 
    /**
13489
 
     * Fired when a column width is unset.
13490
 
     *
13491
 
     * @event columnUnsetWidthEvent
13492
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13493
 
     */
13494
 
 
13495
 
    /**
13496
 
     * Fired when a column is drag-resized.
13497
 
     *
13498
 
     * @event columnResizeEvent
13499
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13500
 
     * @param oArgs.target {HTMLElement} The TH element.
13501
 
     * @param oArgs.width {Number} Width in pixels.     
13502
 
     */
13503
 
 
13504
 
    /**
13505
 
     * Fired when a Column is moved to a new index.
13506
 
     *
13507
 
     * @event columnReorderEvent
13508
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13509
 
     * @param oArgs.oldIndex {Number} The previous index position.
13510
 
     */
13511
 
 
13512
 
    /**
13513
 
     * Fired when a column is hidden.
13514
 
     *
13515
 
     * @event columnHideEvent
13516
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13517
 
     */
13518
 
 
13519
 
    /**
13520
 
     * Fired when a column is shown.
13521
 
     *
13522
 
     * @event columnShowEvent
13523
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13524
 
     */
13525
 
 
13526
 
    /**
13527
 
     * Fired when a column is selected.
13528
 
     *
13529
 
     * @event columnSelectEvent
13530
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13531
 
     */
13532
 
 
13533
 
    /**
13534
 
     * Fired when a column is unselected.
13535
 
     *
13536
 
     * @event columnUnselectEvent
13537
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13538
 
     */
13539
 
    /**
13540
 
     * Fired when a column is removed.
13541
 
     *
13542
 
     * @event columnRemoveEvent
13543
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13544
 
     */
13545
 
 
13546
 
    /**
13547
 
     * Fired when a column is inserted.
13548
 
     *
13549
 
     * @event columnInsertEvent
13550
 
     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
13551
 
     * @param oArgs.index {Number} The index position.
13552
 
     */
13553
 
 
13554
 
    /**
13555
 
     * Fired when a column is highlighted.
13556
 
     *
13557
 
     * @event columnHighlightEvent
13558
 
     * @param oArgs.column {YAHOO.widget.Column} The highlighted Column.
13559
 
     */
13560
 
 
13561
 
    /**
13562
 
     * Fired when a column is unhighlighted.
13563
 
     *
13564
 
     * @event columnUnhighlightEvent
13565
 
     * @param oArgs.column {YAHOO.widget.Column} The unhighlighted Column.
13566
 
     */
13567
 
 
13568
 
 
13569
 
    /**
13570
 
     * Fired when a row has a mouseover.
13571
 
     *
13572
 
     * @event rowMouseoverEvent
13573
 
     * @param oArgs.event {HTMLEvent} The event object.
13574
 
     * @param oArgs.target {HTMLElement} The TR element.
13575
 
     */
13576
 
 
13577
 
    /**
13578
 
     * Fired when a row has a mouseout.
13579
 
     *
13580
 
     * @event rowMouseoutEvent
13581
 
     * @param oArgs.event {HTMLEvent} The event object.
13582
 
     * @param oArgs.target {HTMLElement} The TR element.
13583
 
     */
13584
 
 
13585
 
    /**
13586
 
     * Fired when a row has a mousedown.
13587
 
     *
13588
 
     * @event rowMousedownEvent
13589
 
     * @param oArgs.event {HTMLEvent} The event object.
13590
 
     * @param oArgs.target {HTMLElement} The TR element.
13591
 
     */
13592
 
 
13593
 
    /**
13594
 
     * Fired when a row has a mouseup.
13595
 
     *
13596
 
     * @event rowMouseupEvent
13597
 
     * @param oArgs.event {HTMLEvent} The event object.
13598
 
     * @param oArgs.target {HTMLElement} The TR element.
13599
 
     */
13600
 
 
13601
 
    /**
13602
 
     * Fired when a row has a click.
13603
 
     *
13604
 
     * @event rowClickEvent
13605
 
     * @param oArgs.event {HTMLEvent} The event object.
13606
 
     * @param oArgs.target {HTMLElement} The TR element.
13607
 
     */
13608
 
 
13609
 
    /**
13610
 
     * Fired when a row has a dblclick.
13611
 
     *
13612
 
     * @event rowDblclickEvent
13613
 
     * @param oArgs.event {HTMLEvent} The event object.
13614
 
     * @param oArgs.target {HTMLElement} The TR element.
13615
 
     */
13616
 
 
13617
 
    /**
13618
 
     * Fired when a row is added.
13619
 
     *
13620
 
     * @event rowAddEvent
13621
 
     * @param oArgs.record {YAHOO.widget.Record} The added Record.
13622
 
     */
13623
 
     
13624
 
    /**
13625
 
     * Fired when rows are added.
13626
 
     *
13627
 
     * @event rowsAddEvent
13628
 
     * @param oArgs.record {YAHOO.widget.Record[]} The added Records.
13629
 
     */
13630
 
 
13631
 
    /**
13632
 
     * Fired when a row is updated.
13633
 
     *
13634
 
     * @event rowUpdateEvent
13635
 
     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
13636
 
     * @param oArgs.oldData {Object} Object literal of the old data.
13637
 
     */
13638
 
 
13639
 
    /**
13640
 
     * Fired when a row is deleted.
13641
 
     *
13642
 
     * @event rowDeleteEvent
13643
 
     * @param oArgs.oldData {Object} Object literal of the deleted data.
13644
 
     * @param oArgs.recordIndex {Number} Index of the deleted Record.
13645
 
     * @param oArgs.trElIndex {Number} Index of the deleted TR element, if on current page.
13646
 
     */
13647
 
     
13648
 
    /**
13649
 
     * Fired when rows are deleted.
13650
 
     *
13651
 
     * @event rowsDeleteEvent
13652
 
     * @param oArgs.oldData {Object[]} Array of object literals of the deleted data.
13653
 
     * @param oArgs.recordIndex {Number} Index of the first deleted Record.
13654
 
     * @param oArgs.count {Number} Number of deleted Records.
13655
 
     */
13656
 
 
13657
 
    /**
13658
 
     * Fired when a row is selected.
13659
 
     *
13660
 
     * @event rowSelectEvent
13661
 
     * @param oArgs.el {HTMLElement} The selected TR element, if applicable.
13662
 
     * @param oArgs.record {YAHOO.widget.Record} The selected Record.
13663
 
     */
13664
 
 
13665
 
    /**
13666
 
     * Fired when a row is unselected.
13667
 
     *
13668
 
     * @event rowUnselectEvent
13669
 
     * @param oArgs.el {HTMLElement} The unselected TR element, if applicable.
13670
 
     * @param oArgs.record {YAHOO.widget.Record} The unselected Record.
13671
 
     */
13672
 
 
13673
 
    /**
13674
 
     * Fired when all row selections are cleared.
13675
 
     *
13676
 
     * @event unselectAllRowsEvent
13677
 
     */
13678
 
 
13679
 
    /**
13680
 
     * Fired when a row is highlighted.
13681
 
     *
13682
 
     * @event rowHighlightEvent
13683
 
     * @param oArgs.el {HTMLElement} The highlighted TR element.
13684
 
     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
13685
 
     */
13686
 
 
13687
 
    /**
13688
 
     * Fired when a row is unhighlighted.
13689
 
     *
13690
 
     * @event rowUnhighlightEvent
13691
 
     * @param oArgs.el {HTMLElement} The highlighted TR element.
13692
 
     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
13693
 
     */
13694
 
 
13695
 
    /**
13696
 
     * Fired when a cell is updated.
13697
 
     *
13698
 
     * @event cellUpdateEvent
13699
 
     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
13700
 
     * @param oArgs.column {YAHOO.widget.Column} The updated Column.
13701
 
     * @param oArgs.oldData {Object} Original data value of the updated cell.
13702
 
     */
13703
 
 
13704
 
    /**
13705
 
     * Fired when a cell has a mouseover.
13706
 
     *
13707
 
     * @event cellMouseoverEvent
13708
 
     * @param oArgs.event {HTMLEvent} The event object.
13709
 
     * @param oArgs.target {HTMLElement} The TD element.
13710
 
     */
13711
 
 
13712
 
    /**
13713
 
     * Fired when a cell has a mouseout.
13714
 
     *
13715
 
     * @event cellMouseoutEvent
13716
 
     * @param oArgs.event {HTMLEvent} The event object.
13717
 
     * @param oArgs.target {HTMLElement} The TD element.
13718
 
     */
13719
 
 
13720
 
    /**
13721
 
     * Fired when a cell has a mousedown.
13722
 
     *
13723
 
     * @event cellMousedownEvent
13724
 
     * @param oArgs.event {HTMLEvent} The event object.
13725
 
     * @param oArgs.target {HTMLElement} The TD element.
13726
 
     */
13727
 
 
13728
 
    /**
13729
 
     * Fired when a cell has a mouseup.
13730
 
     *
13731
 
     * @event cellMouseupEvent
13732
 
     * @param oArgs.event {HTMLEvent} The event object.
13733
 
     * @param oArgs.target {HTMLElement} The TD element.
13734
 
     */
13735
 
 
13736
 
    /**
13737
 
     * Fired when a cell has a click.
13738
 
     *
13739
 
     * @event cellClickEvent
13740
 
     * @param oArgs.event {HTMLEvent} The event object.
13741
 
     * @param oArgs.target {HTMLElement} The TD element.
13742
 
     */
13743
 
 
13744
 
    /**
13745
 
     * Fired when a cell has a dblclick.
13746
 
     *
13747
 
     * @event cellDblclickEvent
13748
 
     * @param oArgs.event {HTMLEvent} The event object.
13749
 
     * @param oArgs.target {HTMLElement} The TD element.
13750
 
     */
13751
 
 
13752
 
    /**
13753
 
     * Fired when a cell is formatted.
13754
 
     *
13755
 
     * @event cellFormatEvent
13756
 
     * @param oArgs.el {HTMLElement} The formatted TD element.
13757
 
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13758
 
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13759
 
     * @param oArgs.key {String} (deprecated) The key of the formatted cell.
13760
 
     */
13761
 
 
13762
 
    /**
13763
 
     * Fired when a cell is selected.
13764
 
     *
13765
 
     * @event cellSelectEvent
13766
 
     * @param oArgs.el {HTMLElement} The selected TD element.
13767
 
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13768
 
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13769
 
     * @param oArgs.key {String} (deprecated) The key of the selected cell.
13770
 
     */
13771
 
 
13772
 
    /**
13773
 
     * Fired when a cell is unselected.
13774
 
     *
13775
 
     * @event cellUnselectEvent
13776
 
     * @param oArgs.el {HTMLElement} The unselected TD element.
13777
 
     * @param oArgs.record {YAHOO.widget.Record} The associated Record.
13778
 
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13779
 
     * @param oArgs.key {String} (deprecated) The key of the unselected cell.
13780
 
 
13781
 
     */
13782
 
 
13783
 
    /**
13784
 
     * Fired when a cell is highlighted.
13785
 
     *
13786
 
     * @event cellHighlightEvent
13787
 
     * @param oArgs.el {HTMLElement} The highlighted TD element.
13788
 
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13789
 
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13790
 
     * @param oArgs.key {String} (deprecated) The key of the highlighted cell.
13791
 
 
13792
 
     */
13793
 
 
13794
 
    /**
13795
 
     * Fired when a cell is unhighlighted.
13796
 
     *
13797
 
     * @event cellUnhighlightEvent
13798
 
     * @param oArgs.el {HTMLElement} The unhighlighted TD element.
13799
 
     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13800
 
     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13801
 
     * @param oArgs.key {String} (deprecated) The key of the unhighlighted cell.
13802
 
 
13803
 
     */
13804
 
 
13805
 
    /**
13806
 
     * Fired when all cell selections are cleared.
13807
 
     *
13808
 
     * @event unselectAllCellsEvent
13809
 
     */
13810
 
 
13811
 
    /**
13812
 
     * Fired when a CellEditor is shown.
13813
 
     *
13814
 
     * @event editorShowEvent
13815
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13816
 
     */
13817
 
 
13818
 
    /**
13819
 
     * Fired when a CellEditor has a keydown.
13820
 
     *
13821
 
     * @event editorKeydownEvent
13822
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13823
 
     * @param oArgs.event {HTMLEvent} The event object.
13824
 
     */
13825
 
 
13826
 
    /**
13827
 
     * Fired when a CellEditor input is reverted.
13828
 
     *
13829
 
     * @event editorRevertEvent
13830
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13831
 
     * @param oArgs.newData {Object} New data value from form input field.
13832
 
     * @param oArgs.oldData {Object} Old data value.
13833
 
     */
13834
 
 
13835
 
    /**
13836
 
     * Fired when a CellEditor input is saved.
13837
 
     *
13838
 
     * @event editorSaveEvent
13839
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13840
 
     * @param oArgs.newData {Object} New data value from form input field.
13841
 
     * @param oArgs.oldData {Object} Old data value.
13842
 
     */
13843
 
 
13844
 
    /**
13845
 
     * Fired when a CellEditor input is canceled.
13846
 
     *
13847
 
     * @event editorCancelEvent
13848
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13849
 
     */
13850
 
 
13851
 
    /**
13852
 
     * Fired when a CellEditor has a blur event.
13853
 
     *
13854
 
     * @event editorBlurEvent
13855
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13856
 
     */
13857
 
 
13858
 
    /**
13859
 
     * Fired when a CellEditor is blocked.
13860
 
     *
13861
 
     * @event editorBlockEvent
13862
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13863
 
     */
13864
 
 
13865
 
    /**
13866
 
     * Fired when a CellEditor is unblocked.
13867
 
     *
13868
 
     * @event editorUnblockEvent
13869
 
     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13870
 
     */
13871
 
 
13872
 
 
13873
 
 
13874
 
 
13875
 
 
13876
 
    /**
13877
 
     * Fired when a link is clicked.
13878
 
     *
13879
 
     * @event linkClickEvent
13880
 
     * @param oArgs.event {HTMLEvent} The event object.
13881
 
     * @param oArgs.target {HTMLElement} The A element.
13882
 
     */
13883
 
 
13884
 
    /**
13885
 
     * Fired when a BUTTON element or INPUT element of type "button", "image",
13886
 
     * "submit", "reset" is clicked.
13887
 
     *
13888
 
     * @event buttonClickEvent
13889
 
     * @param oArgs.event {HTMLEvent} The event object.
13890
 
     * @param oArgs.target {HTMLElement} The BUTTON element.
13891
 
     */
13892
 
 
13893
 
    /**
13894
 
     * Fired when a CHECKBOX element is clicked.
13895
 
     *
13896
 
     * @event checkboxClickEvent
13897
 
     * @param oArgs.event {HTMLEvent} The event object.
13898
 
     * @param oArgs.target {HTMLElement} The CHECKBOX element.
13899
 
     */
13900
 
 
13901
 
    /**
13902
 
     * Fired when a SELECT element is changed.
13903
 
     *
13904
 
     * @event dropdownChangeEvent
13905
 
     * @param oArgs.event {HTMLEvent} The event object.
13906
 
     * @param oArgs.target {HTMLElement} The SELECT element.
13907
 
     */
13908
 
 
13909
 
    /**
13910
 
     * Fired when a RADIO element is clicked.
13911
 
     *
13912
 
     * @event radioClickEvent
13913
 
     * @param oArgs.event {HTMLEvent} The event object.
13914
 
     * @param oArgs.target {HTMLElement} The RADIO element.
13915
 
     */
13916
 
 
13917
 
 
13918
 
 
13919
 
 
13920
 
 
13921
 
 
13922
 
 
13923
 
 
13924
 
 
13925
 
 
13926
 
 
13927
 
 
13928
 
 
13929
 
 
13930
 
 
13931
 
 
13932
 
 
13933
 
 
13934
 
 
13935
 
 
13936
 
 
13937
 
 
13938
 
 
13939
 
 
13940
 
 
13941
 
 
13942
 
/////////////////////////////////////////////////////////////////////////////
13943
 
//
13944
 
// Deprecated APIs
13945
 
//
13946
 
/////////////////////////////////////////////////////////////////////////////
13947
 
  
13948
 
/*
13949
 
 * @method showCellEditorBtns
13950
 
 * @deprecated Use CellEditor.renderBtns() 
13951
 
 */
13952
 
showCellEditorBtns : function(elContainer) {
13953
 
    // Buttons
13954
 
    var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
13955
 
    Dom.addClass(elBtnsDiv, DT.CLASS_BUTTON);
13956
 
 
13957
 
    // Save button
13958
 
    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
13959
 
    Dom.addClass(elSaveBtn, DT.CLASS_DEFAULT);
13960
 
    elSaveBtn.innerHTML = "OK";
13961
 
    Ev.addListener(elSaveBtn, "click", function(oArgs, oSelf) {
13962
 
        oSelf.onEventSaveCellEditor(oArgs, oSelf);
13963
 
        oSelf.focusTbodyEl();
13964
 
    }, this, true);
13965
 
 
13966
 
    // Cancel button
13967
 
    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
13968
 
    elCancelBtn.innerHTML = "Cancel";
13969
 
    Ev.addListener(elCancelBtn, "click", function(oArgs, oSelf) {
13970
 
        oSelf.onEventCancelCellEditor(oArgs, oSelf);
13971
 
        oSelf.focusTbodyEl();
13972
 
    }, this, true);
13973
 
 
13974
 
    YAHOO.log("The method showCellEditorBtns() has been deprecated." +
13975
 
            " Please use the CellEditor class.", "warn", this.toString());
13976
 
},
13977
 
 
13978
 
/**
13979
 
 * @method resetCellEditor
13980
 
 * @deprecated Use destroyCellEditor 
13981
 
 */
13982
 
resetCellEditor : function() {
13983
 
    var elContainer = this._oCellEditor.container;
13984
 
    elContainer.style.display = "none";
13985
 
    Ev.purgeElement(elContainer, true);
13986
 
    elContainer.innerHTML = "";
13987
 
    this._oCellEditor.value = null;
13988
 
    this._oCellEditor.isActive = false;
13989
 
 
13990
 
    YAHOO.log("The method resetCellEditor() has been deprecated." +
13991
 
            " Please use the CellEditor class.", "warn", this.toString());
13992
 
},
13993
 
 
13994
 
/**
13995
 
 * @event editorUpdateEvent
13996
 
 * @deprecated Use CellEditor class.
13997
 
 */
13998
 
 
13999
 
/**
14000
 
 * @method getBody
14001
 
 * @deprecated Use getTbodyEl().
14002
 
 */
14003
 
getBody : function() {
14004
 
    // Backward compatibility
14005
 
    YAHOO.log("The method getBody() has been deprecated" +
14006
 
            " in favor of getTbodyEl()", "warn", this.toString());
14007
 
    return this.getTbodyEl();
14008
 
},
14009
 
 
14010
 
/**
14011
 
 * @method getCell
14012
 
 * @deprecated Use getTdEl().
14013
 
 */
14014
 
getCell : function(index) {
14015
 
    // Backward compatibility
14016
 
    YAHOO.log("The method getCell() has been deprecated" +
14017
 
            " in favor of getTdEl()", "warn", this.toString());
14018
 
    return this.getTdEl(index);
14019
 
},
14020
 
 
14021
 
/**
14022
 
 * @method getRow
14023
 
 * @deprecated Use getTrEl().
14024
 
 */
14025
 
getRow : function(index) {
14026
 
    // Backward compatibility
14027
 
    YAHOO.log("The method getRow() has been deprecated" +
14028
 
            " in favor of getTrEl()", "warn", this.toString());
14029
 
    return this.getTrEl(index);
14030
 
},
14031
 
 
14032
 
/**
14033
 
 * @method refreshView
14034
 
 * @deprecated Use render.
14035
 
 */
14036
 
refreshView : function() {
14037
 
    // Backward compatibility
14038
 
    YAHOO.log("The method refreshView() has been deprecated" +
14039
 
            " in favor of render()", "warn", this.toString());
14040
 
    this.render();
14041
 
},
14042
 
 
14043
 
/**
14044
 
 * @method select
14045
 
 * @deprecated Use selectRow.
14046
 
 */
14047
 
select : function(els) {
14048
 
    // Backward compatibility
14049
 
    YAHOO.log("The method select() has been deprecated" +
14050
 
            " in favor of selectRow()", "warn", this.toString());
14051
 
    if(!lang.isArray(els)) {
14052
 
        els = [els];
14053
 
    }
14054
 
    for(var i=0; i<els.length; i++) {
14055
 
        this.selectRow(els[i]);
14056
 
    }
14057
 
},
14058
 
 
14059
 
/**
14060
 
 * @method onEventEditCell
14061
 
 * @deprecated Use onEventShowCellEditor.
14062
 
 */
14063
 
onEventEditCell : function(oArgs) {
14064
 
    // Backward compatibility
14065
 
    YAHOO.log("The method onEventEditCell() has been deprecated" +
14066
 
        " in favor of onEventShowCellEditor()", "warn", this.toString());
14067
 
    this.onEventShowCellEditor(oArgs);
14068
 
},
14069
 
 
14070
 
/**
14071
 
 * @method _syncColWidths
14072
 
 * @deprecated Use validateColumnWidths.
14073
 
 */
14074
 
_syncColWidths : function() {
14075
 
    // Backward compatibility
14076
 
    YAHOO.log("The method _syncColWidths() has been deprecated" +
14077
 
        " in favor of validateColumnWidths()", "warn", this.toString());
14078
 
    this.validateColumnWidths();
14079
 
}
14080
 
 
14081
 
/**
14082
 
 * @event headerRowMouseoverEvent
14083
 
 * @deprecated Use theadRowMouseoverEvent.
14084
 
 */
14085
 
 
14086
 
/**
14087
 
 * @event headerRowMouseoutEvent
14088
 
 * @deprecated Use theadRowMouseoutEvent.
14089
 
 */
14090
 
 
14091
 
/**
14092
 
 * @event headerRowMousedownEvent
14093
 
 * @deprecated Use theadRowMousedownEvent.
14094
 
 */
14095
 
 
14096
 
/**
14097
 
 * @event headerRowClickEvent
14098
 
 * @deprecated Use theadRowClickEvent.
14099
 
 */
14100
 
 
14101
 
/**
14102
 
 * @event headerRowDblclickEvent
14103
 
 * @deprecated Use theadRowDblclickEvent.
14104
 
 */
14105
 
 
14106
 
/**
14107
 
 * @event headerCellMouseoverEvent
14108
 
 * @deprecated Use theadCellMouseoverEvent.
14109
 
 */
14110
 
 
14111
 
/**
14112
 
 * @event headerCellMouseoutEvent
14113
 
 * @deprecated Use theadCellMouseoutEvent.
14114
 
 */
14115
 
 
14116
 
/**
14117
 
 * @event headerCellMousedownEvent
14118
 
 * @deprecated Use theadCellMousedownEvent.
14119
 
 */
14120
 
 
14121
 
/**
14122
 
 * @event headerCellClickEvent
14123
 
 * @deprecated Use theadCellClickEvent.
14124
 
 */
14125
 
 
14126
 
/**
14127
 
 * @event headerCellDblclickEvent
14128
 
 * @deprecated Use theadCellDblclickEvent.
14129
 
 */
14130
 
 
14131
 
/**
14132
 
 * @event headerLabelMouseoverEvent
14133
 
 * @deprecated Use theadLabelMouseoverEvent.
14134
 
 */
14135
 
 
14136
 
/**
14137
 
 * @event headerLabelMouseoutEvent
14138
 
 * @deprecated Use theadLabelMouseoutEvent.
14139
 
 */
14140
 
 
14141
 
/**
14142
 
 * @event headerLabelMousedownEvent
14143
 
 * @deprecated Use theadLabelMousedownEvent.
14144
 
 */
14145
 
 
14146
 
/**
14147
 
 * @event headerLabelClickEvent
14148
 
 * @deprecated Use theadLabelClickEvent.
14149
 
 */
14150
 
 
14151
 
/**
14152
 
 * @event headerLabelDbllickEvent
14153
 
 * @deprecated Use theadLabelDblclickEvent.
14154
 
 */
14155
 
 
14156
 
});
14157
 
 
14158
 
/**
14159
 
 * Alias for onDataReturnSetRows for backward compatibility
14160
 
 * @method onDataReturnSetRecords
14161
 
 * @deprecated Use onDataReturnSetRows
14162
 
 */
14163
 
DT.prototype.onDataReturnSetRecords = DT.prototype.onDataReturnSetRows;
14164
 
 
14165
 
/**
14166
 
 * Alias for onPaginatorChange for backward compatibility
14167
 
 * @method onPaginatorChange
14168
 
 * @deprecated Use onPaginatorChangeRequest
14169
 
 */
14170
 
DT.prototype.onPaginatorChange = DT.prototype.onPaginatorChangeRequest;
14171
 
 
14172
 
/////////////////////////////////////////////////////////////////////////////
14173
 
//
14174
 
// Deprecated static APIs
14175
 
//
14176
 
/////////////////////////////////////////////////////////////////////////////
14177
 
/**
14178
 
 * @method DataTable.formatTheadCell
14179
 
 * @deprecated  Use formatTheadCell.
14180
 
 */
14181
 
DT.formatTheadCell = function() {};
14182
 
 
14183
 
/**
14184
 
 * @method DataTable.editCheckbox
14185
 
 * @deprecated  Use YAHOO.widget.CheckboxCellEditor.
14186
 
 */
14187
 
DT.editCheckbox = function() {};
14188
 
 
14189
 
/**
14190
 
 * @method DataTable.editDate
14191
 
 * @deprecated Use YAHOO.widget.DateCellEditor.
14192
 
 */
14193
 
DT.editDate = function() {};
14194
 
 
14195
 
/**
14196
 
 * @method DataTable.editDropdown
14197
 
 * @deprecated Use YAHOO.widget.DropdownCellEditor.
14198
 
 */
14199
 
DT.editDropdown = function() {};
14200
 
 
14201
 
/**
14202
 
 * @method DataTable.editRadio
14203
 
 * @deprecated Use YAHOO.widget.RadioCellEditor.
14204
 
 */
14205
 
DT.editRadio = function() {};
14206
 
 
14207
 
/**
14208
 
 * @method DataTable.editTextarea
14209
 
 * @deprecated Use YAHOO.widget.TextareaCellEditor
14210
 
 */
14211
 
DT.editTextarea = function() {};
14212
 
 
14213
 
/**
14214
 
 * @method DataTable.editTextbox
14215
 
 * @deprecated Use YAHOO.widget.TextboxCellEditor
14216
 
 */
14217
 
DT.editTextbox= function() {};
14218
 
 
14219
 
})();
14220
 
 
14221
 
(function () {
14222
 
 
14223
 
var lang   = YAHOO.lang,
14224
 
    util   = YAHOO.util,
14225
 
    widget = YAHOO.widget,
14226
 
    ua     = YAHOO.env.ua,
14227
 
    
14228
 
    Dom    = util.Dom,
14229
 
    Ev     = util.Event,
14230
 
    DS     = util.DataSourceBase,
14231
 
    DT     = widget.DataTable,
14232
 
    Pag    = widget.Paginator;
14233
 
    
14234
 
/**
14235
 
 * The ScrollingDataTable class extends the DataTable class to provide
14236
 
 * functionality for x-scrolling, y-scrolling, and xy-scrolling.
14237
 
 *
14238
 
 * @namespace YAHOO.widget
14239
 
 * @class ScrollingDataTable
14240
 
 * @extends YAHOO.widget.DataTable
14241
 
 * @constructor
14242
 
 * @param elContainer {HTMLElement} Container element for the TABLE.
14243
 
 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
14244
 
 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
14245
 
 * @param oConfigs {object} (optional) Object literal of configuration values.
14246
 
 */
14247
 
widget.ScrollingDataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
14248
 
    oConfigs = oConfigs || {};
14249
 
    
14250
 
    // Prevent infinite loop
14251
 
    if(oConfigs.scrollable) {
14252
 
        oConfigs.scrollable = false;
14253
 
    }
14254
 
 
14255
 
    widget.ScrollingDataTable.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs); 
14256
 
 
14257
 
    // Once per instance
14258
 
    this.subscribe("columnShowEvent", this._onColumnChange);
14259
 
};
14260
 
 
14261
 
var SDT = widget.ScrollingDataTable;
14262
 
 
14263
 
/////////////////////////////////////////////////////////////////////////////
14264
 
//
14265
 
// Public constants
14266
 
//
14267
 
/////////////////////////////////////////////////////////////////////////////
14268
 
lang.augmentObject(SDT, {
14269
 
 
14270
 
    /**
14271
 
     * Class name assigned to inner DataTable header container.
14272
 
     *
14273
 
     * @property DataTable.CLASS_HEADER
14274
 
     * @type String
14275
 
     * @static
14276
 
     * @final
14277
 
     * @default "yui-dt-hd"
14278
 
     */
14279
 
    CLASS_HEADER : "yui-dt-hd",
14280
 
    
14281
 
    /**
14282
 
     * Class name assigned to inner DataTable body container.
14283
 
     *
14284
 
     * @property DataTable.CLASS_BODY
14285
 
     * @type String
14286
 
     * @static
14287
 
     * @final
14288
 
     * @default "yui-dt-bd"
14289
 
     */
14290
 
    CLASS_BODY : "yui-dt-bd"
14291
 
});
14292
 
 
14293
 
lang.extend(SDT, DT, {
14294
 
 
14295
 
/**
14296
 
 * Container for fixed header TABLE element.
14297
 
 *
14298
 
 * @property _elHdContainer
14299
 
 * @type HTMLElement
14300
 
 * @private
14301
 
 */
14302
 
_elHdContainer : null,
14303
 
 
14304
 
/**
14305
 
 * Fixed header TABLE element.
14306
 
 *
14307
 
 * @property _elHdTable
14308
 
 * @type HTMLElement
14309
 
 * @private
14310
 
 */
14311
 
_elHdTable : null,
14312
 
 
14313
 
/**
14314
 
 * Container for scrolling body TABLE element.
14315
 
 *
14316
 
 * @property _elBdContainer
14317
 
 * @type HTMLElement
14318
 
 * @private
14319
 
 */
14320
 
_elBdContainer : null,
14321
 
 
14322
 
/**
14323
 
 * Body THEAD element.
14324
 
 *
14325
 
 * @property _elBdThead
14326
 
 * @type HTMLElement
14327
 
 * @private
14328
 
 */
14329
 
_elBdThead : null,
14330
 
 
14331
 
/**
14332
 
 * Offscreen container to temporarily clone SDT for auto-width calculation.
14333
 
 *
14334
 
 * @property _elTmpContainer
14335
 
 * @type HTMLElement
14336
 
 * @private
14337
 
 */
14338
 
_elTmpContainer : null,
14339
 
 
14340
 
/**
14341
 
 * Offscreen TABLE element for auto-width calculation.
14342
 
 *
14343
 
 * @property _elTmpTable
14344
 
 * @type HTMLElement
14345
 
 * @private
14346
 
 */
14347
 
_elTmpTable : null,
14348
 
 
14349
 
/**
14350
 
 * True if x-scrollbar is currently visible.
14351
 
 * @property _bScrollbarX
14352
 
 * @type Boolean
14353
 
 * @private 
14354
 
 */
14355
 
_bScrollbarX : null,
14356
 
 
14357
 
 
14358
 
 
14359
 
 
14360
 
 
14361
 
 
14362
 
 
14363
 
 
14364
 
 
14365
 
 
14366
 
 
14367
 
 
14368
 
 
14369
 
 
14370
 
 
14371
 
/////////////////////////////////////////////////////////////////////////////
14372
 
//
14373
 
// Superclass methods
14374
 
//
14375
 
/////////////////////////////////////////////////////////////////////////////
14376
 
 
14377
 
/**
14378
 
 * Implementation of Element's abstract method. Sets up config values.
14379
 
 *
14380
 
 * @method initAttributes
14381
 
 * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
14382
 
 * @private
14383
 
 */
14384
 
 
14385
 
initAttributes : function(oConfigs) {
14386
 
    oConfigs = oConfigs || {};
14387
 
    SDT.superclass.initAttributes.call(this, oConfigs);
14388
 
 
14389
 
    /**
14390
 
    * @attribute width
14391
 
    * @description Table width for scrollable tables (e.g., "40em").
14392
 
    * @type String
14393
 
    */
14394
 
    this.setAttributeConfig("width", {
14395
 
        value: null,
14396
 
        validator: lang.isString,
14397
 
        method: function(oParam) {
14398
 
            if(this._elHdContainer && this._elBdContainer) {
14399
 
                this._elHdContainer.style.width = oParam;
14400
 
                this._elBdContainer.style.width = oParam;            
14401
 
                this._syncScrollX();      
14402
 
                this._syncScrollOverhang();
14403
 
            }
14404
 
        }
14405
 
    });
14406
 
 
14407
 
    /**
14408
 
    * @attribute height
14409
 
    * @description Table body height for scrollable tables, not including headers (e.g., "40em").
14410
 
    * @type String
14411
 
    */
14412
 
    this.setAttributeConfig("height", {
14413
 
        value: null,
14414
 
        validator: lang.isString,
14415
 
        method: function(oParam) {
14416
 
            if(this._elHdContainer && this._elBdContainer) {
14417
 
                this._elBdContainer.style.height = oParam;    
14418
 
                this._syncScrollX();   
14419
 
                this._syncScrollY();
14420
 
                this._syncScrollOverhang();
14421
 
            }
14422
 
        }
14423
 
    });
14424
 
 
14425
 
    /**
14426
 
    * @attribute COLOR_COLUMNFILLER
14427
 
    * @description CSS color value assigned to header filler on scrollable tables.  
14428
 
    * @type String
14429
 
    * @default "#F2F2F2"
14430
 
    */
14431
 
    this.setAttributeConfig("COLOR_COLUMNFILLER", {
14432
 
        value: "#F2F2F2",
14433
 
        validator: lang.isString,
14434
 
        method: function(oParam) {
14435
 
            this._elHdContainer.style.backgroundColor = oParam;
14436
 
        }
14437
 
    });
14438
 
},
14439
 
 
14440
 
/**
14441
 
 * Initializes DOM elements for a ScrollingDataTable, including creation of
14442
 
 * two separate TABLE elements.
14443
 
 *
14444
 
 * @method _initDomElements
14445
 
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 
14446
 
 * return {Boolean} False in case of error, otherwise true 
14447
 
 * @private
14448
 
 */
14449
 
_initDomElements : function(elContainer) {
14450
 
    // Outer and inner containers
14451
 
    this._initContainerEl(elContainer);
14452
 
    if(this._elContainer && this._elHdContainer && this._elBdContainer) {
14453
 
        // TABLEs
14454
 
        this._initTableEl();
14455
 
        
14456
 
        if(this._elHdTable && this._elTable) {
14457
 
            // COLGROUPs
14458
 
            ///this._initColgroupEl(this._elHdTable, this._elTable);  
14459
 
            this._initColgroupEl(this._elHdTable);        
14460
 
            
14461
 
            // THEADs
14462
 
            this._initTheadEl(this._elHdTable, this._elTable);
14463
 
            
14464
 
            // Primary TBODY
14465
 
            this._initTbodyEl(this._elTable);
14466
 
            // Message TBODY
14467
 
            this._initMsgTbodyEl(this._elTable);            
14468
 
        }
14469
 
    }
14470
 
    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody ||
14471
 
            !this._elHdTable || !this._elBdThead) {
14472
 
        YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
14473
 
        return false;
14474
 
    }
14475
 
    else {
14476
 
        return true;
14477
 
    }
14478
 
},
14479
 
 
14480
 
/**
14481
 
 * Destroy's the DataTable outer and inner container elements, if available.
14482
 
 *
14483
 
 * @method _destroyContainerEl
14484
 
 * @param elContainer {HTMLElement} Reference to the container element. 
14485
 
 * @private
14486
 
 */
14487
 
_destroyContainerEl : function(elContainer) {
14488
 
    Dom.removeClass(elContainer, DT.CLASS_SCROLLABLE);
14489
 
    SDT.superclass._destroyContainerEl.call(this, elContainer);
14490
 
    this._elHdContainer = null;
14491
 
    this._elBdContainer = null;
14492
 
},
14493
 
 
14494
 
/**
14495
 
 * Initializes the DataTable outer container element and creates inner header
14496
 
 * and body container elements.
14497
 
 *
14498
 
 * @method _initContainerEl
14499
 
 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
14500
 
 * @private
14501
 
 */
14502
 
_initContainerEl : function(elContainer) {
14503
 
    SDT.superclass._initContainerEl.call(this, elContainer);
14504
 
    
14505
 
    if(this._elContainer) {
14506
 
        elContainer = this._elContainer; // was constructor input, now is DOM ref
14507
 
        Dom.addClass(elContainer, DT.CLASS_SCROLLABLE);
14508
 
        
14509
 
        // Container for header TABLE
14510
 
        var elHdContainer = document.createElement("div");
14511
 
        elHdContainer.style.width = this.get("width") || "";
14512
 
        elHdContainer.style.backgroundColor = this.get("COLOR_COLUMNFILLER");
14513
 
        Dom.addClass(elHdContainer, SDT.CLASS_HEADER);
14514
 
        this._elHdContainer = elHdContainer;
14515
 
        elContainer.appendChild(elHdContainer);
14516
 
    
14517
 
        // Container for body TABLE
14518
 
        var elBdContainer = document.createElement("div");
14519
 
        elBdContainer.style.width = this.get("width") || "";
14520
 
        elBdContainer.style.height = this.get("height") || "";
14521
 
        Dom.addClass(elBdContainer, SDT.CLASS_BODY);
14522
 
        Ev.addListener(elBdContainer, "scroll", this._onScroll, this); // to sync horiz scroll headers
14523
 
        this._elBdContainer = elBdContainer;
14524
 
        elContainer.appendChild(elBdContainer);
14525
 
    }
14526
 
},
14527
 
 
14528
 
/**
14529
 
 * Creates HTML markup CAPTION element.
14530
 
 *
14531
 
 * @method _initCaptionEl
14532
 
 * @param sCaption {String} Text for caption.
14533
 
 * @private
14534
 
 */
14535
 
_initCaptionEl : function(sCaption) {
14536
 
    // Not yet supported
14537
 
    /*if(this._elHdTable && sCaption) {
14538
 
        // Create CAPTION element
14539
 
        if(!this._elCaption) { 
14540
 
            this._elCaption = this._elHdTable.createCaption();
14541
 
        }
14542
 
        // Set CAPTION value
14543
 
        this._elCaption.innerHTML = sCaption;
14544
 
    }
14545
 
    else if(this._elCaption) {
14546
 
        this._elCaption.parentNode.removeChild(this._elCaption);
14547
 
    }*/
14548
 
},
14549
 
 
14550
 
/**
14551
 
 * Destroy's the DataTable head TABLE element, if available.
14552
 
 *
14553
 
 * @method _destroyHdTableEl
14554
 
 * @private
14555
 
 */
14556
 
_destroyHdTableEl : function() {
14557
 
    var elTable = this._elHdTable;
14558
 
    if(elTable) {
14559
 
        Ev.purgeElement(elTable, true);
14560
 
        elTable.parentNode.removeChild(elTable);
14561
 
        
14562
 
        // A little out of place, but where else can we null out these extra elements?
14563
 
        ///this._elBdColgroup = null;
14564
 
        this._elBdThead = null;
14565
 
    }
14566
 
},
14567
 
 
14568
 
/**
14569
 
 * Initializes ScrollingDataTable TABLE elements into the two inner containers.
14570
 
 *
14571
 
 * @method _initTableEl
14572
 
 * @private
14573
 
 */
14574
 
_initTableEl : function() {
14575
 
    // Head TABLE
14576
 
    if(this._elHdContainer) {
14577
 
        this._destroyHdTableEl();
14578
 
    
14579
 
        // Create TABLE
14580
 
        this._elHdTable = this._elHdContainer.appendChild(document.createElement("table"));   
14581
 
    } 
14582
 
    // Body TABLE
14583
 
    SDT.superclass._initTableEl.call(this, this._elBdContainer);
14584
 
},
14585
 
 
14586
 
/**
14587
 
 * Initializes ScrollingDataTable THEAD elements into the two inner containers.
14588
 
 *
14589
 
 * @method _initTheadEl
14590
 
 * @param elHdTable {HTMLElement} (optional) Fixed header TABLE element reference.
14591
 
 * @param elTable {HTMLElement} (optional) TABLE element reference.
14592
 
 * @private
14593
 
 */
14594
 
_initTheadEl : function(elHdTable, elTable) {
14595
 
    elHdTable = elHdTable || this._elHdTable;
14596
 
    elTable = elTable || this._elTable;
14597
 
    
14598
 
    // Scrolling body's THEAD
14599
 
    this._initBdTheadEl(elTable);
14600
 
    // Standard fixed head THEAD
14601
 
    SDT.superclass._initTheadEl.call(this, elHdTable);
14602
 
},
14603
 
 
14604
 
/**
14605
 
 * SDT changes ID so as not to duplicate the accessibility TH IDs.
14606
 
 *
14607
 
 * @method _initThEl
14608
 
 * @param elTh {HTMLElement} TH element reference.
14609
 
 * @param oColumn {YAHOO.widget.Column} Column object.
14610
 
 * @private
14611
 
 */
14612
 
_initThEl : function(elTh, oColumn) {
14613
 
    SDT.superclass._initThEl.call(this, elTh, oColumn);
14614
 
    elTh.id = this.getId() +"-fixedth-" + oColumn.getSanitizedKey(); // Needed for getColumn by TH and ColumnDD
14615
 
},
14616
 
 
14617
 
/**
14618
 
 * Destroy's the DataTable body THEAD element, if available.
14619
 
 *
14620
 
 * @method _destroyBdTheadEl
14621
 
 * @private
14622
 
 */
14623
 
_destroyBdTheadEl : function() {
14624
 
    var elBdThead = this._elBdThead;
14625
 
    if(elBdThead) {
14626
 
        var elTable = elBdThead.parentNode;
14627
 
        Ev.purgeElement(elBdThead, true);
14628
 
        elTable.removeChild(elBdThead);
14629
 
        this._elBdThead = null;
14630
 
 
14631
 
        this._destroyColumnHelpers();
14632
 
    }
14633
 
},
14634
 
 
14635
 
/**
14636
 
 * Initializes body THEAD element.
14637
 
 *
14638
 
 * @method _initBdTheadEl
14639
 
 * @param elTable {HTMLElement} TABLE element into which to create THEAD.
14640
 
 * @return {HTMLElement} Initialized THEAD element. 
14641
 
 * @private
14642
 
 */
14643
 
_initBdTheadEl : function(elTable) {
14644
 
    if(elTable) {
14645
 
        // Destroy previous
14646
 
        this._destroyBdTheadEl();
14647
 
 
14648
 
        var elThead = elTable.insertBefore(document.createElement("thead"), elTable.firstChild);
14649
 
        
14650
 
        // Add TRs to the THEAD;
14651
 
        var oColumnSet = this._oColumnSet,
14652
 
            colTree = oColumnSet.tree,
14653
 
            elTh, elTheadTr, oColumn, i, j, k, len;
14654
 
 
14655
 
        for(i=0, k=colTree.length; i<k; i++) {
14656
 
            elTheadTr = elThead.appendChild(document.createElement("tr"));
14657
 
    
14658
 
            // ...and create TH cells
14659
 
            for(j=0, len=colTree[i].length; j<len; j++) {
14660
 
                oColumn = colTree[i][j];
14661
 
                elTh = elTheadTr.appendChild(document.createElement("th"));
14662
 
                this._initBdThEl(elTh,oColumn,i,j);
14663
 
            }
14664
 
        }
14665
 
        this._elBdThead = elThead;
14666
 
        YAHOO.log("Accessibility TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
14667
 
    }
14668
 
},
14669
 
 
14670
 
/**
14671
 
 * Populates TH element for the body THEAD element.
14672
 
 *
14673
 
 * @method _initBdThEl
14674
 
 * @param elTh {HTMLElement} TH element reference.
14675
 
 * @param oColumn {YAHOO.widget.Column} Column object.
14676
 
 * @private
14677
 
 */
14678
 
_initBdThEl : function(elTh, oColumn) {
14679
 
    elTh.id = this.getId()+"-th-" + oColumn.getSanitizedKey(); // Needed for accessibility
14680
 
    elTh.rowSpan = oColumn.getRowspan();
14681
 
    elTh.colSpan = oColumn.getColspan();
14682
 
    // Assign abbr attribute
14683
 
    if(oColumn.abbr) {
14684
 
        elTh.abbr = oColumn.abbr;
14685
 
    }
14686
 
 
14687
 
    // TODO: strip links and form elements
14688
 
    var sKey = oColumn.getKey();
14689
 
    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
14690
 
    elTh.innerHTML = sLabel;
14691
 
},
14692
 
 
14693
 
/**
14694
 
 * Initializes ScrollingDataTable TBODY element for data
14695
 
 *
14696
 
 * @method _initTbodyEl
14697
 
 * @param elTable {HTMLElement} TABLE element into which to create TBODY .
14698
 
 * @private
14699
 
 */
14700
 
_initTbodyEl : function(elTable) {
14701
 
    SDT.superclass._initTbodyEl.call(this, elTable);
14702
 
    
14703
 
    // Bug 2105534 - Safari 3 gap
14704
 
    // Bug 2492591 - IE8 offsetTop
14705
 
    elTable.style.marginTop = (this._elTbody.offsetTop > 0) ?
14706
 
            "-"+this._elTbody.offsetTop+"px" : 0;
14707
 
},
14708
 
 
14709
 
 
14710
 
 
14711
 
 
14712
 
 
14713
 
 
14714
 
 
14715
 
 
14716
 
 
14717
 
 
14718
 
 
14719
 
 
14720
 
 
14721
 
 
14722
 
 
14723
 
 
14724
 
 
14725
 
 
14726
 
 
14727
 
 
14728
 
 
14729
 
 
14730
 
 
14731
 
 
14732
 
 
14733
 
 
14734
 
 
14735
 
 
14736
 
 
14737
 
/**
14738
 
 * Sets focus on the given element.
14739
 
 *
14740
 
 * @method _focusEl
14741
 
 * @param el {HTMLElement} Element.
14742
 
 * @private
14743
 
 */
14744
 
_focusEl : function(el) {
14745
 
    el = el || this._elTbody;
14746
 
    var oSelf = this;
14747
 
    this._storeScrollPositions();
14748
 
    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
14749
 
    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
14750
 
    // strange unexpected things as the user clicks on buttons and other controls.
14751
 
    
14752
 
    // Bug 1921135: Wrap the whole thing in a setTimeout
14753
 
    setTimeout(function() {
14754
 
        setTimeout(function() {
14755
 
            try {
14756
 
                el.focus();
14757
 
                oSelf._restoreScrollPositions();
14758
 
            }
14759
 
            catch(e) {
14760
 
            }
14761
 
        },0);
14762
 
    }, 0);
14763
 
},
14764
 
 
14765
 
 
14766
 
 
14767
 
 
14768
 
 
14769
 
 
14770
 
 
14771
 
 
14772
 
 
14773
 
 
14774
 
 
14775
 
 
14776
 
 
14777
 
 
14778
 
 
14779
 
 
14780
 
 
14781
 
 
14782
 
 
14783
 
/**
14784
 
 * Internal wrapper calls run() on render Chain instance.
14785
 
 *
14786
 
 * @method _runRenderChain
14787
 
 * @private 
14788
 
 */
14789
 
_runRenderChain : function() {
14790
 
    this._storeScrollPositions();
14791
 
    this._oChainRender.run();
14792
 
},
14793
 
 
14794
 
/**
14795
 
 * Stores scroll positions so they can be restored after a render. 
14796
 
 *
14797
 
 * @method _storeScrollPositions
14798
 
 * @private 
14799
 
 */
14800
 
 _storeScrollPositions : function() {
14801
 
    this._nScrollTop = this._elBdContainer.scrollTop;
14802
 
    this._nScrollLeft = this._elBdContainer.scrollLeft;
14803
 
},
14804
 
 
14805
 
/**
14806
 
 * Restores scroll positions to stored value. 
14807
 
 *
14808
 
 * @method _retoreScrollPositions
14809
 
 * @private 
14810
 
 */
14811
 
 _restoreScrollPositions : function() {
14812
 
    // Reset scroll positions
14813
 
    if(this._nScrollTop) {
14814
 
        this._elBdContainer.scrollTop = this._nScrollTop;
14815
 
        this._nScrollTop = null;
14816
 
    } 
14817
 
    if(this._nScrollLeft) {
14818
 
        this._elBdContainer.scrollLeft = this._nScrollLeft;
14819
 
        this._nScrollLeft = null;
14820
 
    } 
14821
 
},
14822
 
 
14823
 
/**
14824
 
 * Helper function calculates and sets a validated width for a Column in a ScrollingDataTable.
14825
 
 *
14826
 
 * @method _validateColumnWidth
14827
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
14828
 
 * @param elTd {HTMLElement} TD element to validate against.
14829
 
 * @private
14830
 
 */
14831
 
_validateColumnWidth : function(oColumn, elTd) {
14832
 
    // Only Columns without widths that are not hidden
14833
 
    if(!oColumn.width && !oColumn.hidden) {
14834
 
        var elTh = oColumn.getThEl();
14835
 
        // Unset a calculated auto-width
14836
 
        if(oColumn._calculatedWidth) {
14837
 
            this._setColumnWidth(oColumn, "auto", "visible");
14838
 
        }
14839
 
        // Compare auto-widths
14840
 
        if(elTh.offsetWidth !== elTd.offsetWidth) {
14841
 
            var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
14842
 
                    oColumn.getThLinerEl() : elTd.firstChild;               
14843
 
 
14844
 
            // Grab the wider liner width, unless the minWidth is wider
14845
 
            var newWidth = Math.max(0,
14846
 
                (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
14847
 
                oColumn.minWidth);
14848
 
                
14849
 
            var sOverflow = 'visible';
14850
 
            
14851
 
            // Now validate against maxAutoWidth
14852
 
            if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
14853
 
                newWidth = oColumn.maxAutoWidth;
14854
 
                sOverflow = "hidden";
14855
 
            }
14856
 
 
14857
 
            // Set to the wider auto-width
14858
 
            this._elTbody.style.display = "none";
14859
 
            this._setColumnWidth(oColumn, newWidth+'px', sOverflow);
14860
 
            oColumn._calculatedWidth = newWidth;
14861
 
            this._elTbody.style.display = "";
14862
 
        }
14863
 
    }
14864
 
},
14865
 
 
14866
 
/**
14867
 
 * For one or all Columns of a ScrollingDataTable, when Column is not hidden,
14868
 
 * and width is not set, syncs widths of header and body cells and 
14869
 
 * validates that width against minWidth and/or maxAutoWidth as necessary.
14870
 
 *
14871
 
 * @method validateColumnWidths
14872
 
 * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
14873
 
 */
14874
 
validateColumnWidths : function(oColumn) {
14875
 
    // Validate there is at least one TR with proper TDs
14876
 
    var allKeys   = this._oColumnSet.keys,
14877
 
        allKeysLength = allKeys.length,
14878
 
        elRow     = this.getFirstTrEl();
14879
 
 
14880
 
    // Reset overhang for IE
14881
 
    if(ua.ie) {
14882
 
        this._setOverhangValue(1);
14883
 
    }
14884
 
 
14885
 
    if(allKeys && elRow && (elRow.childNodes.length === allKeysLength)) {
14886
 
        // Temporarily unsnap container since it causes inaccurate calculations
14887
 
        var sWidth = this.get("width");
14888
 
        if(sWidth) {
14889
 
            this._elHdContainer.style.width = "";
14890
 
            this._elBdContainer.style.width = "";
14891
 
        }
14892
 
        this._elContainer.style.width = "";
14893
 
        
14894
 
        //Validate just one Column
14895
 
        if(oColumn && lang.isNumber(oColumn.getKeyIndex())) {
14896
 
            this._validateColumnWidth(oColumn, elRow.childNodes[oColumn.getKeyIndex()]);
14897
 
        }
14898
 
        // Iterate through all Columns to unset calculated widths in one pass
14899
 
        else {
14900
 
            var elTd, todos = [], thisTodo, i, len;
14901
 
            for(i=0; i<allKeysLength; i++) {
14902
 
                oColumn = allKeys[i];
14903
 
                // Only Columns without widths that are not hidden, unset a calculated auto-width
14904
 
                if(!oColumn.width && !oColumn.hidden && oColumn._calculatedWidth) {
14905
 
                    todos[todos.length] = oColumn;      
14906
 
                }
14907
 
            }
14908
 
            
14909
 
            this._elTbody.style.display = "none";
14910
 
            for(i=0, len=todos.length; i<len; i++) {
14911
 
                this._setColumnWidth(todos[i], "auto", "visible");
14912
 
            }
14913
 
            this._elTbody.style.display = "";
14914
 
            
14915
 
            todos = [];
14916
 
 
14917
 
            // Iterate through all Columns and make the store the adjustments to make in one pass
14918
 
            for(i=0; i<allKeysLength; i++) {
14919
 
                oColumn = allKeys[i];
14920
 
                elTd = elRow.childNodes[i];
14921
 
                // Only Columns without widths that are not hidden
14922
 
                if(!oColumn.width && !oColumn.hidden) {
14923
 
                    var elTh = oColumn.getThEl();
14924
 
 
14925
 
                    // Compare auto-widths
14926
 
                    if(elTh.offsetWidth !== elTd.offsetWidth) {
14927
 
                        var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
14928
 
                                oColumn.getThLinerEl() : elTd.firstChild;               
14929
 
                
14930
 
                        // Grab the wider liner width, unless the minWidth is wider
14931
 
                        var newWidth = Math.max(0,
14932
 
                            (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
14933
 
                            oColumn.minWidth);
14934
 
                            
14935
 
                        var sOverflow = 'visible';
14936
 
                        
14937
 
                        // Now validate against maxAutoWidth
14938
 
                        if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
14939
 
                            newWidth = oColumn.maxAutoWidth;
14940
 
                            sOverflow = "hidden";
14941
 
                        }
14942
 
                
14943
 
                        todos[todos.length] = [oColumn, newWidth, sOverflow];
14944
 
                    }
14945
 
                }
14946
 
            }
14947
 
            
14948
 
            this._elTbody.style.display = "none";
14949
 
            for(i=0, len=todos.length; i<len; i++) {
14950
 
                thisTodo = todos[i];
14951
 
                // Set to the wider auto-width
14952
 
                this._setColumnWidth(thisTodo[0], thisTodo[1]+"px", thisTodo[2]);
14953
 
                thisTodo[0]._calculatedWidth = thisTodo[1];
14954
 
            }
14955
 
            this._elTbody.style.display = "";
14956
 
        }
14957
 
    
14958
 
        // Resnap unsnapped containers
14959
 
        if(sWidth) {
14960
 
            this._elHdContainer.style.width = sWidth;
14961
 
            this._elBdContainer.style.width = sWidth;
14962
 
        } 
14963
 
    }
14964
 
    
14965
 
    this._syncScroll();
14966
 
    this._restoreScrollPositions();
14967
 
},
14968
 
 
14969
 
/**
14970
 
 * Syncs padding around scrollable tables, including Column header right-padding
14971
 
 * and container width and height.
14972
 
 *
14973
 
 * @method _syncScroll
14974
 
 * @private 
14975
 
 */
14976
 
_syncScroll : function() {
14977
 
    this._syncScrollX();
14978
 
    this._syncScrollY();
14979
 
    this._syncScrollOverhang();
14980
 
    if(ua.opera) {
14981
 
        // Bug 1925874
14982
 
        this._elHdContainer.scrollLeft = this._elBdContainer.scrollLeft;
14983
 
        if(!this.get("width")) {
14984
 
            // Bug 1926125
14985
 
            document.body.style += '';
14986
 
        }
14987
 
    }
14988
 
 },
14989
 
 
14990
 
/**
14991
 
 * Snaps container width for y-scrolling tables.
14992
 
 *
14993
 
 * @method _syncScrollY
14994
 
 * @private
14995
 
 */
14996
 
_syncScrollY : function() {
14997
 
    var elTbody = this._elTbody,
14998
 
        elBdContainer = this._elBdContainer;
14999
 
    
15000
 
    // X-scrolling not enabled
15001
 
    if(!this.get("width")) {
15002
 
        // Snap outer container width to content
15003
 
        this._elContainer.style.width = 
15004
 
                (elBdContainer.scrollHeight > elBdContainer.clientHeight) ?
15005
 
                // but account for y-scrollbar since it is visible
15006
 
                (elTbody.parentNode.clientWidth + 19) + "px" :
15007
 
                // no y-scrollbar, just borders
15008
 
                (elTbody.parentNode.clientWidth + 2) + "px";
15009
 
    }
15010
 
},
15011
 
 
15012
 
/**
15013
 
 * Snaps container height for x-scrolling tables in IE. Syncs message TBODY width.
15014
 
 *
15015
 
 * @method _syncScrollX
15016
 
 * @private
15017
 
 */
15018
 
_syncScrollX : function() {
15019
 
    var elTbody = this._elTbody,
15020
 
        elBdContainer = this._elBdContainer;
15021
 
    
15022
 
    // IE 6 and 7 only when y-scrolling not enabled
15023
 
    if(!this.get("height") && (ua.ie)) {
15024
 
        // Snap outer container height to content
15025
 
        elBdContainer.style.height = 
15026
 
                // but account for x-scrollbar if it is visible
15027
 
                (elBdContainer.scrollWidth > elBdContainer.offsetWidth ) ?
15028
 
                (elTbody.parentNode.offsetHeight + 18) + "px" : 
15029
 
                elTbody.parentNode.offsetHeight + "px";
15030
 
    }
15031
 
 
15032
 
    // Sync message tbody
15033
 
    if(this._elTbody.rows.length === 0) {
15034
 
        this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
15035
 
    }
15036
 
    else {
15037
 
        this._elMsgTbody.parentNode.style.width = "";
15038
 
    }
15039
 
},
15040
 
 
15041
 
/**
15042
 
 * Adds/removes Column header overhang as necesary.
15043
 
 *
15044
 
 * @method _syncScrollOverhang
15045
 
 * @private
15046
 
 */
15047
 
_syncScrollOverhang : function() {
15048
 
    var elBdContainer = this._elBdContainer,
15049
 
        // Overhang should be either 1 (default) or 18px, depending on the location of the right edge of the table
15050
 
        nPadding = 1;
15051
 
    
15052
 
    // Y-scrollbar is visible, which is when the overhang needs to jut out
15053
 
    if((elBdContainer.scrollHeight > elBdContainer.clientHeight) &&
15054
 
        // X-scrollbar is also visible, which means the right is jagged, not flush with the Column
15055
 
        (elBdContainer.scrollWidth > elBdContainer.clientWidth)) {
15056
 
        nPadding = 18;
15057
 
    }
15058
 
    
15059
 
    this._setOverhangValue(nPadding);
15060
 
    
15061
 
},
15062
 
 
15063
 
/**
15064
 
 * Sets Column header overhang to given width.
15065
 
 *
15066
 
 * @method _setOverhangValue
15067
 
 * @param nBorderWidth {Number} Value of new border for overhang. 
15068
 
 * @private
15069
 
 */
15070
 
_setOverhangValue : function(nBorderWidth) {
15071
 
    var aLastHeaders = this._oColumnSet.headers[this._oColumnSet.headers.length-1] || [],
15072
 
        len = aLastHeaders.length,
15073
 
        sPrefix = this._sId+"-fixedth-",
15074
 
        sValue = nBorderWidth + "px solid " + this.get("COLOR_COLUMNFILLER");
15075
 
 
15076
 
    this._elThead.style.display = "none";
15077
 
    for(var i=0; i<len; i++) {
15078
 
        Dom.get(sPrefix+aLastHeaders[i]).style.borderRight = sValue;
15079
 
    }
15080
 
    this._elThead.style.display = "";
15081
 
},
15082
 
 
15083
 
 
15084
 
 
15085
 
 
15086
 
 
15087
 
 
15088
 
 
15089
 
 
15090
 
 
15091
 
 
15092
 
 
15093
 
 
15094
 
 
15095
 
 
15096
 
 
15097
 
 
15098
 
 
15099
 
 
15100
 
 
15101
 
 
15102
 
 
15103
 
 
15104
 
 
15105
 
 
15106
 
 
15107
 
 
15108
 
 
15109
 
 
15110
 
 
15111
 
 
15112
 
 
15113
 
 
15114
 
 
15115
 
 
15116
 
 
15117
 
 
15118
 
 
15119
 
 
15120
 
/**
15121
 
 * Returns DOM reference to the DataTable's fixed header container element.
15122
 
 *
15123
 
 * @method getHdContainerEl
15124
 
 * @return {HTMLElement} Reference to DIV element.
15125
 
 */
15126
 
getHdContainerEl : function() {
15127
 
    return this._elHdContainer;
15128
 
},
15129
 
 
15130
 
/**
15131
 
 * Returns DOM reference to the DataTable's scrolling body container element.
15132
 
 *
15133
 
 * @method getBdContainerEl
15134
 
 * @return {HTMLElement} Reference to DIV element.
15135
 
 */
15136
 
getBdContainerEl : function() {
15137
 
    return this._elBdContainer;
15138
 
},
15139
 
 
15140
 
/**
15141
 
 * Returns DOM reference to the DataTable's fixed header TABLE element.
15142
 
 *
15143
 
 * @method getHdTableEl
15144
 
 * @return {HTMLElement} Reference to TABLE element.
15145
 
 */
15146
 
getHdTableEl : function() {
15147
 
    return this._elHdTable;
15148
 
},
15149
 
 
15150
 
/**
15151
 
 * Returns DOM reference to the DataTable's scrolling body TABLE element.
15152
 
 *
15153
 
 * @method getBdTableEl
15154
 
 * @return {HTMLElement} Reference to TABLE element.
15155
 
 */
15156
 
getBdTableEl : function() {
15157
 
    return this._elTable;
15158
 
},
15159
 
 
15160
 
/**
15161
 
 * Disables ScrollingDataTable UI.
15162
 
 *
15163
 
 * @method disable
15164
 
 */
15165
 
disable : function() {
15166
 
    var elMask = this._elMask;
15167
 
    elMask.style.width = this._elBdContainer.offsetWidth + "px";
15168
 
    elMask.style.height = this._elHdContainer.offsetHeight + this._elBdContainer.offsetHeight + "px";
15169
 
    elMask.style.display = "";
15170
 
    this.fireEvent("disableEvent");
15171
 
},
15172
 
 
15173
 
/**
15174
 
 * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
15175
 
 * non-nested Columns, and top-level parent Columns (which will remove all
15176
 
 * children Columns).
15177
 
 *
15178
 
 * @method removeColumn
15179
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
15180
 
 * @return oColumn {YAHOO.widget.Column} Removed Column instance.
15181
 
 */
15182
 
removeColumn : function(oColumn) {
15183
 
    // Store scroll pos
15184
 
    var hdPos = this._elHdContainer.scrollLeft;
15185
 
    var bdPos = this._elBdContainer.scrollLeft;
15186
 
    
15187
 
    // Call superclass method
15188
 
    oColumn = SDT.superclass.removeColumn.call(this, oColumn);
15189
 
    
15190
 
    // Restore scroll pos
15191
 
    this._elHdContainer.scrollLeft = hdPos;
15192
 
    this._elBdContainer.scrollLeft = bdPos;
15193
 
    
15194
 
    return oColumn;
15195
 
},
15196
 
 
15197
 
/**
15198
 
 * Inserts given Column at the index if given, otherwise at the end. NOTE: You
15199
 
 * can only add non-nested Columns and top-level parent Columns. You cannot add
15200
 
 * a nested Column to an existing parent.
15201
 
 *
15202
 
 * @method insertColumn
15203
 
 * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
15204
 
 * definition or a Column instance.
15205
 
 * @param index {Number} (optional) New tree index.
15206
 
 * @return oColumn {YAHOO.widget.Column} Inserted Column instance. 
15207
 
 */
15208
 
insertColumn : function(oColumn, index) {
15209
 
    // Store scroll pos
15210
 
    var hdPos = this._elHdContainer.scrollLeft;
15211
 
    var bdPos = this._elBdContainer.scrollLeft;
15212
 
    
15213
 
    // Call superclass method
15214
 
    var oNewColumn = SDT.superclass.insertColumn.call(this, oColumn, index);
15215
 
    
15216
 
    // Restore scroll pos
15217
 
    this._elHdContainer.scrollLeft = hdPos;
15218
 
    this._elBdContainer.scrollLeft = bdPos;
15219
 
    
15220
 
    return oNewColumn;
15221
 
},
15222
 
 
15223
 
/**
15224
 
 * Removes given Column and inserts into given tree index. NOTE: You
15225
 
 * can only reorder non-nested Columns and top-level parent Columns. You cannot
15226
 
 * reorder a nested Column to an existing parent.
15227
 
 *
15228
 
 * @method reorderColumn
15229
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
15230
 
 * @param index {Number} New tree index.
15231
 
 */
15232
 
reorderColumn : function(oColumn, index) {
15233
 
    // Store scroll pos
15234
 
    var hdPos = this._elHdContainer.scrollLeft;
15235
 
    var bdPos = this._elBdContainer.scrollLeft;
15236
 
    
15237
 
    // Call superclass method
15238
 
    var oNewColumn = SDT.superclass.reorderColumn.call(this, oColumn, index);
15239
 
    
15240
 
    // Restore scroll pos
15241
 
    this._elHdContainer.scrollLeft = hdPos;
15242
 
    this._elBdContainer.scrollLeft = bdPos;
15243
 
 
15244
 
    return oNewColumn;
15245
 
},
15246
 
 
15247
 
/**
15248
 
 * Sets given Column to given pixel width. If new width is less than minWidth
15249
 
 * width, sets to minWidth. Updates oColumn.width value.
15250
 
 *
15251
 
 * @method setColumnWidth
15252
 
 * @param oColumn {YAHOO.widget.Column} Column instance.
15253
 
 * @param nWidth {Number} New width in pixels.
15254
 
 */
15255
 
setColumnWidth : function(oColumn, nWidth) {
15256
 
    oColumn = this.getColumn(oColumn);
15257
 
    if(oColumn) {
15258
 
        // Validate new width against minWidth
15259
 
        if(lang.isNumber(nWidth)) {
15260
 
            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
15261
 
 
15262
 
            // Save state
15263
 
            oColumn.width = nWidth;
15264
 
            
15265
 
            // Resize the DOM elements
15266
 
            this._setColumnWidth(oColumn, nWidth+"px");
15267
 
            this._syncScroll();
15268
 
            
15269
 
            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
15270
 
            YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
15271
 
        }
15272
 
        // Unsets a width to auto-size
15273
 
        else if(nWidth === null) {
15274
 
            // Save state
15275
 
            oColumn.width = nWidth;
15276
 
            
15277
 
            // Resize the DOM elements
15278
 
            this._setColumnWidth(oColumn, "auto");
15279
 
            this.validateColumnWidths(oColumn);
15280
 
            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
15281
 
            YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
15282
 
        }
15283
 
        
15284
 
        // Bug 2339454: resize then sort misaligment
15285
 
        this._clearTrTemplateEl();
15286
 
    }
15287
 
    else {
15288
 
        YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
15289
 
    }
15290
 
},
15291
 
 
15292
 
/**
15293
 
 * Displays message within secondary TBODY.
15294
 
 *
15295
 
 * @method showTableMessage
15296
 
 * @param sHTML {String} (optional) Value for innerHTMlang.
15297
 
 * @param sClassName {String} (optional) Classname.
15298
 
 */
15299
 
showTableMessage : function(sHTML, sClassName) {
15300
 
    var elCell = this._elMsgTd;
15301
 
    if(lang.isString(sHTML)) {
15302
 
        elCell.firstChild.innerHTML = sHTML;
15303
 
    }
15304
 
    if(lang.isString(sClassName)) {
15305
 
        Dom.addClass(elCell.firstChild, sClassName);
15306
 
    }
15307
 
 
15308
 
    // Needed for SDT only
15309
 
    var elThead = this.getTheadEl();
15310
 
    var elTable = elThead.parentNode;
15311
 
    var newWidth = elTable.offsetWidth;
15312
 
    this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
15313
 
 
15314
 
    this._elMsgTbody.style.display = "";
15315
 
 
15316
 
    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
15317
 
    YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
15318
 
},
15319
 
 
15320
 
 
15321
 
 
15322
 
 
15323
 
 
15324
 
 
15325
 
 
15326
 
 
15327
 
 
15328
 
 
15329
 
 
15330
 
 
15331
 
 
15332
 
/////////////////////////////////////////////////////////////////////////////
15333
 
//
15334
 
// Private Custom Event Handlers
15335
 
//
15336
 
/////////////////////////////////////////////////////////////////////////////
15337
 
 
15338
 
/**
15339
 
 * Handles Column mutations
15340
 
 *
15341
 
 * @method onColumnChange
15342
 
 * @param oArgs {Object} Custom Event data.
15343
 
 */
15344
 
_onColumnChange : function(oArg) {
15345
 
    // Figure out which Column changed
15346
 
    var oColumn = (oArg.column) ? oArg.column :
15347
 
            (oArg.editor) ? oArg.editor.column : null;
15348
 
    this._storeScrollPositions();
15349
 
    this.validateColumnWidths(oColumn);
15350
 
},
15351
 
 
15352
 
 
15353
 
 
15354
 
 
15355
 
 
15356
 
 
15357
 
 
15358
 
 
15359
 
 
15360
 
 
15361
 
 
15362
 
 
15363
 
 
15364
 
 
15365
 
 
15366
 
/////////////////////////////////////////////////////////////////////////////
15367
 
//
15368
 
// Private DOM Event Handlers
15369
 
//
15370
 
/////////////////////////////////////////////////////////////////////////////
15371
 
 
15372
 
/**
15373
 
 * Syncs scrolltop and scrollleft of all TABLEs.
15374
 
 *
15375
 
 * @method _onScroll
15376
 
 * @param e {HTMLEvent} The scroll event.
15377
 
 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
15378
 
 * @private
15379
 
 */
15380
 
_onScroll : function(e, oSelf) {
15381
 
    oSelf._elHdContainer.scrollLeft = oSelf._elBdContainer.scrollLeft;
15382
 
 
15383
 
    if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
15384
 
        oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
15385
 
        oSelf.cancelCellEditor();
15386
 
    }
15387
 
 
15388
 
    var elTarget = Ev.getTarget(e);
15389
 
    var elTag = elTarget.nodeName.toLowerCase();
15390
 
    oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget});
15391
 
},
15392
 
 
15393
 
/**
15394
 
 * Handles keydown events on the THEAD element.
15395
 
 *
15396
 
 * @method _onTheadKeydown
15397
 
 * @param e {HTMLEvent} The key event.
15398
 
 * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
15399
 
 * @private
15400
 
 */
15401
 
_onTheadKeydown : function(e, oSelf) {
15402
 
    // If tabbing to next TH label link causes THEAD to scroll,
15403
 
    // need to sync scrollLeft with TBODY
15404
 
    if(Ev.getCharCode(e) === 9) {
15405
 
        setTimeout(function() {
15406
 
            if((oSelf instanceof SDT) && oSelf._sId) {
15407
 
                oSelf._elBdContainer.scrollLeft = oSelf._elHdContainer.scrollLeft;
15408
 
            }
15409
 
        },0);
15410
 
    }
15411
 
    
15412
 
    var elTarget = Ev.getTarget(e);
15413
 
    var elTag = elTarget.nodeName.toLowerCase();
15414
 
    var bKeepBubbling = true;
15415
 
    while(elTarget && (elTag != "table")) {
15416
 
        switch(elTag) {
15417
 
            case "body":
15418
 
                return;
15419
 
            case "input":
15420
 
            case "textarea":
15421
 
                // TODO: implement textareaKeyEvent
15422
 
                break;
15423
 
            case "thead":
15424
 
                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
15425
 
                break;
15426
 
            default:
15427
 
                break;
15428
 
        }
15429
 
        if(bKeepBubbling === false) {
15430
 
            return;
15431
 
        }
15432
 
        else {
15433
 
            elTarget = elTarget.parentNode;
15434
 
            if(elTarget) {
15435
 
                elTag = elTarget.nodeName.toLowerCase();
15436
 
            }
15437
 
        }
15438
 
    }
15439
 
    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
15440
 
}
15441
 
 
15442
 
 
15443
 
 
15444
 
 
15445
 
/**
15446
 
 * Fired when a fixed scrolling DataTable has a scroll.
15447
 
 *
15448
 
 * @event tableScrollEvent
15449
 
 * @param oArgs.event {HTMLEvent} The event object.
15450
 
 * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE)
15451
 
 * or the DataTable's TBODY element (everyone else).
15452
 
 *
15453
 
 */
15454
 
 
15455
 
 
15456
 
 
15457
 
 
15458
 
});
15459
 
 
15460
 
})();
15461
 
 
15462
 
(function () {
15463
 
 
15464
 
var lang   = YAHOO.lang,
15465
 
    util   = YAHOO.util,
15466
 
    widget = YAHOO.widget,
15467
 
    ua     = YAHOO.env.ua,
15468
 
    
15469
 
    Dom    = util.Dom,
15470
 
    Ev     = util.Event,
15471
 
    
15472
 
    DT     = widget.DataTable;
15473
 
/****************************************************************************/
15474
 
/****************************************************************************/
15475
 
/****************************************************************************/
15476
 
    
15477
 
/**
15478
 
 * The BaseCellEditor class provides base functionality common to all inline cell
15479
 
 * editors for a DataTable widget.
15480
 
 *
15481
 
 * @namespace YAHOO.widget
15482
 
 * @class BaseCellEditor
15483
 
 * @uses YAHOO.util.EventProvider 
15484
 
 * @constructor
15485
 
 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
15486
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
15487
 
 */
15488
 
widget.BaseCellEditor = function(sType, oConfigs) {
15489
 
    this._sId = this._sId || "yui-ceditor" + YAHOO.widget.BaseCellEditor._nCount++;
15490
 
    this._sType = sType;
15491
 
    
15492
 
    // Validate inputs
15493
 
    this._initConfigs(oConfigs); 
15494
 
    
15495
 
    // Create Custom Events
15496
 
    this._initEvents();
15497
 
             
15498
 
    // Draw UI
15499
 
    this.render();
15500
 
};
15501
 
 
15502
 
var BCE = widget.BaseCellEditor;
15503
 
 
15504
 
/////////////////////////////////////////////////////////////////////////////
15505
 
//
15506
 
// Static members
15507
 
//
15508
 
/////////////////////////////////////////////////////////////////////////////
15509
 
lang.augmentObject(BCE, {
15510
 
 
15511
 
/**
15512
 
 * Global instance counter.
15513
 
 *
15514
 
 * @property CellEditor._nCount
15515
 
 * @type Number
15516
 
 * @static
15517
 
 * @default 0
15518
 
 * @private 
15519
 
 */
15520
 
_nCount : 0,
15521
 
 
15522
 
/**
15523
 
 * Class applied to CellEditor container.
15524
 
 *
15525
 
 * @property CellEditor.CLASS_CELLEDITOR
15526
 
 * @type String
15527
 
 * @static
15528
 
 * @default "yui-ceditor"
15529
 
 */
15530
 
CLASS_CELLEDITOR : "yui-ceditor"
15531
 
 
15532
 
});
15533
 
 
15534
 
BCE.prototype = {
15535
 
/////////////////////////////////////////////////////////////////////////////
15536
 
//
15537
 
// Private members
15538
 
//
15539
 
/////////////////////////////////////////////////////////////////////////////
15540
 
/**
15541
 
 * Unique id assigned to instance "yui-ceditorN", useful prefix for generating unique
15542
 
 * DOM ID strings and log messages.
15543
 
 *
15544
 
 * @property _sId
15545
 
 * @type String
15546
 
 * @private
15547
 
 */
15548
 
_sId : null,
15549
 
 
15550
 
/**
15551
 
 * Editor type.
15552
 
 *
15553
 
 * @property _sType
15554
 
 * @type String
15555
 
 * @private
15556
 
 */
15557
 
_sType : null,
15558
 
 
15559
 
/**
15560
 
 * DataTable instance.
15561
 
 *
15562
 
 * @property _oDataTable
15563
 
 * @type YAHOO.widget.DataTable
15564
 
 * @private 
15565
 
 */
15566
 
_oDataTable : null,
15567
 
 
15568
 
/**
15569
 
 * Column instance.
15570
 
 *
15571
 
 * @property _oColumn
15572
 
 * @type YAHOO.widget.Column
15573
 
 * @default null
15574
 
 */
15575
 
_oColumn : null,
15576
 
 
15577
 
/**
15578
 
 * Record instance.
15579
 
 *
15580
 
 * @property _oRecord
15581
 
 * @type YAHOO.widget.Record
15582
 
 * @default null
15583
 
 * @private 
15584
 
 */
15585
 
_oRecord : null,
15586
 
 
15587
 
/**
15588
 
 * TD element.
15589
 
 *
15590
 
 * @property _elTd
15591
 
 * @type HTMLElement
15592
 
 * @default null
15593
 
 * @private
15594
 
 */
15595
 
_elTd : null,
15596
 
 
15597
 
/**
15598
 
 * Container for inline editor.
15599
 
 *
15600
 
 * @property _elContainer
15601
 
 * @type HTMLElement
15602
 
 * @private 
15603
 
 */
15604
 
_elContainer : null,
15605
 
 
15606
 
/**
15607
 
 * Reference to Cancel button, if available.
15608
 
 *
15609
 
 * @property _elCancelBtn
15610
 
 * @type HTMLElement
15611
 
 * @default null
15612
 
 * @private 
15613
 
 */
15614
 
_elCancelBtn : null,
15615
 
 
15616
 
/**
15617
 
 * Reference to Save button, if available.
15618
 
 *
15619
 
 * @property _elSaveBtn
15620
 
 * @type HTMLElement
15621
 
 * @default null
15622
 
 * @private 
15623
 
 */
15624
 
_elSaveBtn : null,
15625
 
 
15626
 
 
15627
 
 
15628
 
 
15629
 
 
15630
 
 
15631
 
 
15632
 
 
15633
 
/////////////////////////////////////////////////////////////////////////////
15634
 
//
15635
 
// Private methods
15636
 
//
15637
 
/////////////////////////////////////////////////////////////////////////////
15638
 
 
15639
 
/**
15640
 
 * Initialize configs.
15641
 
 *
15642
 
 * @method _initConfigs
15643
 
 * @private   
15644
 
 */
15645
 
_initConfigs : function(oConfigs) {
15646
 
    // Object literal defines CellEditor configs
15647
 
    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
15648
 
        for(var sConfig in oConfigs) {
15649
 
            if(sConfig) {
15650
 
                this[sConfig] = oConfigs[sConfig];
15651
 
            }
15652
 
        }
15653
 
    }
15654
 
},
15655
 
 
15656
 
/**
15657
 
 * Initialize Custom Events.
15658
 
 *
15659
 
 * @method _initEvents
15660
 
 * @private   
15661
 
 */
15662
 
_initEvents : function() {
15663
 
    this.createEvent("showEvent");
15664
 
    this.createEvent("keydownEvent");
15665
 
    this.createEvent("invalidDataEvent");
15666
 
    this.createEvent("revertEvent");
15667
 
    this.createEvent("saveEvent");
15668
 
    this.createEvent("cancelEvent");
15669
 
    this.createEvent("blurEvent");
15670
 
    this.createEvent("blockEvent");
15671
 
    this.createEvent("unblockEvent");
15672
 
},
15673
 
 
15674
 
 
15675
 
 
15676
 
 
15677
 
 
15678
 
 
15679
 
 
15680
 
 
15681
 
 
15682
 
 
15683
 
 
15684
 
 
15685
 
 
15686
 
/////////////////////////////////////////////////////////////////////////////
15687
 
//
15688
 
// Public properties
15689
 
//
15690
 
/////////////////////////////////////////////////////////////////////////////
15691
 
/**
15692
 
 * Implementer defined function that can submit the input value to a server. This
15693
 
 * function must accept the arguments fnCallback and oNewValue. When the submission
15694
 
 * is complete, the function must also call fnCallback(bSuccess, oNewValue) to 
15695
 
 * finish the save routine in the CellEditor. This function can also be used to 
15696
 
 * perform extra validation or input value manipulation. 
15697
 
 *
15698
 
 * @property asyncSubmitter
15699
 
 * @type HTMLFunction
15700
 
 */
15701
 
asyncSubmitter : null,
15702
 
 
15703
 
/**
15704
 
 * Current value.
15705
 
 *
15706
 
 * @property value
15707
 
 * @type MIXED
15708
 
 */
15709
 
value : null,
15710
 
 
15711
 
/**
15712
 
 * Default value in case Record data is undefined. NB: Null values will not trigger
15713
 
 * the default value.
15714
 
 *
15715
 
 * @property defaultValue
15716
 
 * @type MIXED
15717
 
 * @default null
15718
 
 */
15719
 
defaultValue : null,
15720
 
 
15721
 
/**
15722
 
 * Validator function for input data, called from the DataTable instance scope,
15723
 
 * receives the arguments (inputValue, currentValue, editorInstance) and returns
15724
 
 * either the validated (or type-converted) value or undefined.
15725
 
 *
15726
 
 * @property validator
15727
 
 * @type HTMLFunction
15728
 
 * @default null
15729
 
 */
15730
 
validator : null,
15731
 
 
15732
 
/**
15733
 
 * If validation is enabled, resets input field of invalid data.
15734
 
 *
15735
 
 * @property resetInvalidData
15736
 
 * @type Boolean
15737
 
 * @default true
15738
 
 */
15739
 
resetInvalidData : true,
15740
 
 
15741
 
/**
15742
 
 * True if currently active.
15743
 
 *
15744
 
 * @property isActive
15745
 
 * @type Boolean
15746
 
 */
15747
 
isActive : false,
15748
 
 
15749
 
/**
15750
 
 * Text to display on Save button.
15751
 
 *
15752
 
 * @property LABEL_SAVE
15753
 
 * @type String
15754
 
 * @default "Save"
15755
 
 */
15756
 
LABEL_SAVE : "Save",
15757
 
 
15758
 
/**
15759
 
 * Text to display on Cancel button.
15760
 
 *
15761
 
 * @property LABEL_CANCEL
15762
 
 * @type String
15763
 
 * @default "Cancel"
15764
 
 */
15765
 
LABEL_CANCEL : "Cancel",
15766
 
 
15767
 
/**
15768
 
 * True if Save/Cancel buttons should not be displayed in the CellEditor.
15769
 
 *
15770
 
 * @property disableBtns
15771
 
 * @type Boolean
15772
 
 * @default false
15773
 
 */
15774
 
disableBtns : false,
15775
 
 
15776
 
 
15777
 
 
15778
 
 
15779
 
 
15780
 
 
15781
 
 
15782
 
/////////////////////////////////////////////////////////////////////////////
15783
 
//
15784
 
// Public methods
15785
 
//
15786
 
/////////////////////////////////////////////////////////////////////////////
15787
 
/**
15788
 
 * CellEditor instance name, for logging.
15789
 
 *
15790
 
 * @method toString
15791
 
 * @return {String} Unique name of the CellEditor instance.
15792
 
 */
15793
 
 
15794
 
toString : function() {
15795
 
    return "CellEditor instance " + this._sId;
15796
 
},
15797
 
 
15798
 
/**
15799
 
 * CellEditor unique ID.
15800
 
 *
15801
 
 * @method getId
15802
 
 * @return {String} Unique ID of the CellEditor instance.
15803
 
 */
15804
 
 
15805
 
getId : function() {
15806
 
    return this._sId;
15807
 
},
15808
 
 
15809
 
/**
15810
 
 * Returns reference to associated DataTable instance.
15811
 
 *
15812
 
 * @method getDataTable
15813
 
 * @return {YAHOO.widget.DataTable} DataTable instance.
15814
 
 */
15815
 
 
15816
 
getDataTable : function() {
15817
 
    return this._oDataTable;
15818
 
},
15819
 
 
15820
 
/**
15821
 
 * Returns reference to associated Column instance.
15822
 
 *
15823
 
 * @method getColumn
15824
 
 * @return {YAHOO.widget.Column} Column instance.
15825
 
 */
15826
 
 
15827
 
getColumn : function() {
15828
 
    return this._oColumn;
15829
 
},
15830
 
 
15831
 
/**
15832
 
 * Returns reference to associated Record instance.
15833
 
 *
15834
 
 * @method getRecord
15835
 
 * @return {YAHOO.widget.Record} Record instance.
15836
 
 */
15837
 
 
15838
 
getRecord : function() {
15839
 
    return this._oRecord;
15840
 
},
15841
 
 
15842
 
 
15843
 
 
15844
 
/**
15845
 
 * Returns reference to associated TD element.
15846
 
 *
15847
 
 * @method getTdEl
15848
 
 * @return {HTMLElement} TD element.
15849
 
 */
15850
 
 
15851
 
getTdEl : function() {
15852
 
    return this._elTd;
15853
 
},
15854
 
 
15855
 
/**
15856
 
 * Returns container element.
15857
 
 *
15858
 
 * @method getContainerEl
15859
 
 * @return {HTMLElement} Reference to container element.
15860
 
 */
15861
 
 
15862
 
getContainerEl : function() {
15863
 
    return this._elContainer;
15864
 
},
15865
 
 
15866
 
/**
15867
 
 * Nulls out the entire CellEditor instance and related objects, removes attached
15868
 
 * event listeners, and clears out DOM elements inside the container, removes
15869
 
 * container from the DOM.
15870
 
 *
15871
 
 * @method destroy
15872
 
 */
15873
 
destroy : function() {
15874
 
    this.unsubscribeAll();
15875
 
    
15876
 
    // Column is late-binding in attach()
15877
 
    var oColumn = this.getColumn();
15878
 
    if(oColumn) {
15879
 
        oColumn.editor = null;
15880
 
    }
15881
 
    
15882
 
    var elContainer = this.getContainerEl();
15883
 
    Ev.purgeElement(elContainer, true);
15884
 
    elContainer.parentNode.removeChild(elContainer);
15885
 
},
15886
 
 
15887
 
/**
15888
 
 * Renders DOM elements and attaches event listeners.
15889
 
 *
15890
 
 * @method render
15891
 
 */
15892
 
render : function() {
15893
 
    if(this._elContainer) {
15894
 
        YAHOO.util.Event.purgeElement(this._elContainer, true);
15895
 
        this._elContainer.innerHTML = "";
15896
 
    }
15897
 
 
15898
 
    // Render Cell Editor container element as first child of body
15899
 
    var elContainer = document.createElement("div");
15900
 
    elContainer.id = this.getId() + "-container"; // Needed for tracking blur event
15901
 
    elContainer.style.display = "none";
15902
 
    elContainer.tabIndex = 0;
15903
 
    elContainer.className = DT.CLASS_EDITOR;
15904
 
    document.body.insertBefore(elContainer, document.body.firstChild);
15905
 
    this._elContainer = elContainer;
15906
 
    
15907
 
    // Handle ESC key
15908
 
    Ev.addListener(elContainer, "keydown", function(e, oSelf) {
15909
 
        // ESC cancels Cell Editor
15910
 
        if((e.keyCode == 27)) {
15911
 
            var target = Ev.getTarget(e);
15912
 
            // workaround for Mac FF3 bug that disabled clicks when ESC hit when
15913
 
            // select is open. [bug 2273056]
15914
 
            if (target.nodeName && target.nodeName.toLowerCase() === 'select') {
15915
 
                target.blur();
15916
 
            }
15917
 
            oSelf.cancel();
15918
 
        }
15919
 
        // Pass through event
15920
 
        oSelf.fireEvent("keydownEvent", {editor:this, event:e});
15921
 
    }, this);
15922
 
    
15923
 
    this.renderForm();
15924
 
 
15925
 
    // Show Save/Cancel buttons
15926
 
    if(!this.disableBtns) {
15927
 
        this.renderBtns();
15928
 
    }
15929
 
    
15930
 
    this.doAfterRender();
15931
 
},
15932
 
 
15933
 
/**
15934
 
 * Renders Save/Cancel buttons.
15935
 
 *
15936
 
 * @method renderBtns
15937
 
 */
15938
 
renderBtns : function() {
15939
 
    // Buttons
15940
 
    var elBtnsDiv = this.getContainerEl().appendChild(document.createElement("div"));
15941
 
    elBtnsDiv.className = DT.CLASS_BUTTON;
15942
 
 
15943
 
    // Save button
15944
 
    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
15945
 
    elSaveBtn.className = DT.CLASS_DEFAULT;
15946
 
    elSaveBtn.innerHTML = this.LABEL_SAVE;
15947
 
    Ev.addListener(elSaveBtn, "click", function(oArgs) {
15948
 
        this.save();
15949
 
    }, this, true);
15950
 
    this._elSaveBtn = elSaveBtn;
15951
 
 
15952
 
    // Cancel button
15953
 
    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
15954
 
    elCancelBtn.innerHTML = this.LABEL_CANCEL;
15955
 
    Ev.addListener(elCancelBtn, "click", function(oArgs) {
15956
 
        this.cancel();
15957
 
    }, this, true);
15958
 
    this._elCancelBtn = elCancelBtn;
15959
 
},
15960
 
 
15961
 
/**
15962
 
 * Attach CellEditor for a new interaction.
15963
 
 *
15964
 
 * @method attach
15965
 
 * @param oDataTable {YAHOO.widget.DataTable} Associated DataTable instance.
15966
 
 * @param elCell {HTMLElement} Cell to edit.  
15967
 
 */
15968
 
attach : function(oDataTable, elCell) {
15969
 
    // Validate 
15970
 
    if(oDataTable instanceof YAHOO.widget.DataTable) {
15971
 
        this._oDataTable = oDataTable;
15972
 
        
15973
 
        // Validate cell
15974
 
        elCell = oDataTable.getTdEl(elCell);
15975
 
        if(elCell) {
15976
 
            this._elTd = elCell;
15977
 
 
15978
 
            // Validate Column
15979
 
            var oColumn = oDataTable.getColumn(elCell);
15980
 
            if(oColumn) {
15981
 
                this._oColumn = oColumn;
15982
 
                
15983
 
                // Validate Record
15984
 
                var oRecord = oDataTable.getRecord(elCell);
15985
 
                if(oRecord) {
15986
 
                    this._oRecord = oRecord;
15987
 
                    var value = oRecord.getData(this.getColumn().getKey());
15988
 
                    this.value = (value !== undefined) ? value : this.defaultValue;
15989
 
                    return true;
15990
 
                }
15991
 
            }            
15992
 
        }
15993
 
    }
15994
 
    YAHOO.log("Could not attach CellEditor","error",this.toString());
15995
 
    return false;
15996
 
},
15997
 
 
15998
 
/**
15999
 
 * Moves container into position for display.
16000
 
 *
16001
 
 * @method move
16002
 
 */
16003
 
move : function() {
16004
 
    // Move Editor
16005
 
    var elContainer = this.getContainerEl(),
16006
 
        elTd = this.getTdEl(),
16007
 
        x = Dom.getX(elTd),
16008
 
        y = Dom.getY(elTd);
16009
 
 
16010
 
    //TODO: remove scrolling logic
16011
 
    // SF doesn't get xy for cells in scrolling table
16012
 
    // when tbody display is set to block
16013
 
    if(isNaN(x) || isNaN(y)) {
16014
 
        var elTbody = this.getDataTable().getTbodyEl();
16015
 
        x = elTd.offsetLeft + // cell pos relative to table
16016
 
                Dom.getX(elTbody.parentNode) - // plus table pos relative to document
16017
 
                elTbody.scrollLeft; // minus tbody scroll
16018
 
        y = elTd.offsetTop + // cell pos relative to table
16019
 
                Dom.getY(elTbody.parentNode) - // plus table pos relative to document
16020
 
                elTbody.scrollTop + // minus tbody scroll
16021
 
                this.getDataTable().getTheadEl().offsetHeight; // account for fixed THEAD cells
16022
 
    }
16023
 
 
16024
 
    elContainer.style.left = x + "px";
16025
 
    elContainer.style.top = y + "px";
16026
 
},
16027
 
 
16028
 
/**
16029
 
 * Displays CellEditor UI in the correct position.
16030
 
 *
16031
 
 * @method show
16032
 
 */
16033
 
show : function() {
16034
 
    this.resetForm();
16035
 
    this.isActive = true;
16036
 
    this.getContainerEl().style.display = "";
16037
 
    this.focus();
16038
 
    this.fireEvent("showEvent", {editor:this});
16039
 
    YAHOO.log("CellEditor shown", "info", this.toString()); 
16040
 
},
16041
 
 
16042
 
/**
16043
 
 * Fires blockEvent
16044
 
 *
16045
 
 * @method block
16046
 
 */
16047
 
block : function() {
16048
 
    this.fireEvent("blockEvent", {editor:this});
16049
 
    YAHOO.log("CellEditor blocked", "info", this.toString()); 
16050
 
},
16051
 
 
16052
 
/**
16053
 
 * Fires unblockEvent
16054
 
 *
16055
 
 * @method unblock
16056
 
 */
16057
 
unblock : function() {
16058
 
    this.fireEvent("unblockEvent", {editor:this});
16059
 
    YAHOO.log("CellEditor unblocked", "info", this.toString()); 
16060
 
},
16061
 
 
16062
 
/**
16063
 
 * Saves value of CellEditor and hides UI.
16064
 
 *
16065
 
 * @method save
16066
 
 */
16067
 
save : function() {
16068
 
    // Get new value
16069
 
    var inputValue = this.getInputValue();
16070
 
    var validValue = inputValue;
16071
 
    
16072
 
    // Validate new value
16073
 
    if(this.validator) {
16074
 
        validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this);
16075
 
        if(validValue === undefined ) {
16076
 
            if(this.resetInvalidData) {
16077
 
                this.resetForm();
16078
 
            }
16079
 
            this.fireEvent("invalidDataEvent",
16080
 
                    {editor:this, oldData:this.value, newData:inputValue});
16081
 
            YAHOO.log("Could not save Cell Editor input due to invalid data " +
16082
 
                    lang.dump(inputValue), "warn", this.toString());
16083
 
            return;
16084
 
        }
16085
 
    }
16086
 
        
16087
 
    var oSelf = this;
16088
 
    var finishSave = function(bSuccess, oNewValue) {
16089
 
        var oOrigValue = oSelf.value;
16090
 
        if(bSuccess) {
16091
 
            // Update new value
16092
 
            oSelf.value = oNewValue;
16093
 
            oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue);
16094
 
            
16095
 
            // Hide CellEditor
16096
 
            oSelf.getContainerEl().style.display = "none";
16097
 
            oSelf.isActive = false;
16098
 
            oSelf.getDataTable()._oCellEditor =  null;
16099
 
            
16100
 
            oSelf.fireEvent("saveEvent",
16101
 
                    {editor:oSelf, oldData:oOrigValue, newData:oSelf.value});
16102
 
            YAHOO.log("Cell Editor input saved", "info", this.toString());
16103
 
        }
16104
 
        else {
16105
 
            oSelf.resetForm();
16106
 
            oSelf.fireEvent("revertEvent",
16107
 
                    {editor:oSelf, oldData:oOrigValue, newData:oNewValue});
16108
 
            YAHOO.log("Could not save Cell Editor input " +
16109
 
                    lang.dump(oNewValue), "warn", oSelf.toString());
16110
 
        }
16111
 
        oSelf.unblock();
16112
 
    };
16113
 
    
16114
 
    this.block();
16115
 
    if(lang.isFunction(this.asyncSubmitter)) {
16116
 
        this.asyncSubmitter.call(this, finishSave, validValue);
16117
 
    } 
16118
 
    else {   
16119
 
        finishSave(true, validValue);
16120
 
    }
16121
 
},
16122
 
 
16123
 
/**
16124
 
 * Cancels CellEditor input and hides UI.
16125
 
 *
16126
 
 * @method cancel
16127
 
 */
16128
 
cancel : function() {
16129
 
    if(this.isActive) {
16130
 
        this.getContainerEl().style.display = "none";
16131
 
        this.isActive = false;
16132
 
        this.getDataTable()._oCellEditor =  null;
16133
 
        this.fireEvent("cancelEvent", {editor:this});
16134
 
        YAHOO.log("CellEditor canceled", "info", this.toString());
16135
 
    }
16136
 
    else {
16137
 
        YAHOO.log("Unable to cancel CellEditor", "warn", this.toString());
16138
 
    }
16139
 
},
16140
 
 
16141
 
/**
16142
 
 * Renders form elements.
16143
 
 *
16144
 
 * @method renderForm
16145
 
 */
16146
 
renderForm : function() {
16147
 
    // To be implemented by subclass
16148
 
},
16149
 
 
16150
 
/**
16151
 
 * Access to add additional event listeners.
16152
 
 *
16153
 
 * @method doAfterRender
16154
 
 */
16155
 
doAfterRender : function() {
16156
 
    // To be implemented by subclass
16157
 
},
16158
 
 
16159
 
 
16160
 
/**
16161
 
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
16162
 
 * to save input without them. 
16163
 
 *
16164
 
 * @method handleDisabledBtns
16165
 
 */
16166
 
handleDisabledBtns : function() {
16167
 
    // To be implemented by subclass
16168
 
},
16169
 
 
16170
 
/**
16171
 
 * Resets CellEditor UI to initial state.
16172
 
 *
16173
 
 * @method resetForm
16174
 
 */
16175
 
resetForm : function() {
16176
 
    // To be implemented by subclass
16177
 
},
16178
 
 
16179
 
/**
16180
 
 * Sets focus in CellEditor.
16181
 
 *
16182
 
 * @method focus
16183
 
 */
16184
 
focus : function() {
16185
 
    // To be implemented by subclass
16186
 
},
16187
 
 
16188
 
/**
16189
 
 * Retrieves input value from CellEditor.
16190
 
 *
16191
 
 * @method getInputValue
16192
 
 */
16193
 
getInputValue : function() {
16194
 
    // To be implemented by subclass
16195
 
}
16196
 
 
16197
 
};
16198
 
 
16199
 
lang.augmentProto(BCE, util.EventProvider);
16200
 
 
16201
 
 
16202
 
/////////////////////////////////////////////////////////////////////////////
16203
 
//
16204
 
// Custom Events
16205
 
//
16206
 
/////////////////////////////////////////////////////////////////////////////
16207
 
 
16208
 
/**
16209
 
 * Fired when a CellEditor is shown.
16210
 
 *
16211
 
 * @event showEvent
16212
 
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16213
 
 */
16214
 
 
16215
 
/**
16216
 
 * Fired when a CellEditor has a keydown.
16217
 
 *
16218
 
 * @event keydownEvent
16219
 
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
16220
 
 * @param oArgs.event {HTMLEvent} The event object.
16221
 
 */
16222
 
 
16223
 
/**
16224
 
 * Fired when a CellEditor input is reverted due to invalid data.
16225
 
 *
16226
 
 * @event invalidDataEvent
16227
 
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
16228
 
 * @param oArgs.newData {Object} New data value from form input field.
16229
 
 * @param oArgs.oldData {Object} Old data value.
16230
 
 */
16231
 
 
16232
 
/**
16233
 
 * Fired when a CellEditor input is reverted due to asyncSubmitter failure.
16234
 
 *
16235
 
 * @event revertEvent
16236
 
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
16237
 
 * @param oArgs.newData {Object} New data value from form input field.
16238
 
 * @param oArgs.oldData {Object} Old data value.
16239
 
 */
16240
 
 
16241
 
/**
16242
 
 * Fired when a CellEditor input is saved.
16243
 
 *
16244
 
 * @event saveEvent
16245
 
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
16246
 
 * @param oArgs.newData {Object} New data value from form input field.
16247
 
 * @param oArgs.oldData {Object} Old data value.
16248
 
 */
16249
 
 
16250
 
/**
16251
 
 * Fired when a CellEditor input is canceled.
16252
 
 *
16253
 
 * @event cancelEvent
16254
 
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
16255
 
 */
16256
 
 
16257
 
/**
16258
 
 * Fired when a CellEditor has a blur event.
16259
 
 *
16260
 
 * @event blurEvent
16261
 
 * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
16262
 
 */
16263
 
 
16264
 
 
16265
 
 
16266
 
 
16267
 
 
16268
 
 
16269
 
 
16270
 
 
16271
 
 
16272
 
 
16273
 
 
16274
 
 
16275
 
 
16276
 
 
16277
 
/****************************************************************************/
16278
 
/****************************************************************************/
16279
 
/****************************************************************************/
16280
 
    
16281
 
/**
16282
 
 * The CheckboxCellEditor class provides functionality for inline editing
16283
 
 * DataTable cell data with checkboxes.
16284
 
 *
16285
 
 * @namespace YAHOO.widget
16286
 
 * @class CheckboxCellEditor
16287
 
 * @extends YAHOO.widget.BaseCellEditor
16288
 
 * @constructor
16289
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
16290
 
 */
16291
 
widget.CheckboxCellEditor = function(oConfigs) {
16292
 
    this._sId = "yui-checkboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16293
 
    widget.CheckboxCellEditor.superclass.constructor.call(this, "checkbox", oConfigs); 
16294
 
};
16295
 
 
16296
 
// CheckboxCellEditor extends BaseCellEditor
16297
 
lang.extend(widget.CheckboxCellEditor, BCE, {
16298
 
 
16299
 
/////////////////////////////////////////////////////////////////////////////
16300
 
//
16301
 
// CheckboxCellEditor public properties
16302
 
//
16303
 
/////////////////////////////////////////////////////////////////////////////
16304
 
/**
16305
 
 * Array of checkbox values. Can either be a simple array (e.g., ["red","green","blue"])
16306
 
 * or a an array of objects (e.g., [{label:"red", value:"#FF0000"},
16307
 
 * {label:"green", value:"#00FF00"}, {label:"blue", value:"#0000FF"}]). 
16308
 
 *
16309
 
 * @property checkboxOptions
16310
 
 * @type String[] | Object[]
16311
 
 */
16312
 
checkboxOptions : null,
16313
 
 
16314
 
/**
16315
 
 * Reference to the checkbox elements.
16316
 
 *
16317
 
 * @property checkboxes
16318
 
 * @type HTMLElement[] 
16319
 
 */
16320
 
checkboxes : null,
16321
 
 
16322
 
/**
16323
 
 * Array of checked values
16324
 
 *
16325
 
 * @property value
16326
 
 * @type String[] 
16327
 
 */
16328
 
value : null,
16329
 
 
16330
 
/////////////////////////////////////////////////////////////////////////////
16331
 
//
16332
 
// CheckboxCellEditor public methods
16333
 
//
16334
 
/////////////////////////////////////////////////////////////////////////////
16335
 
 
16336
 
/**
16337
 
 * Render a form with input(s) type=checkbox.
16338
 
 *
16339
 
 * @method renderForm
16340
 
 */
16341
 
renderForm : function() {
16342
 
    if(lang.isArray(this.checkboxOptions)) {
16343
 
        var checkboxOption, checkboxValue, checkboxId, elLabel, j, len;
16344
 
        
16345
 
        // Create the checkbox buttons in an IE-friendly way...
16346
 
        for(j=0,len=this.checkboxOptions.length; j<len; j++) {
16347
 
            checkboxOption = this.checkboxOptions[j];
16348
 
            checkboxValue = lang.isValue(checkboxOption.value) ?
16349
 
                    checkboxOption.value : checkboxOption;
16350
 
 
16351
 
            checkboxId = this.getId() + "-chk" + j;
16352
 
            this.getContainerEl().innerHTML += "<input type=\"checkbox\"" +
16353
 
                    " id=\"" + checkboxId + "\"" + // Needed for label
16354
 
                    " value=\"" + checkboxValue + "\" />";
16355
 
            
16356
 
            // Create the labels in an IE-friendly way
16357
 
            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
16358
 
            elLabel.htmlFor = checkboxId;
16359
 
            elLabel.innerHTML = lang.isValue(checkboxOption.label) ?
16360
 
                    checkboxOption.label : checkboxOption;
16361
 
        }
16362
 
        
16363
 
        // Store the reference to the checkbox elements
16364
 
        var allCheckboxes = [];
16365
 
        for(j=0; j<len; j++) {
16366
 
            allCheckboxes[allCheckboxes.length] = this.getContainerEl().childNodes[j*2];
16367
 
        }
16368
 
        this.checkboxes = allCheckboxes;
16369
 
 
16370
 
        if(this.disableBtns) {
16371
 
            this.handleDisabledBtns();
16372
 
        }
16373
 
    }
16374
 
    else {
16375
 
        YAHOO.log("Could not find checkboxOptions", "error", this.toString());
16376
 
    }
16377
 
},
16378
 
 
16379
 
/**
16380
 
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
16381
 
 * to save input without them. 
16382
 
 *
16383
 
 * @method handleDisabledBtns
16384
 
 */
16385
 
handleDisabledBtns : function() {
16386
 
    Ev.addListener(this.getContainerEl(), "click", function(v){
16387
 
        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
16388
 
            // Save on blur
16389
 
            this.save();
16390
 
        }
16391
 
    }, this, true);
16392
 
},
16393
 
 
16394
 
/**
16395
 
 * Resets CheckboxCellEditor UI to initial state.
16396
 
 *
16397
 
 * @method resetForm
16398
 
 */
16399
 
resetForm : function() {
16400
 
    // Normalize to array
16401
 
    var originalValues = lang.isArray(this.value) ? this.value : [this.value];
16402
 
    
16403
 
    // Match checks to value
16404
 
    for(var i=0, j=this.checkboxes.length; i<j; i++) {
16405
 
        this.checkboxes[i].checked = false;
16406
 
        for(var k=0, len=originalValues.length; k<len; k++) {
16407
 
            if(this.checkboxes[i].value === originalValues[k]) {
16408
 
                this.checkboxes[i].checked = true;
16409
 
            }
16410
 
        }
16411
 
    }
16412
 
},
16413
 
 
16414
 
/**
16415
 
 * Sets focus in CheckboxCellEditor.
16416
 
 *
16417
 
 * @method focus
16418
 
 */
16419
 
focus : function() {
16420
 
    this.checkboxes[0].focus();
16421
 
},
16422
 
 
16423
 
/**
16424
 
 * Retrieves input value from CheckboxCellEditor.
16425
 
 *
16426
 
 * @method getInputValue
16427
 
 */
16428
 
getInputValue : function() {
16429
 
    var checkedValues = [];
16430
 
    for(var i=0, j=this.checkboxes.length; i<j; i++) {
16431
 
        if(this.checkboxes[i].checked) {
16432
 
            checkedValues[checkedValues.length] = this.checkboxes[i].value;
16433
 
        }
16434
 
    }  
16435
 
    return checkedValues;
16436
 
}
16437
 
 
16438
 
});
16439
 
 
16440
 
// Copy static members to CheckboxCellEditor class
16441
 
lang.augmentObject(widget.CheckboxCellEditor, BCE);
16442
 
 
16443
 
 
16444
 
 
16445
 
 
16446
 
 
16447
 
 
16448
 
 
16449
 
 
16450
 
/****************************************************************************/
16451
 
/****************************************************************************/
16452
 
/****************************************************************************/
16453
 
    
16454
 
/**
16455
 
 * The DataCellEditor class provides functionality for inline editing
16456
 
 * DataTable cell data with a YUI Calendar.
16457
 
 *
16458
 
 * @namespace YAHOO.widget
16459
 
 * @class DateCellEditor
16460
 
 * @extends YAHOO.widget.BaseCellEditor 
16461
 
 * @constructor
16462
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
16463
 
 */
16464
 
widget.DateCellEditor = function(oConfigs) {
16465
 
    this._sId = "yui-dateceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16466
 
    widget.DateCellEditor.superclass.constructor.call(this, "date", oConfigs); 
16467
 
};
16468
 
 
16469
 
// CheckboxCellEditor extends BaseCellEditor
16470
 
lang.extend(widget.DateCellEditor, BCE, {
16471
 
 
16472
 
/////////////////////////////////////////////////////////////////////////////
16473
 
//
16474
 
// DateCellEditor public properties
16475
 
//
16476
 
/////////////////////////////////////////////////////////////////////////////
16477
 
/**
16478
 
 * Reference to Calendar instance.
16479
 
 *
16480
 
 * @property calendar
16481
 
 * @type YAHOO.widget.Calendar
16482
 
 */
16483
 
calendar : null,
16484
 
 
16485
 
/**
16486
 
 * Configs for the calendar instance, to be passed to Calendar constructor.
16487
 
 *
16488
 
 * @property calendarOptions
16489
 
 * @type Object
16490
 
 */
16491
 
calendarOptions : null,
16492
 
 
16493
 
/**
16494
 
 * Default value.
16495
 
 *
16496
 
 * @property defaultValue
16497
 
 * @type Date
16498
 
 * @default new Date()
16499
 
 */
16500
 
defaultValue : new Date(),
16501
 
 
16502
 
 
16503
 
/////////////////////////////////////////////////////////////////////////////
16504
 
//
16505
 
// DateCellEditor public methods
16506
 
//
16507
 
/////////////////////////////////////////////////////////////////////////////
16508
 
 
16509
 
/**
16510
 
 * Render a Calendar.
16511
 
 *
16512
 
 * @method renderForm
16513
 
 */
16514
 
renderForm : function() {
16515
 
    // Calendar widget
16516
 
    if(YAHOO.widget.Calendar) {
16517
 
        var calContainer = this.getContainerEl().appendChild(document.createElement("div"));
16518
 
        calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor
16519
 
        var calendar =
16520
 
                new YAHOO.widget.Calendar(this.getId() + "-date",
16521
 
                calContainer.id, this.calendarOptions);
16522
 
        calendar.render();
16523
 
        calContainer.style.cssFloat = "none";
16524
 
 
16525
 
        if(ua.ie) {
16526
 
            var calFloatClearer = this.getContainerEl().appendChild(document.createElement("div"));
16527
 
            calFloatClearer.style.clear = "both";
16528
 
        }
16529
 
        
16530
 
        this.calendar = calendar;
16531
 
 
16532
 
        if(this.disableBtns) {
16533
 
            this.handleDisabledBtns();
16534
 
        }
16535
 
    }
16536
 
    else {
16537
 
        YAHOO.log("Could not find YUI Calendar", "error", this.toString());
16538
 
    }
16539
 
    
16540
 
},
16541
 
 
16542
 
/**
16543
 
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
16544
 
 * to save input without them. 
16545
 
 *
16546
 
 * @method handleDisabledBtns
16547
 
 */
16548
 
handleDisabledBtns : function() {
16549
 
    this.calendar.selectEvent.subscribe(function(v){
16550
 
        // Save on select
16551
 
        this.save();
16552
 
    }, this, true);
16553
 
},
16554
 
 
16555
 
/**
16556
 
 * Resets DateCellEditor UI to initial state.
16557
 
 *
16558
 
 * @method resetForm
16559
 
 */
16560
 
resetForm : function() {
16561
 
    var value = this.value;
16562
 
    var selectedValue = (value.getMonth()+1)+"/"+value.getDate()+"/"+value.getFullYear();
16563
 
    this.calendar.cfg.setProperty("selected",selectedValue,false);
16564
 
        this.calendar.render();
16565
 
},
16566
 
 
16567
 
/**
16568
 
 * Sets focus in DateCellEditor.
16569
 
 *
16570
 
 * @method focus
16571
 
 */
16572
 
focus : function() {
16573
 
    // To be impmlemented by subclass
16574
 
},
16575
 
 
16576
 
/**
16577
 
 * Retrieves input value from DateCellEditor.
16578
 
 *
16579
 
 * @method getInputValue
16580
 
 */
16581
 
getInputValue : function() {
16582
 
    return this.calendar.getSelectedDates()[0];
16583
 
}
16584
 
 
16585
 
});
16586
 
 
16587
 
// Copy static members to DateCellEditor class
16588
 
lang.augmentObject(widget.DateCellEditor, BCE);
16589
 
 
16590
 
 
16591
 
 
16592
 
 
16593
 
 
16594
 
 
16595
 
 
16596
 
 
16597
 
 
16598
 
/****************************************************************************/
16599
 
/****************************************************************************/
16600
 
/****************************************************************************/
16601
 
    
16602
 
/**
16603
 
 * The DropdownCellEditor class provides functionality for inline editing
16604
 
 * DataTable cell data a SELECT element.
16605
 
 *
16606
 
 * @namespace YAHOO.widget
16607
 
 * @class DropdownCellEditor
16608
 
 * @extends YAHOO.widget.BaseCellEditor 
16609
 
 * @constructor
16610
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
16611
 
 */
16612
 
widget.DropdownCellEditor = function(oConfigs) {
16613
 
    this._sId = "yui-dropdownceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16614
 
    widget.DropdownCellEditor.superclass.constructor.call(this, "dropdown", oConfigs); 
16615
 
};
16616
 
 
16617
 
// DropdownCellEditor extends BaseCellEditor
16618
 
lang.extend(widget.DropdownCellEditor, BCE, {
16619
 
 
16620
 
/////////////////////////////////////////////////////////////////////////////
16621
 
//
16622
 
// DropdownCellEditor public properties
16623
 
//
16624
 
/////////////////////////////////////////////////////////////////////////////
16625
 
/**
16626
 
 * Array of dropdown values. Can either be a simple array (e.g., 
16627
 
 * ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g., 
16628
 
 * [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"},
16629
 
 * {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]). 
16630
 
 *
16631
 
 * @property dropdownOptions
16632
 
 * @type String[] | Object[]
16633
 
 */
16634
 
dropdownOptions : null,
16635
 
 
16636
 
/**
16637
 
 * Reference to Dropdown element.
16638
 
 *
16639
 
 * @property dropdown
16640
 
 * @type HTMLElement
16641
 
 */
16642
 
dropdown : null,
16643
 
 
16644
 
 
16645
 
/////////////////////////////////////////////////////////////////////////////
16646
 
//
16647
 
// DropdownCellEditor public methods
16648
 
//
16649
 
/////////////////////////////////////////////////////////////////////////////
16650
 
 
16651
 
/**
16652
 
 * Render a form with select element.
16653
 
 *
16654
 
 * @method renderForm
16655
 
 */
16656
 
renderForm : function() {
16657
 
    var elDropdown = this.getContainerEl().appendChild(document.createElement("select"));
16658
 
    elDropdown.style.zoom = 1;
16659
 
    this.dropdown = elDropdown;
16660
 
    
16661
 
    if(lang.isArray(this.dropdownOptions)) {
16662
 
        var dropdownOption, elOption;
16663
 
        for(var i=0, j=this.dropdownOptions.length; i<j; i++) {
16664
 
            dropdownOption = this.dropdownOptions[i];
16665
 
            elOption = document.createElement("option");
16666
 
            elOption.value = (lang.isValue(dropdownOption.value)) ?
16667
 
                    dropdownOption.value : dropdownOption;
16668
 
            elOption.innerHTML = (lang.isValue(dropdownOption.label)) ?
16669
 
                    dropdownOption.label : dropdownOption;
16670
 
            elOption = elDropdown.appendChild(elOption);
16671
 
        }
16672
 
        
16673
 
        if(this.disableBtns) {
16674
 
            this.handleDisabledBtns();
16675
 
        }
16676
 
    }
16677
 
},
16678
 
 
16679
 
/**
16680
 
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
16681
 
 * to save input without them. 
16682
 
 *
16683
 
 * @method handleDisabledBtns
16684
 
 */
16685
 
handleDisabledBtns : function() {
16686
 
    Ev.addListener(this.dropdown, "change", function(v){
16687
 
        // Save on change
16688
 
        this.save();
16689
 
    }, this, true);        
16690
 
},
16691
 
 
16692
 
/**
16693
 
 * Resets DropdownCellEditor UI to initial state.
16694
 
 *
16695
 
 * @method resetForm
16696
 
 */
16697
 
resetForm : function() {
16698
 
    for(var i=0, j=this.dropdown.options.length; i<j; i++) {
16699
 
        if(this.value === this.dropdown.options[i].value) {
16700
 
            this.dropdown.options[i].selected = true;
16701
 
        }
16702
 
    }    
16703
 
},
16704
 
 
16705
 
/**
16706
 
 * Sets focus in DropdownCellEditor.
16707
 
 *
16708
 
 * @method focus
16709
 
 */
16710
 
focus : function() {
16711
 
    this.getDataTable()._focusEl(this.dropdown);
16712
 
},
16713
 
 
16714
 
/**
16715
 
 * Retrieves input value from DropdownCellEditor.
16716
 
 *
16717
 
 * @method getInputValue
16718
 
 */
16719
 
getInputValue : function() {
16720
 
    return this.dropdown.options[this.dropdown.options.selectedIndex].value;
16721
 
}
16722
 
 
16723
 
});
16724
 
 
16725
 
// Copy static members to DropdownCellEditor class
16726
 
lang.augmentObject(widget.DropdownCellEditor, BCE);
16727
 
 
16728
 
 
16729
 
 
16730
 
 
16731
 
 
16732
 
 
16733
 
/****************************************************************************/
16734
 
/****************************************************************************/
16735
 
/****************************************************************************/
16736
 
    
16737
 
/**
16738
 
 * The RadioCellEditor class provides functionality for inline editing
16739
 
 * DataTable cell data with radio buttons.
16740
 
 *
16741
 
 * @namespace YAHOO.widget
16742
 
 * @class RadioCellEditor
16743
 
 * @extends YAHOO.widget.BaseCellEditor 
16744
 
 * @constructor
16745
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
16746
 
 */
16747
 
widget.RadioCellEditor = function(oConfigs) {
16748
 
    this._sId = "yui-radioceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16749
 
    widget.RadioCellEditor.superclass.constructor.call(this, "radio", oConfigs); 
16750
 
};
16751
 
 
16752
 
// RadioCellEditor extends BaseCellEditor
16753
 
lang.extend(widget.RadioCellEditor, BCE, {
16754
 
 
16755
 
/////////////////////////////////////////////////////////////////////////////
16756
 
//
16757
 
// RadioCellEditor public properties
16758
 
//
16759
 
/////////////////////////////////////////////////////////////////////////////
16760
 
/**
16761
 
 * Reference to radio elements.
16762
 
 *
16763
 
 * @property radios
16764
 
 * @type HTMLElement[]
16765
 
 */
16766
 
radios : null,
16767
 
 
16768
 
/**
16769
 
 * Array of radio values. Can either be a simple array (e.g., ["yes","no","maybe"])
16770
 
 * or a an array of objects (e.g., [{label:"yes", value:1}, {label:"no", value:-1},
16771
 
 * {label:"maybe", value:0}]). 
16772
 
 *
16773
 
 * @property radioOptions
16774
 
 * @type String[] | Object[]
16775
 
 */
16776
 
radioOptions : null,
16777
 
 
16778
 
/////////////////////////////////////////////////////////////////////////////
16779
 
//
16780
 
// RadioCellEditor public methods
16781
 
//
16782
 
/////////////////////////////////////////////////////////////////////////////
16783
 
 
16784
 
/**
16785
 
 * Render a form with input(s) type=radio.
16786
 
 *
16787
 
 * @method renderForm
16788
 
 */
16789
 
renderForm : function() {
16790
 
    if(lang.isArray(this.radioOptions)) {
16791
 
        var radioOption, radioValue, radioId, elLabel;
16792
 
        
16793
 
        // Create the radio buttons in an IE-friendly way
16794
 
        for(var i=0, len=this.radioOptions.length; i<len; i++) {
16795
 
            radioOption = this.radioOptions[i];
16796
 
            radioValue = lang.isValue(radioOption.value) ?
16797
 
                    radioOption.value : radioOption;
16798
 
            radioId = this.getId() + "-radio" + i;
16799
 
            this.getContainerEl().innerHTML += "<input type=\"radio\"" +
16800
 
                    " name=\"" + this.getId() + "\"" +
16801
 
                    " value=\"" + radioValue + "\"" +
16802
 
                    " id=\"" +  radioId + "\" />"; // Needed for label
16803
 
            
16804
 
            // Create the labels in an IE-friendly way
16805
 
            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
16806
 
            elLabel.htmlFor = radioId;
16807
 
            elLabel.innerHTML = (lang.isValue(radioOption.label)) ?
16808
 
                    radioOption.label : radioOption;
16809
 
        }
16810
 
        
16811
 
        // Store the reference to the checkbox elements
16812
 
        var allRadios = [],
16813
 
            elRadio;
16814
 
        for(var j=0; j<len; j++) {
16815
 
            elRadio = this.getContainerEl().childNodes[j*2];
16816
 
            allRadios[allRadios.length] = elRadio;
16817
 
        }
16818
 
        this.radios = allRadios;
16819
 
 
16820
 
        if(this.disableBtns) {
16821
 
            this.handleDisabledBtns();
16822
 
        }
16823
 
    }
16824
 
    else {
16825
 
        YAHOO.log("Could not find radioOptions", "error", this.toString());
16826
 
    }
16827
 
},
16828
 
 
16829
 
/**
16830
 
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
16831
 
 * to save input without them. 
16832
 
 *
16833
 
 * @method handleDisabledBtns
16834
 
 */
16835
 
handleDisabledBtns : function() {
16836
 
    Ev.addListener(this.getContainerEl(), "click", function(v){
16837
 
        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
16838
 
            // Save on blur
16839
 
            this.save();
16840
 
        }
16841
 
    }, this, true);
16842
 
},
16843
 
 
16844
 
/**
16845
 
 * Resets RadioCellEditor UI to initial state.
16846
 
 *
16847
 
 * @method resetForm
16848
 
 */
16849
 
resetForm : function() {
16850
 
    for(var i=0, j=this.radios.length; i<j; i++) {
16851
 
        var elRadio = this.radios[i];
16852
 
        if(this.value === elRadio.value) {
16853
 
            elRadio.checked = true;
16854
 
            return;
16855
 
        }
16856
 
    }
16857
 
},
16858
 
 
16859
 
/**
16860
 
 * Sets focus in RadioCellEditor.
16861
 
 *
16862
 
 * @method focus
16863
 
 */
16864
 
focus : function() {
16865
 
    for(var i=0, j=this.radios.length; i<j; i++) {
16866
 
        if(this.radios[i].checked) {
16867
 
            this.radios[i].focus();
16868
 
            return;
16869
 
        }
16870
 
    }
16871
 
},
16872
 
 
16873
 
/**
16874
 
 * Retrieves input value from RadioCellEditor.
16875
 
 *
16876
 
 * @method getInputValue
16877
 
 */
16878
 
getInputValue : function() {
16879
 
    for(var i=0, j=this.radios.length; i<j; i++) {
16880
 
        if(this.radios[i].checked) {
16881
 
            return this.radios[i].value;
16882
 
        }
16883
 
    }
16884
 
}
16885
 
 
16886
 
});
16887
 
 
16888
 
// Copy static members to RadioCellEditor class
16889
 
lang.augmentObject(widget.RadioCellEditor, BCE);
16890
 
 
16891
 
 
16892
 
 
16893
 
 
16894
 
 
16895
 
 
16896
 
/****************************************************************************/
16897
 
/****************************************************************************/
16898
 
/****************************************************************************/
16899
 
    
16900
 
/**
16901
 
 * The TextareaCellEditor class provides functionality for inline editing
16902
 
 * DataTable cell data with a TEXTAREA element.
16903
 
 *
16904
 
 * @namespace YAHOO.widget
16905
 
 * @class TextareaCellEditor
16906
 
 * @extends YAHOO.widget.BaseCellEditor 
16907
 
 * @constructor
16908
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
16909
 
 */
16910
 
widget.TextareaCellEditor = function(oConfigs) {
16911
 
    this._sId = "yui-textareaceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16912
 
    widget.TextareaCellEditor.superclass.constructor.call(this, "textarea", oConfigs); 
16913
 
};
16914
 
 
16915
 
// TextareaCellEditor extends BaseCellEditor
16916
 
lang.extend(widget.TextareaCellEditor, BCE, {
16917
 
 
16918
 
/////////////////////////////////////////////////////////////////////////////
16919
 
//
16920
 
// TextareaCellEditor public properties
16921
 
//
16922
 
/////////////////////////////////////////////////////////////////////////////
16923
 
/**
16924
 
 * Reference to textarea element.
16925
 
 *
16926
 
 * @property textarea
16927
 
 * @type HTMLElement
16928
 
 */
16929
 
textarea : null,
16930
 
 
16931
 
 
16932
 
/////////////////////////////////////////////////////////////////////////////
16933
 
//
16934
 
// TextareaCellEditor public methods
16935
 
//
16936
 
/////////////////////////////////////////////////////////////////////////////
16937
 
 
16938
 
/**
16939
 
 * Render a form with textarea.
16940
 
 *
16941
 
 * @method renderForm
16942
 
 */
16943
 
renderForm : function() {
16944
 
    var elTextarea = this.getContainerEl().appendChild(document.createElement("textarea"));
16945
 
    this.textarea = elTextarea;
16946
 
 
16947
 
    if(this.disableBtns) {
16948
 
        this.handleDisabledBtns();
16949
 
    }
16950
 
},
16951
 
 
16952
 
/**
16953
 
 * After rendering form, if disabledBtns is set to true, then sets up a mechanism
16954
 
 * to save input without them. 
16955
 
 *
16956
 
 * @method handleDisabledBtns
16957
 
 */
16958
 
handleDisabledBtns : function() {
16959
 
    Ev.addListener(this.textarea, "blur", function(v){
16960
 
        // Save on blur
16961
 
        this.save();
16962
 
    }, this, true);        
16963
 
},
16964
 
 
16965
 
/**
16966
 
 * Moves TextareaCellEditor UI to a cell.
16967
 
 *
16968
 
 * @method move
16969
 
 */
16970
 
move : function() {
16971
 
    this.textarea.style.width = this.getTdEl().offsetWidth + "px";
16972
 
    this.textarea.style.height = "3em";
16973
 
    YAHOO.widget.TextareaCellEditor.superclass.move.call(this);
16974
 
},
16975
 
 
16976
 
/**
16977
 
 * Resets TextareaCellEditor UI to initial state.
16978
 
 *
16979
 
 * @method resetForm
16980
 
 */
16981
 
resetForm : function() {
16982
 
    this.textarea.value = this.value;
16983
 
},
16984
 
 
16985
 
/**
16986
 
 * Sets focus in TextareaCellEditor.
16987
 
 *
16988
 
 * @method focus
16989
 
 */
16990
 
focus : function() {
16991
 
    // Bug 2303181, Bug 2263600
16992
 
    this.getDataTable()._focusEl(this.textarea);
16993
 
    this.textarea.select();
16994
 
},
16995
 
 
16996
 
/**
16997
 
 * Retrieves input value from TextareaCellEditor.
16998
 
 *
16999
 
 * @method getInputValue
17000
 
 */
17001
 
getInputValue : function() {
17002
 
    return this.textarea.value;
17003
 
}
17004
 
 
17005
 
});
17006
 
 
17007
 
// Copy static members to TextareaCellEditor class
17008
 
lang.augmentObject(widget.TextareaCellEditor, BCE);
17009
 
 
17010
 
 
17011
 
 
17012
 
 
17013
 
 
17014
 
 
17015
 
 
17016
 
 
17017
 
 
17018
 
/****************************************************************************/
17019
 
/****************************************************************************/
17020
 
/****************************************************************************/
17021
 
    
17022
 
/**
17023
 
 * The TextboxCellEditor class provides functionality for inline editing
17024
 
 * DataTable cell data with an INPUT TYPE=TEXT element.
17025
 
 *
17026
 
 * @namespace YAHOO.widget
17027
 
 * @class TextboxCellEditor
17028
 
 * @extends YAHOO.widget.BaseCellEditor 
17029
 
 * @constructor
17030
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
17031
 
 */
17032
 
widget.TextboxCellEditor = function(oConfigs) {
17033
 
    this._sId = "yui-textboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
17034
 
    widget.TextboxCellEditor.superclass.constructor.call(this, "textbox", oConfigs); 
17035
 
};
17036
 
 
17037
 
// TextboxCellEditor extends BaseCellEditor
17038
 
lang.extend(widget.TextboxCellEditor, BCE, {
17039
 
 
17040
 
/////////////////////////////////////////////////////////////////////////////
17041
 
//
17042
 
// TextboxCellEditor public properties
17043
 
//
17044
 
/////////////////////////////////////////////////////////////////////////////
17045
 
/**
17046
 
 * Reference to the textbox element.
17047
 
 *
17048
 
 * @property textbox
17049
 
 */
17050
 
textbox : null,
17051
 
 
17052
 
/////////////////////////////////////////////////////////////////////////////
17053
 
//
17054
 
// TextboxCellEditor public methods
17055
 
//
17056
 
/////////////////////////////////////////////////////////////////////////////
17057
 
 
17058
 
/**
17059
 
 * Render a form with input type=text.
17060
 
 *
17061
 
 * @method renderForm
17062
 
 */
17063
 
renderForm : function() {
17064
 
    var elTextbox;
17065
 
    // Bug 1802582: SF3/Mac needs a form element wrapping the input
17066
 
    if(ua.webkit>420) {
17067
 
        elTextbox = this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input"));
17068
 
    }
17069
 
    else {
17070
 
        elTextbox = this.getContainerEl().appendChild(document.createElement("input"));
17071
 
    }
17072
 
    elTextbox.type = "text";
17073
 
    this.textbox = elTextbox;
17074
 
 
17075
 
    // Save on enter by default
17076
 
    // Bug: 1802582 Set up a listener on each textbox to track on keypress
17077
 
    // since SF/OP can't preventDefault on keydown
17078
 
    Ev.addListener(elTextbox, "keypress", function(v){
17079
 
        if((v.keyCode === 13)) {
17080
 
            // Prevent form submit
17081
 
            YAHOO.util.Event.preventDefault(v);
17082
 
            this.save();
17083
 
        }
17084
 
    }, this, true);
17085
 
 
17086
 
    if(this.disableBtns) {
17087
 
        // By default this is no-op since enter saves by default
17088
 
        this.handleDisabledBtns();
17089
 
    }
17090
 
},
17091
 
 
17092
 
/**
17093
 
 * Moves TextboxCellEditor UI to a cell.
17094
 
 *
17095
 
 * @method move
17096
 
 */
17097
 
move : function() {
17098
 
    this.textbox.style.width = this.getTdEl().offsetWidth + "px";
17099
 
    widget.TextboxCellEditor.superclass.move.call(this);
17100
 
},
17101
 
 
17102
 
/**
17103
 
 * Resets TextboxCellEditor UI to initial state.
17104
 
 *
17105
 
 * @method resetForm
17106
 
 */
17107
 
resetForm : function() {
17108
 
    this.textbox.value = lang.isValue(this.value) ? this.value.toString() : "";
17109
 
},
17110
 
 
17111
 
/**
17112
 
 * Sets focus in TextboxCellEditor.
17113
 
 *
17114
 
 * @method focus
17115
 
 */
17116
 
focus : function() {
17117
 
    // Bug 2303181, Bug 2263600
17118
 
    this.getDataTable()._focusEl(this.textbox);
17119
 
    this.textbox.select();
17120
 
},
17121
 
 
17122
 
/**
17123
 
 * Returns new value for TextboxCellEditor.
17124
 
 *
17125
 
 * @method getInputValue
17126
 
 */
17127
 
getInputValue : function() {
17128
 
    return this.textbox.value;
17129
 
}
17130
 
 
17131
 
});
17132
 
 
17133
 
// Copy static members to TextboxCellEditor class
17134
 
lang.augmentObject(widget.TextboxCellEditor, BCE);
17135
 
 
17136
 
 
17137
 
 
17138
 
 
17139
 
 
17140
 
 
17141
 
 
17142
 
/////////////////////////////////////////////////////////////////////////////
17143
 
//
17144
 
// DataTable extension
17145
 
//
17146
 
/////////////////////////////////////////////////////////////////////////////
17147
 
 
17148
 
/**
17149
 
 * CellEditor subclasses.
17150
 
 * @property DataTable.Editors
17151
 
 * @type Object
17152
 
 * @static
17153
 
 */
17154
 
DT.Editors = {
17155
 
    checkbox : widget.CheckboxCellEditor,
17156
 
    "date"   : widget.DateCellEditor,
17157
 
    dropdown : widget.DropdownCellEditor,
17158
 
    radio    : widget.RadioCellEditor,
17159
 
    textarea : widget.TextareaCellEditor,
17160
 
    textbox  : widget.TextboxCellEditor
17161
 
};
17162
 
 
17163
 
/****************************************************************************/
17164
 
/****************************************************************************/
17165
 
/****************************************************************************/
17166
 
    
17167
 
/**
17168
 
 * Factory class for instantiating a BaseCellEditor subclass.
17169
 
 *
17170
 
 * @namespace YAHOO.widget
17171
 
 * @class CellEditor
17172
 
 * @extends YAHOO.widget.BaseCellEditor 
17173
 
 * @constructor
17174
 
 * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
17175
 
 * @param oConfigs {Object} (Optional) Object literal of configs.
17176
 
 */
17177
 
widget.CellEditor = function(sType, oConfigs) {
17178
 
    // Point to one of the subclasses
17179
 
    if(sType && DT.Editors[sType]) {
17180
 
        lang.augmentObject(BCE, DT.Editors[sType]);
17181
 
        return new DT.Editors[sType](oConfigs);
17182
 
    }
17183
 
    else {
17184
 
        return new BCE(null, oConfigs);
17185
 
    }
17186
 
};
17187
 
 
17188
 
var CE = widget.CellEditor;
17189
 
 
17190
 
// Copy static members to CellEditor class
17191
 
lang.augmentObject(CE, BCE);
17192
 
 
17193
 
 
17194
 
})();
17195
 
 
17196
 
YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.7.0", build: "1799"});