~smagoun/whoopsie/whoopsie-lp1017637

« back to all changes in this revision

Viewing changes to backend/stats/static/js/yui/build/datatable-base-deprecated/datatable-base-deprecated.js

  • Committer: Evan Dandrea
  • Date: 2012-05-09 05:53:45 UTC
  • Revision ID: evan.dandrea@canonical.com-20120509055345-z2j41tmcbf4as5uf
The backend now lives in lp:daisy and the website (errors.ubuntu.com) now lives in lp:errors.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
YUI 3.5.0 (build 5089)
3
 
Copyright 2012 Yahoo! Inc. All rights reserved.
4
 
Licensed under the BSD License.
5
 
http://yuilibrary.com/license/
6
 
*/
7
 
YUI.add('datatable-base-deprecated', function(Y) {
8
 
 
9
 
var YLang = Y.Lang,
10
 
    YisValue = YLang.isValue,
11
 
    fromTemplate = Y.Lang.sub,
12
 
    YNode = Y.Node,
13
 
    Ycreate = YNode.create,
14
 
    YgetClassName = Y.ClassNameManager.getClassName,
15
 
 
16
 
    DATATABLE = "datatable",
17
 
    COLUMN = "column",
18
 
    
19
 
    FOCUS = "focus",
20
 
    KEYDOWN = "keydown",
21
 
    MOUSEENTER = "mouseenter",
22
 
    MOUSELEAVE = "mouseleave",
23
 
    MOUSEUP = "mouseup",
24
 
    MOUSEDOWN = "mousedown",
25
 
    CLICK = "click",
26
 
    DBLCLICK = "dblclick",
27
 
 
28
 
    CLASS_COLUMNS = YgetClassName(DATATABLE, "columns"),
29
 
    CLASS_DATA = YgetClassName(DATATABLE, "data"),
30
 
    CLASS_MSG = YgetClassName(DATATABLE, "msg"),
31
 
    CLASS_LINER = YgetClassName(DATATABLE, "liner"),
32
 
    CLASS_FIRST = YgetClassName(DATATABLE, "first"),
33
 
    CLASS_LAST = YgetClassName(DATATABLE, "last"),
34
 
    CLASS_EVEN = YgetClassName(DATATABLE, "even"),
35
 
    CLASS_ODD = YgetClassName(DATATABLE, "odd"),
36
 
 
37
 
    TEMPLATE_TABLE = '<table></table>',
38
 
    TEMPLATE_COL = '<col></col>',
39
 
    TEMPLATE_THEAD = '<thead class="'+CLASS_COLUMNS+'"></thead>',
40
 
    TEMPLATE_TBODY = '<tbody class="'+CLASS_DATA+'"></tbody>',
41
 
    TEMPLATE_TH = '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}" abbr="{abbr}"><div class="'+CLASS_LINER+'">{value}</div></th>',
42
 
    TEMPLATE_TR = '<tr id="{id}"></tr>',
43
 
    TEMPLATE_TD = '<td headers="{headers}" class="{classnames}"><div class="'+CLASS_LINER+'">{value}</div></td>',
44
 
    TEMPLATE_VALUE = '{value}',
45
 
    TEMPLATE_MSG = '<tbody class="'+CLASS_MSG+'"></tbody>';
46
 
    
47
 
 
48
 
 
49
 
/**
50
 
 * The Column class defines and manages attributes of Columns for DataTable.
51
 
 *
52
 
 * @class Column
53
 
 * @extends Widget
54
 
 * @constructor
55
 
 */
56
 
function Column(config) {
57
 
    Column.superclass.constructor.apply(this, arguments);
58
 
}
59
 
 
60
 
/////////////////////////////////////////////////////////////////////////////
61
 
//
62
 
// STATIC PROPERTIES
63
 
//
64
 
/////////////////////////////////////////////////////////////////////////////
65
 
Y.mix(Column, {
66
 
    /**
67
 
     * Class name.
68
 
     *
69
 
     * @property NAME
70
 
     * @type {String}
71
 
     * @static
72
 
     * @final
73
 
     * @value "column"
74
 
     */
75
 
    NAME: "column",
76
 
 
77
 
/////////////////////////////////////////////////////////////////////////////
78
 
//
79
 
// ATTRIBUTES
80
 
//
81
 
/////////////////////////////////////////////////////////////////////////////
82
 
    ATTRS: {
83
 
        /**
84
 
        Unique internal identifier, used to stamp ID on TH element.
85
 
        
86
 
        @attribute id
87
 
        @type {String}
88
 
        @readOnly
89
 
        **/
90
 
        id: {
91
 
            valueFn: "_defaultId",
92
 
            readOnly: true
93
 
        },
94
 
        
95
 
        /**
96
 
        User-supplied identifier. Defaults to id.
97
 
        @attribute key
98
 
        @type {String}
99
 
        **/
100
 
        key: {
101
 
            valueFn: "_defaultKey"
102
 
        },
103
 
 
104
 
        /**
105
 
        Points to underlying data field (for sorting or formatting, for
106
 
        example). Useful when column doesn't hold any data itself, but is just
107
 
        a visual representation of data from another column or record field.
108
 
        Defaults to key.
109
 
 
110
 
        @attribute field
111
 
        @type {String}
112
 
        @default (column key)
113
 
        **/
114
 
        field: {
115
 
            valueFn: "_defaultField"
116
 
        },
117
 
 
118
 
        /**
119
 
        Display label for column header. Defaults to key.
120
 
 
121
 
        @attribute label
122
 
        @type {String}
123
 
        **/
124
 
        label: {
125
 
            valueFn: "_defaultLabel"
126
 
        },
127
 
        
128
 
        /**
129
 
        Array of child column definitions (for nested headers).
130
 
 
131
 
        @attribute children
132
 
        @type {String}
133
 
        @default null
134
 
        **/
135
 
        children: {
136
 
            value: null
137
 
        },
138
 
        
139
 
        /**
140
 
        TH abbr attribute.
141
 
 
142
 
        @attribute abbr
143
 
        @type {String}
144
 
        @default ""
145
 
        **/
146
 
        abbr: {
147
 
            value: ""
148
 
        },
149
 
 
150
 
        //TODO: support custom classnames
151
 
        // TH CSS classnames
152
 
        classnames: {
153
 
            readOnly: true,
154
 
            getter: "_getClassnames"
155
 
        },
156
 
        
157
 
        /**
158
 
        Formating template string or function for cells in this column.
159
 
 
160
 
        Function formatters receive a single object (described below) and are
161
 
        expected to output the `innerHTML` of the cell.
162
 
 
163
 
        String templates can include markup and {placeholder} tokens to be
164
 
        filled in from the object passed to function formatters.
165
 
 
166
 
        @attribute formatter
167
 
        @type {String|Function}
168
 
        @param {Object} data Data relevant to the rendering of this cell
169
 
            @param {String} data.classnames CSS classes to add to the cell
170
 
            @param {Column} data.column This Column instance
171
 
            @param {Object} data.data The raw object data from the Record
172
 
            @param {String} data.field This Column's "field" attribute value
173
 
            @param {String} data.headers TH ids to reference in the cell's
174
 
                            "headers" attribute
175
 
            @param {Record} data.record The Record instance for this row
176
 
            @param {Number} data.rowindex The index for this row
177
 
            @param {Node}   data.tbody The TBODY Node that will house the cell
178
 
            @param {Node}   data.tr The row TR Node that will house the cell
179
 
            @param {Any}    data.value The raw Record data for this cell
180
 
        **/
181
 
        formatter: {},
182
 
 
183
 
        /**
184
 
        The default markup to display in cells that have no corresponding record
185
 
        data or content from formatters.
186
 
 
187
 
        @attribute emptyCellValue
188
 
        @type {String}
189
 
        @default ''
190
 
        **/
191
 
        emptyCellValue: {
192
 
            value: '',
193
 
            validator: Y.Lang.isString
194
 
        },
195
 
 
196
 
        //requires datatable-sort
197
 
        sortable: {
198
 
            value: false
199
 
        },
200
 
        //sortOptions:defaultDir, sortFn, field
201
 
 
202
 
        //TODO: support editable columns
203
 
        // Column editor
204
 
        editor: {},
205
 
 
206
 
        //TODO: support resizeable columns
207
 
        //TODO: support setting widths
208
 
        // requires datatable-colresize
209
 
        width: {},
210
 
        resizeable: {},
211
 
        minimized: {},
212
 
        minWidth: {},
213
 
        maxAutoWidth: {}
214
 
    }
215
 
});
216
 
 
217
 
/////////////////////////////////////////////////////////////////////////////
218
 
//
219
 
// PROTOTYPE
220
 
//
221
 
/////////////////////////////////////////////////////////////////////////////
222
 
Y.extend(Column, Y.Widget, {
223
 
    /////////////////////////////////////////////////////////////////////////////
224
 
    //
225
 
    // ATTRIBUTE HELPERS
226
 
    //
227
 
    /////////////////////////////////////////////////////////////////////////////
228
 
    /**
229
 
    * Return ID for instance.
230
 
    *
231
 
    * @method _defaultId
232
 
    * @return {String}
233
 
    * @private
234
 
    */
235
 
    _defaultId: function() {
236
 
        return Y.guid();
237
 
    },
238
 
 
239
 
    /**
240
 
    * Return key for instance. Defaults to ID if one was not provided.
241
 
    *
242
 
    * @method _defaultKey
243
 
    * @return {String}
244
 
    * @private
245
 
    */
246
 
    _defaultKey: function() {
247
 
        return Y.guid();
248
 
    },
249
 
 
250
 
    /**
251
 
    * Return field for instance. Defaults to key if one was not provided.
252
 
    *
253
 
    * @method _defaultField
254
 
    * @return {String}
255
 
    * @private
256
 
    */
257
 
    _defaultField: function() {
258
 
        return this.get("key");
259
 
    },
260
 
 
261
 
    /**
262
 
    * Return label for instance. Defaults to key if one was not provided.
263
 
    *
264
 
    * @method _defaultLabel
265
 
    * @return {String}
266
 
    * @private
267
 
    */
268
 
    _defaultLabel: function() {
269
 
        return this.get("key");
270
 
    },
271
 
 
272
 
    /**
273
 
     * Updates the UI if changes are made to abbr.
274
 
     *
275
 
     * @method _afterAbbrChange
276
 
     * @param e {Event} Custom event for the attribute change.
277
 
     * @private
278
 
     */
279
 
    _afterAbbrChange: function (e) {
280
 
        this._uiSetAbbr(e.newVal);
281
 
    },
282
 
 
283
 
    /////////////////////////////////////////////////////////////////////////////
284
 
    //
285
 
    // PROPERTIES
286
 
    //
287
 
    /////////////////////////////////////////////////////////////////////////////
288
 
    /**
289
 
     * Reference to Column's current position index within its Columnset's keys
290
 
     * array, if applicable. This property only applies to non-nested and bottom-
291
 
     * level child Columns. Value is set by Columnset code.
292
 
     *
293
 
     * @property keyIndex
294
 
     * @type {Number}
295
 
     */
296
 
    keyIndex: null,
297
 
    
298
 
    /**
299
 
    * Array of TH IDs associated with this column, for TD "headers" attribute.
300
 
    * Value is set by Columnset code
301
 
    *
302
 
    * @property headers
303
 
    * @type {String[]}
304
 
    */
305
 
    headers: null,
306
 
 
307
 
    /**
308
 
     * Number of cells the header spans. Value is set by Columnset code.
309
 
     *
310
 
     * @property colSpan
311
 
     * @type {Number}
312
 
     * @default 1
313
 
     */
314
 
    colSpan: 1,
315
 
    
316
 
    /**
317
 
     * Number of rows the header spans. Value is set by Columnset code.
318
 
     *
319
 
     * @property rowSpan
320
 
     * @type {Number}
321
 
     * @default 1
322
 
     */
323
 
    rowSpan: 1,
324
 
 
325
 
    /**
326
 
     * Column's parent Column instance, if applicable. Value is set by Columnset
327
 
     * code.
328
 
     *
329
 
     * @property parent
330
 
     * @type {Column}
331
 
     */
332
 
    parent: null,
333
 
 
334
 
    /**
335
 
     * The Node reference to the associated TH element.
336
 
     *
337
 
     * @property thNode
338
 
     * @type {Node}
339
 
     */
340
 
     
341
 
    thNode: null,
342
 
 
343
 
    /*TODO
344
 
     * The Node reference to the associated liner element.
345
 
     *
346
 
     * @property thLinerNode
347
 
     * @type {Node}
348
 
     
349
 
    thLinerNode: null,*/
350
 
    
351
 
    /////////////////////////////////////////////////////////////////////////////
352
 
    //
353
 
    // METHODS
354
 
    //
355
 
    /////////////////////////////////////////////////////////////////////////////
356
 
    /**
357
 
    * Initializer.
358
 
    *
359
 
    * @method initializer
360
 
    * @param config {Object} Config object.
361
 
    * @private
362
 
    */
363
 
    initializer: function(config) {
364
 
    },
365
 
 
366
 
    /**
367
 
    * Destructor.
368
 
    *
369
 
    * @method destructor
370
 
    * @private
371
 
    */
372
 
    destructor: function() {
373
 
    },
374
 
 
375
 
    /**
376
 
     * Returns classnames for Column.
377
 
     *
378
 
     * @method _getClassnames
379
 
     * @private
380
 
     */
381
 
    _getClassnames: function () {
382
 
        return Y.ClassNameManager.getClassName(COLUMN, this.get("key").replace(/[^\w\-]/g,""));
383
 
    },
384
 
 
385
 
    ////////////////////////////////////////////////////////////////////////////
386
 
    //
387
 
    // SYNC
388
 
    //
389
 
    ////////////////////////////////////////////////////////////////////////////
390
 
    /**
391
 
    * Syncs UI to intial state.
392
 
    *
393
 
    * @method syncUI
394
 
    * @private
395
 
    */
396
 
    syncUI: function() {
397
 
        this._uiSetAbbr(this.get("abbr"));
398
 
    },
399
 
 
400
 
    /**
401
 
     * Updates abbr.
402
 
     *
403
 
     * @method _uiSetAbbr
404
 
     * @param val {String} New abbr.
405
 
     * @protected
406
 
     */
407
 
    _uiSetAbbr: function(val) {
408
 
        this.thNode.set("abbr", val);
409
 
    }
410
 
});
411
 
 
412
 
Y.Column = Column;
413
 
/**
414
 
 * The Columnset class defines and manages a collection of Columns.
415
 
 *
416
 
 * @class Columnset
417
 
 * @extends Base
418
 
 * @constructor
419
 
 */
420
 
function Columnset(config) {
421
 
    Columnset.superclass.constructor.apply(this, arguments);
422
 
}
423
 
 
424
 
/////////////////////////////////////////////////////////////////////////////
425
 
//
426
 
// STATIC PROPERTIES
427
 
//
428
 
/////////////////////////////////////////////////////////////////////////////
429
 
Y.mix(Columnset, {
430
 
    /**
431
 
     * Class name.
432
 
     *
433
 
     * @property NAME
434
 
     * @type String
435
 
     * @static
436
 
     * @final
437
 
     * @value "columnset"
438
 
     */
439
 
    NAME: "columnset",
440
 
 
441
 
    /////////////////////////////////////////////////////////////////////////////
442
 
    //
443
 
    // ATTRIBUTES
444
 
    //
445
 
    /////////////////////////////////////////////////////////////////////////////
446
 
    ATTRS: {
447
 
        /**
448
 
        * @attribute definitions
449
 
        * @description Array of column definitions that will populate this Columnset.
450
 
        * @type Array
451
 
        */
452
 
        definitions: {
453
 
            setter: "_setDefinitions"
454
 
        }
455
 
 
456
 
    }
457
 
});
458
 
 
459
 
/////////////////////////////////////////////////////////////////////////////
460
 
//
461
 
// PROTOTYPE
462
 
//
463
 
/////////////////////////////////////////////////////////////////////////////
464
 
Y.extend(Columnset, Y.Base, {
465
 
    /////////////////////////////////////////////////////////////////////////////
466
 
    //
467
 
    // ATTRIBUTE HELPERS
468
 
    //
469
 
    /////////////////////////////////////////////////////////////////////////////
470
 
    /**
471
 
    * @method _setDefinitions
472
 
    * @description Clones definitions before setting.
473
 
    * @param definitions {Array} Array of column definitions.
474
 
    * @return Array
475
 
    * @private
476
 
    */
477
 
    _setDefinitions: function(definitions) {
478
 
            return Y.clone(definitions);
479
 
    },
480
 
    
481
 
    /////////////////////////////////////////////////////////////////////////////
482
 
    //
483
 
    // PROPERTIES
484
 
    //
485
 
    /////////////////////////////////////////////////////////////////////////////
486
 
    /**
487
 
     * Top-down tree representation of Column hierarchy. Used to create DOM
488
 
     * elements.
489
 
     *
490
 
     * @property tree
491
 
     * @type {Column[]}
492
 
     */
493
 
    tree: null,
494
 
 
495
 
    /**
496
 
     * Hash of all Columns by ID.
497
 
     *
498
 
     * @property idHash
499
 
     * @type Object
500
 
     */
501
 
    idHash: null,
502
 
 
503
 
    /**
504
 
     * Hash of all Columns by key.
505
 
     *
506
 
     * @property keyHash
507
 
     * @type Object
508
 
     */
509
 
    keyHash: null,
510
 
 
511
 
    /**
512
 
     * Array of only Columns that are meant to be displayed in DOM.
513
 
     *
514
 
     * @property keys
515
 
     * @type {Column[]}
516
 
     */
517
 
    keys: null,
518
 
 
519
 
    /////////////////////////////////////////////////////////////////////////////
520
 
    //
521
 
    // METHODS
522
 
    //
523
 
    /////////////////////////////////////////////////////////////////////////////
524
 
    /**
525
 
    * Initializer. Generates all internal representations of the collection of
526
 
    * Columns.
527
 
    *
528
 
    * @method initializer
529
 
    * @param config {Object} Config object.
530
 
    * @private
531
 
    */
532
 
    initializer: function() {
533
 
 
534
 
        // DOM tree representation of all Columns
535
 
        var tree = [],
536
 
        // Hash of all Columns by ID
537
 
        idHash = {},
538
 
        // Hash of all Columns by key
539
 
        keyHash = {},
540
 
        // Flat representation of only Columns that are meant to display data
541
 
        keys = [],
542
 
        // Original definitions
543
 
        definitions = this.get("definitions"),
544
 
 
545
 
        self = this;
546
 
 
547
 
        // Internal recursive function to define Column instances
548
 
        function parseColumns(depth, currentDefinitions, parent) {
549
 
            var i=0,
550
 
                len = currentDefinitions.length,
551
 
                currentDefinition,
552
 
                column,
553
 
                currentChildren;
554
 
 
555
 
            // One level down
556
 
            depth++;
557
 
 
558
 
            // Create corresponding dom node if not already there for this depth
559
 
            if(!tree[depth]) {
560
 
                tree[depth] = [];
561
 
            }
562
 
 
563
 
            // Parse each node at this depth for attributes and any children
564
 
            for(; i<len; ++i) {
565
 
                currentDefinition = currentDefinitions[i];
566
 
 
567
 
                currentDefinition = YLang.isString(currentDefinition) ? {key:currentDefinition} : currentDefinition;
568
 
 
569
 
                // Instantiate a new Column for each node
570
 
                column = new Y.Column(currentDefinition);
571
 
 
572
 
                // Cross-reference Column ID back to the original object literal definition
573
 
                currentDefinition.yuiColumnId = column.get("id");
574
 
 
575
 
                // Add the new Column to the hash
576
 
                idHash[column.get("id")] = column;
577
 
                keyHash[column.get("key")] = column;
578
 
 
579
 
                // Assign its parent as an attribute, if applicable
580
 
                if(parent) {
581
 
                    column.parent = parent;
582
 
                }
583
 
 
584
 
                // The Column has descendants
585
 
                if(YLang.isArray(currentDefinition.children)) {
586
 
                    currentChildren = currentDefinition.children;
587
 
                    column._set("children", currentChildren);
588
 
 
589
 
                    self._setColSpans(column, currentDefinition);
590
 
 
591
 
                    self._cascadePropertiesToChildren(column, currentChildren);
592
 
 
593
 
                    // The children themselves must also be parsed for Column instances
594
 
                    if(!tree[depth+1]) {
595
 
                        tree[depth+1] = [];
596
 
                    }
597
 
                    parseColumns(depth, currentChildren, column);
598
 
                }
599
 
                // This Column does not have any children
600
 
                else {
601
 
                    column.keyIndex = keys.length;
602
 
                    // Default is already 1
603
 
                    //column.colSpan = 1;
604
 
                    keys.push(column);
605
 
                }
606
 
 
607
 
                // Add the Column to the top-down dom tree
608
 
                tree[depth].push(column);
609
 
            }
610
 
            depth--;
611
 
        }
612
 
 
613
 
        // Parse out Column instances from the array of object literals
614
 
        parseColumns(-1, definitions);
615
 
 
616
 
 
617
 
        // Save to the Columnset instance
618
 
        this.tree = tree;
619
 
        this.idHash = idHash;
620
 
        this.keyHash = keyHash;
621
 
        this.keys = keys;
622
 
 
623
 
        this._setRowSpans();
624
 
        this._setHeaders();
625
 
    },
626
 
 
627
 
    /**
628
 
    * Destructor.
629
 
    *
630
 
    * @method destructor
631
 
    * @private
632
 
    */
633
 
    destructor: function() {
634
 
    },
635
 
 
636
 
    /////////////////////////////////////////////////////////////////////////////
637
 
    //
638
 
    // COLUMN HELPERS
639
 
    //
640
 
    /////////////////////////////////////////////////////////////////////////////
641
 
    /**
642
 
    * Cascade certain properties to children if not defined on their own.
643
 
    *
644
 
    * @method _cascadePropertiesToChildren
645
 
    * @private
646
 
    */
647
 
    _cascadePropertiesToChildren: function(column, currentChildren) {
648
 
        //TODO: this is all a giant todo
649
 
        var i = 0,
650
 
            len = currentChildren.length,
651
 
            child;
652
 
 
653
 
        // Cascade certain properties to children if not defined on their own
654
 
        for(; i<len; ++i) {
655
 
            child = currentChildren[i];
656
 
            if(column.get("className") && (child.className === undefined)) {
657
 
                child.className = column.get("className");
658
 
            }
659
 
            if(column.get("editor") && (child.editor === undefined)) {
660
 
                child.editor = column.get("editor");
661
 
            }
662
 
            if(column.get("formatter") && (child.formatter === undefined)) {
663
 
                child.formatter = column.get("formatter");
664
 
            }
665
 
            if(column.get("resizeable") && (child.resizeable === undefined)) {
666
 
                child.resizeable = column.get("resizeable");
667
 
            }
668
 
            if(column.get("sortable") && (child.sortable === undefined)) {
669
 
                child.sortable = column.get("sortable");
670
 
            }
671
 
            if(column.get("hidden")) {
672
 
                child.hidden = true;
673
 
            }
674
 
            if(column.get("width") && (child.width === undefined)) {
675
 
                child.width = column.get("width");
676
 
            }
677
 
            if(column.get("minWidth") && (child.minWidth === undefined)) {
678
 
                child.minWidth = column.get("minWidth");
679
 
            }
680
 
            if(column.get("maxAutoWidth") && (child.maxAutoWidth === undefined)) {
681
 
                child.maxAutoWidth = column.get("maxAutoWidth");
682
 
            }
683
 
        }
684
 
    },
685
 
 
686
 
    /**
687
 
    * @method _setColSpans
688
 
    * @description Calculates and sets colSpan attribute on given Column.
689
 
    * @param column {Array} Column instance.
690
 
    * @param definition {Object} Column definition.
691
 
    * @private
692
 
    */
693
 
    _setColSpans: function(column, definition) {
694
 
        // Determine COLSPAN value for this Column
695
 
        var terminalChildNodes = 0;
696
 
 
697
 
        function countTerminalChildNodes(ancestor) {
698
 
            var descendants = ancestor.children,
699
 
                i = 0,
700
 
                len = descendants.length;
701
 
 
702
 
            // Drill down each branch and count terminal nodes
703
 
            for(; i<len; ++i) {
704
 
                // Keep drilling down
705
 
                if(YLang.isArray(descendants[i].children)) {
706
 
                    countTerminalChildNodes(descendants[i]);
707
 
                }
708
 
                // Reached branch terminus
709
 
                else {
710
 
                    terminalChildNodes++;
711
 
                }
712
 
            }
713
 
        }
714
 
        countTerminalChildNodes(definition);
715
 
        column.colSpan = terminalChildNodes;
716
 
    },
717
 
 
718
 
    /**
719
 
    * @method _setRowSpans
720
 
    * @description Calculates and sets rowSpan attribute on all Columns.
721
 
    * @private
722
 
    */
723
 
    _setRowSpans: function() {
724
 
        // Determine ROWSPAN value for each Column in the DOM tree
725
 
        function parseDomTreeForRowSpan(tree) {
726
 
            var maxRowDepth = 1,
727
 
                currentRow,
728
 
                currentColumn,
729
 
                m,p;
730
 
 
731
 
            // Calculate the max depth of descendants for this row
732
 
            function countMaxRowDepth(row, tmpRowDepth) {
733
 
                tmpRowDepth = tmpRowDepth || 1;
734
 
 
735
 
                var i = 0,
736
 
                    len = row.length,
737
 
                    col;
738
 
 
739
 
                for(; i<len; ++i) {
740
 
                    col = row[i];
741
 
                    // Column has children, so keep counting
742
 
                    if(YLang.isArray(col.children)) {
743
 
                        tmpRowDepth++;
744
 
                        countMaxRowDepth(col.children, tmpRowDepth);
745
 
                        tmpRowDepth--;
746
 
                    }
747
 
                    // Column has children, so keep counting
748
 
                    else if(col.get && YLang.isArray(col.get("children"))) {
749
 
                        tmpRowDepth++;
750
 
                        countMaxRowDepth(col.get("children"), tmpRowDepth);
751
 
                        tmpRowDepth--;
752
 
                    }
753
 
                    // No children, is it the max depth?
754
 
                    else {
755
 
                        if(tmpRowDepth > maxRowDepth) {
756
 
                            maxRowDepth = tmpRowDepth;
757
 
                        }
758
 
                    }
759
 
                }
760
 
            }
761
 
 
762
 
            // Count max row depth for each row
763
 
            for(m=0; m<tree.length; m++) {
764
 
                currentRow = tree[m];
765
 
                countMaxRowDepth(currentRow);
766
 
 
767
 
                // Assign the right ROWSPAN values to each Column in the row
768
 
                for(p=0; p<currentRow.length; p++) {
769
 
                    currentColumn = currentRow[p];
770
 
                    if(!YLang.isArray(currentColumn.get("children"))) {
771
 
                        currentColumn.rowSpan = maxRowDepth;
772
 
                    }
773
 
                    // Default is already 1
774
 
                    // else currentColumn.rowSpan =1;
775
 
                }
776
 
 
777
 
                // Reset counter for next row
778
 
                maxRowDepth = 1;
779
 
            }
780
 
        }
781
 
        parseDomTreeForRowSpan(this.tree);
782
 
    },
783
 
 
784
 
    /**
785
 
    * @method _setHeaders
786
 
    * @description Calculates and sets headers attribute on all Columns.
787
 
    * @private
788
 
    */
789
 
    _setHeaders: function() {
790
 
        var headers, column,
791
 
            allKeys = this.keys,
792
 
            i=0, len = allKeys.length;
793
 
 
794
 
        function recurseAncestorsForHeaders(headers, column) {
795
 
            headers.push(column.get("id"));
796
 
            if(column.parent) {
797
 
                recurseAncestorsForHeaders(headers, column.parent);
798
 
            }
799
 
        }
800
 
        for(; i<len; ++i) {
801
 
            headers = [];
802
 
            column = allKeys[i];
803
 
            recurseAncestorsForHeaders(headers, column);
804
 
            column.headers = headers.reverse().join(" ");
805
 
        }
806
 
    },
807
 
 
808
 
    //TODO
809
 
    getColumn: function() {
810
 
    }
811
 
});
812
 
 
813
 
Y.Columnset = Columnset;
814
 
/**
815
 
 * The DataTable widget provides a progressively enhanced DHTML control for
816
 
 * displaying tabular data across A-grade browsers.
817
 
 *
818
 
 * @module datatable
819
 
 * @main datatable
820
 
 */
821
 
 
822
 
/**
823
 
 * Provides the base DataTable implementation, which can be extended to add
824
 
 * additional functionality, such as sorting or scrolling.
825
 
 *
826
 
 * @module datatable
827
 
 * @submodule datatable-base
828
 
 */
829
 
 
830
 
/**
831
 
 * Base class for the DataTable widget.
832
 
 * @class DataTable.Base
833
 
 * @extends Widget
834
 
 * @constructor
835
 
 */
836
 
function DTBase(config) {
837
 
    DTBase.superclass.constructor.apply(this, arguments);
838
 
}
839
 
 
840
 
/////////////////////////////////////////////////////////////////////////////
841
 
//
842
 
// STATIC PROPERTIES
843
 
//
844
 
/////////////////////////////////////////////////////////////////////////////
845
 
Y.mix(DTBase, {
846
 
 
847
 
    /**
848
 
     * Class name.
849
 
     *
850
 
     * @property NAME
851
 
     * @type String
852
 
     * @static
853
 
     * @final
854
 
     * @value "dataTable"
855
 
     */
856
 
    NAME:  "dataTable",
857
 
 
858
 
/////////////////////////////////////////////////////////////////////////////
859
 
//
860
 
// ATTRIBUTES
861
 
//
862
 
/////////////////////////////////////////////////////////////////////////////
863
 
    ATTRS: {
864
 
        /**
865
 
        * @attribute columnset
866
 
        * @description Pointer to Columnset instance.
867
 
        * @type Array | Y.Columnset
868
 
        */
869
 
        columnset: {
870
 
            setter: "_setColumnset"
871
 
        },
872
 
 
873
 
        /**
874
 
        * @attribute recordset
875
 
        * @description Pointer to Recordset instance.
876
 
        * @type Array | Y.Recordset
877
 
        */
878
 
        recordset: {
879
 
            valueFn: '_initRecordset',
880
 
            setter: "_setRecordset"
881
 
        },
882
 
 
883
 
        /*TODO
884
 
        * @attribute state
885
 
        * @description Internal state.
886
 
        * @readonly
887
 
        * @type
888
 
        */
889
 
        /*state: {
890
 
            value: new Y.State(),
891
 
            readOnly: true
892
 
 
893
 
        },*/
894
 
 
895
 
        /**
896
 
        * @attribute summary
897
 
        * @description Summary.
898
 
        * @type String
899
 
        */
900
 
        summary: {
901
 
        },
902
 
 
903
 
        /**
904
 
        * @attribute caption
905
 
        * @description Caption
906
 
        * @type String
907
 
        */
908
 
        caption: {
909
 
        },
910
 
 
911
 
        /**
912
 
        * @attribute thValueTemplate
913
 
        * @description Tokenized markup template for TH value.
914
 
        * @type String
915
 
        * @default '{value}'
916
 
        */
917
 
        thValueTemplate: {
918
 
            value: TEMPLATE_VALUE
919
 
        },
920
 
 
921
 
        /**
922
 
        * @attribute tdValueTemplate
923
 
        * @description Tokenized markup template for TD value.
924
 
        * @type String
925
 
        * @default '{value}'
926
 
        */
927
 
        tdValueTemplate: {
928
 
            value: TEMPLATE_VALUE
929
 
        },
930
 
 
931
 
        /**
932
 
        * @attribute trTemplate
933
 
        * @description Tokenized markup template for TR node creation.
934
 
        * @type String
935
 
        * @default '<tr id="{id}"></tr>'
936
 
        */
937
 
        trTemplate: {
938
 
            value: TEMPLATE_TR
939
 
        }
940
 
    },
941
 
 
942
 
/////////////////////////////////////////////////////////////////////////////
943
 
//
944
 
// TODO: HTML_PARSER
945
 
//
946
 
/////////////////////////////////////////////////////////////////////////////
947
 
    HTML_PARSER: {
948
 
        /*caption: function (srcNode) {
949
 
            
950
 
        }*/
951
 
    }
952
 
});
953
 
 
954
 
/////////////////////////////////////////////////////////////////////////////
955
 
//
956
 
// PROTOTYPE
957
 
//
958
 
/////////////////////////////////////////////////////////////////////////////
959
 
Y.extend(DTBase, Y.Widget, {
960
 
    /**
961
 
    * @property thTemplate
962
 
    * @description Tokenized markup template for TH node creation.
963
 
    * @type String
964
 
    * @default '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}" abbr="{abbr}"><div class="'+CLASS_LINER+'">{value}</div></th>'
965
 
    */
966
 
    thTemplate: TEMPLATE_TH,
967
 
 
968
 
    /**
969
 
    * @property tdTemplate
970
 
    * @description Tokenized markup template for TD node creation.
971
 
    * @type String
972
 
    * @default '<td headers="{headers}" class="{classnames}"><div class="yui3-datatable-liner">{value}</div></td>'
973
 
    */
974
 
    tdTemplate: TEMPLATE_TD,
975
 
    
976
 
    /**
977
 
    * @property _theadNode
978
 
    * @description Pointer to THEAD node.
979
 
    * @type {Node}
980
 
    * @private
981
 
    */
982
 
    _theadNode: null,
983
 
    
984
 
    /**
985
 
    * @property _tbodyNode
986
 
    * @description Pointer to TBODY node.
987
 
    * @type {Node}
988
 
    * @private
989
 
    */
990
 
    _tbodyNode: null,
991
 
    
992
 
    /**
993
 
    * @property _msgNode
994
 
    * @description Pointer to message display node.
995
 
    * @type {Node}
996
 
    * @private
997
 
    */
998
 
    _msgNode: null,
999
 
 
1000
 
    /////////////////////////////////////////////////////////////////////////////
1001
 
    //
1002
 
    // ATTRIBUTE HELPERS
1003
 
    //
1004
 
    /////////////////////////////////////////////////////////////////////////////
1005
 
    /**
1006
 
    * @method _setColumnset
1007
 
    * @description Converts Array to Y.Columnset.
1008
 
    * @param columns {Array | Y.Columnset}
1009
 
    * @return {Columnset}
1010
 
    * @private
1011
 
    */
1012
 
    _setColumnset: function(columns) {
1013
 
        return YLang.isArray(columns) ? new Y.Columnset({definitions:columns}) : columns;
1014
 
    },
1015
 
 
1016
 
    /**
1017
 
     * Updates the UI if Columnset is changed.
1018
 
     *
1019
 
     * @method _afterColumnsetChange
1020
 
     * @param e {Event} Custom event for the attribute change.
1021
 
     * @protected
1022
 
     */
1023
 
    _afterColumnsetChange: function (e) {
1024
 
        this._uiSetColumnset(e.newVal);
1025
 
    },
1026
 
 
1027
 
    /**
1028
 
    * @method _setRecordset
1029
 
    * @description Converts Array to Y.Recordset.
1030
 
    * @param records {Array | Recordset}
1031
 
    * @return {Recordset}
1032
 
    * @private
1033
 
    */
1034
 
    _setRecordset: function(rs) {
1035
 
        if(YLang.isArray(rs)) {
1036
 
            rs = new Y.Recordset({records:rs});
1037
 
        }
1038
 
 
1039
 
        rs.addTarget(this);
1040
 
        return rs;
1041
 
    },
1042
 
    
1043
 
    /**
1044
 
    * Updates the UI if Recordset is changed.
1045
 
    *
1046
 
    * @method _afterRecordsetChange
1047
 
    * @param e {Event} Custom event for the attribute change.
1048
 
    * @protected
1049
 
    */
1050
 
    _afterRecordsetChange: function (e) {
1051
 
        this._uiSetRecordset(e.newVal);
1052
 
    },
1053
 
 
1054
 
    /**
1055
 
    * Updates the UI if Recordset records are changed.
1056
 
    *
1057
 
    * @method _afterRecordsChange
1058
 
    * @param e {Event} Custom event for the attribute change.
1059
 
    * @protected
1060
 
    */
1061
 
    _afterRecordsChange: function (e) {
1062
 
        this._uiSetRecordset(this.get('recordset'));
1063
 
    },
1064
 
 
1065
 
    /**
1066
 
     * Updates the UI if summary is changed.
1067
 
     *
1068
 
     * @method _afterSummaryChange
1069
 
     * @param e {Event} Custom event for the attribute change.
1070
 
     * @protected
1071
 
     */
1072
 
    _afterSummaryChange: function (e) {
1073
 
        this._uiSetSummary(e.newVal);
1074
 
    },
1075
 
 
1076
 
    /**
1077
 
     * Updates the UI if caption is changed.
1078
 
     *
1079
 
     * @method _afterCaptionChange
1080
 
     * @param e {Event} Custom event for the attribute change.
1081
 
     * @protected
1082
 
     */
1083
 
    _afterCaptionChange: function (e) {
1084
 
        this._uiSetCaption(e.newVal);
1085
 
    },
1086
 
 
1087
 
    ////////////////////////////////////////////////////////////////////////////
1088
 
    //
1089
 
    // METHODS
1090
 
    //
1091
 
    ////////////////////////////////////////////////////////////////////////////
1092
 
 
1093
 
    /**
1094
 
    * Destructor.
1095
 
    *
1096
 
    * @method destructor
1097
 
    * @private
1098
 
    */
1099
 
    destructor: function() {
1100
 
         this.get("recordset").removeTarget(this);
1101
 
    },
1102
 
    
1103
 
    ////////////////////////////////////////////////////////////////////////////
1104
 
    //
1105
 
    // RENDER
1106
 
    //
1107
 
    ////////////////////////////////////////////////////////////////////////////
1108
 
 
1109
 
    /**
1110
 
    * Renders UI.
1111
 
    *
1112
 
    * @method renderUI
1113
 
    * @private
1114
 
    */
1115
 
    renderUI: function() {
1116
 
        // TABLE
1117
 
        this._addTableNode(this.get("contentBox"));
1118
 
 
1119
 
        // COLGROUP
1120
 
        this._addColgroupNode(this._tableNode);
1121
 
 
1122
 
        // THEAD
1123
 
        this._addTheadNode(this._tableNode);
1124
 
 
1125
 
        // Primary TBODY
1126
 
        this._addTbodyNode(this._tableNode);
1127
 
 
1128
 
        // Message TBODY
1129
 
        this._addMessageNode(this._tableNode);
1130
 
 
1131
 
        // CAPTION
1132
 
        this._addCaptionNode(this._tableNode);
1133
 
   },
1134
 
 
1135
 
    /**
1136
 
    * Creates and attaches TABLE element to given container.
1137
 
    *
1138
 
    * @method _addTableNode
1139
 
    * @param containerNode {Node} Parent node.
1140
 
    * @protected
1141
 
    * @return {Node}
1142
 
    */
1143
 
    _addTableNode: function(containerNode) {
1144
 
        if (!this._tableNode) {
1145
 
            this._tableNode = containerNode.appendChild(Ycreate(TEMPLATE_TABLE));
1146
 
        }
1147
 
        return this._tableNode;
1148
 
    },
1149
 
 
1150
 
    /**
1151
 
    * Creates and attaches COLGROUP element to given TABLE.
1152
 
    *
1153
 
    * @method _addColgroupNode
1154
 
    * @param tableNode {Node} Parent node.
1155
 
    * @protected
1156
 
    * @return {Node}
1157
 
    */
1158
 
    _addColgroupNode: function(tableNode) {
1159
 
        // Add COLs to DOCUMENT FRAGMENT
1160
 
        var len = this.get("columnset").keys.length,
1161
 
            i = 0,
1162
 
            allCols = ["<colgroup>"];
1163
 
 
1164
 
        for(; i<len; ++i) {
1165
 
            allCols.push(TEMPLATE_COL);
1166
 
        }
1167
 
 
1168
 
        allCols.push("</colgroup>");
1169
 
 
1170
 
        // Create COLGROUP
1171
 
        this._colgroupNode = tableNode.insertBefore(Ycreate(allCols.join("")), tableNode.get("firstChild"));
1172
 
 
1173
 
        return this._colgroupNode;
1174
 
    },
1175
 
 
1176
 
    /**
1177
 
    * Creates and attaches THEAD element to given container.
1178
 
    *
1179
 
    * @method _addTheadNode
1180
 
    * @param tableNode {Node} Parent node.
1181
 
    * @protected
1182
 
    * @return {Node}
1183
 
    */
1184
 
    _addTheadNode: function(tableNode) {
1185
 
        if(tableNode) {
1186
 
            this._theadNode = tableNode.insertBefore(Ycreate(TEMPLATE_THEAD), this._colgroupNode.next());
1187
 
            return this._theadNode;
1188
 
        }
1189
 
    },
1190
 
 
1191
 
    /**
1192
 
    * Creates and attaches TBODY element to given container.
1193
 
    *
1194
 
    * @method _addTbodyNode
1195
 
    * @param tableNode {Node} Parent node.
1196
 
    * @protected
1197
 
    * @return {Node}
1198
 
    */
1199
 
    _addTbodyNode: function(tableNode) {
1200
 
        this._tbodyNode = tableNode.appendChild(Ycreate(TEMPLATE_TBODY));
1201
 
        return this._tbodyNode;
1202
 
    },
1203
 
 
1204
 
    /**
1205
 
    * Creates and attaches message display element to given container.
1206
 
    *
1207
 
    * @method _addMessageNode
1208
 
    * @param tableNode {Node} Parent node.
1209
 
    * @protected
1210
 
    * @return {Node}
1211
 
    */
1212
 
    _addMessageNode: function(tableNode) {
1213
 
        this._msgNode = tableNode.insertBefore(Ycreate(TEMPLATE_MSG), this._tbodyNode);
1214
 
        return this._msgNode;
1215
 
    },
1216
 
 
1217
 
    /**
1218
 
    * Creates and attaches CAPTION element to given container.
1219
 
    *
1220
 
    * @method _addCaptionNode
1221
 
    * @param tableNode {Node} Parent node.
1222
 
    * @protected
1223
 
    * @return {Node}
1224
 
    */
1225
 
    _addCaptionNode: function(tableNode) {
1226
 
        this._captionNode = Y.Node.create('<caption></caption>');
1227
 
    },
1228
 
 
1229
 
    ////////////////////////////////////////////////////////////////////////////
1230
 
    //
1231
 
    // BIND
1232
 
    //
1233
 
    ////////////////////////////////////////////////////////////////////////////
1234
 
 
1235
 
    /**
1236
 
    * Binds events.
1237
 
    *
1238
 
    * @method bindUI
1239
 
    * @private
1240
 
    */
1241
 
    bindUI: function() {
1242
 
        this.after({
1243
 
            columnsetChange: this._afterColumnsetChange,
1244
 
            summaryChange  : this._afterSummaryChange,
1245
 
            captionChange  : this._afterCaptionChange,
1246
 
            recordsetChange: this._afterRecordsChange,
1247
 
            "recordset:tableChange": this._afterRecordsChange
1248
 
        });
1249
 
    },
1250
 
    
1251
 
    delegate: function(type) {
1252
 
        //TODO: is this necessary?
1253
 
        if(type==="dblclick") {
1254
 
            this.get("boundingBox").delegate.apply(this.get("boundingBox"), arguments);
1255
 
        }
1256
 
        else {
1257
 
            this.get("contentBox").delegate.apply(this.get("contentBox"), arguments);
1258
 
        }
1259
 
    },
1260
 
    
1261
 
 
1262
 
    ////////////////////////////////////////////////////////////////////////////
1263
 
    //
1264
 
    // SYNC
1265
 
    //
1266
 
    ////////////////////////////////////////////////////////////////////////////
1267
 
 
1268
 
    /**
1269
 
    * Syncs UI to intial state.
1270
 
    *
1271
 
    * @method syncUI
1272
 
    * @private
1273
 
    */
1274
 
    syncUI: function() {
1275
 
        // THEAD ROWS
1276
 
        this._uiSetColumnset(this.get("columnset"));
1277
 
        // DATA ROWS
1278
 
        this._uiSetRecordset(this.get("recordset"));
1279
 
        // SUMMARY
1280
 
        this._uiSetSummary(this.get("summary"));
1281
 
        // CAPTION
1282
 
        this._uiSetCaption(this.get("caption"));
1283
 
    },
1284
 
 
1285
 
    /**
1286
 
     * Updates summary.
1287
 
     *
1288
 
     * @method _uiSetSummary
1289
 
     * @param val {String} New summary.
1290
 
     * @protected
1291
 
     */
1292
 
    _uiSetSummary: function(val) {
1293
 
        val = YisValue(val) ? val : "";
1294
 
        this._tableNode.set("summary", val);
1295
 
    },
1296
 
 
1297
 
    /**
1298
 
     * Updates caption.
1299
 
     *
1300
 
     * @method _uiSetCaption
1301
 
     * @param val {String} New caption.
1302
 
     * @protected
1303
 
     */
1304
 
    _uiSetCaption: function(val) {
1305
 
        var caption = this._captionNode,
1306
 
            inDoc   = caption.inDoc(),
1307
 
            method  = val ? (!inDoc && 'prepend') : (inDoc && 'removeChild');
1308
 
 
1309
 
        caption.setContent(val || '');
1310
 
 
1311
 
        if (method) {
1312
 
            // prepend of remove necessary
1313
 
            this._tableNode[method](caption);
1314
 
        }
1315
 
    },
1316
 
 
1317
 
 
1318
 
    ////////////////////////////////////////////////////////////////////////////
1319
 
    //
1320
 
    // THEAD/COLUMNSET FUNCTIONALITY
1321
 
    //
1322
 
    ////////////////////////////////////////////////////////////////////////////
1323
 
    /**
1324
 
     * Updates THEAD.
1325
 
     *
1326
 
     * @method _uiSetColumnset
1327
 
     * @param cs {Columnset} New Columnset.
1328
 
     * @protected
1329
 
     */
1330
 
    _uiSetColumnset: function(cs) {
1331
 
        var tree = cs.tree,
1332
 
            thead = this._theadNode,
1333
 
            i = 0,
1334
 
            len = tree.length,
1335
 
            parent = thead.get("parentNode"),
1336
 
            nextSibling = thead.next();
1337
 
            
1338
 
        // Move THEAD off DOM
1339
 
        thead.remove();
1340
 
        
1341
 
        thead.get("children").remove(true);
1342
 
 
1343
 
        // Iterate tree of columns to add THEAD rows
1344
 
        for(; i<len; ++i) {
1345
 
            this._addTheadTrNode({
1346
 
                thead:   thead,
1347
 
                columns: tree[i],
1348
 
                id     : '' // to avoid {id} leftovers from the trTemplate
1349
 
            }, (i === 0), (i === len - 1));
1350
 
        }
1351
 
 
1352
 
        // Column helpers needs _theadNode to exist
1353
 
        //this._createColumnHelpers();
1354
 
 
1355
 
        
1356
 
        // Re-attach THEAD to DOM
1357
 
        parent.insert(thead, nextSibling);
1358
 
 
1359
 
     },
1360
 
     
1361
 
    /**
1362
 
    * Creates and attaches header row element.
1363
 
    *
1364
 
    * @method _addTheadTrNode
1365
 
    * @param o {Object} {thead, columns}.
1366
 
    * @param isFirst {Boolean} Is first row.
1367
 
    * @param isFirst {Boolean} Is last row.
1368
 
    * @protected
1369
 
    */
1370
 
     _addTheadTrNode: function(o, isFirst, isLast) {
1371
 
        o.tr = this._createTheadTrNode(o, isFirst, isLast);
1372
 
        this._attachTheadTrNode(o);
1373
 
     },
1374
 
     
1375
 
 
1376
 
    /**
1377
 
    * Creates header row element.
1378
 
    *
1379
 
    * @method _createTheadTrNode
1380
 
    * @param o {Object} {thead, columns}.
1381
 
    * @param isFirst {Boolean} Is first row.
1382
 
    * @param isLast {Boolean} Is last row.
1383
 
    * @protected
1384
 
    * @return {Node}
1385
 
    */
1386
 
    _createTheadTrNode: function(o, isFirst, isLast) {
1387
 
        //TODO: custom classnames
1388
 
        var tr = Ycreate(fromTemplate(this.get("trTemplate"), o)),
1389
 
            i = 0,
1390
 
            columns = o.columns,
1391
 
            len = columns.length,
1392
 
            column;
1393
 
 
1394
 
         // Set FIRST/LAST class
1395
 
        if(isFirst) {
1396
 
            tr.addClass(CLASS_FIRST);
1397
 
        }
1398
 
        if(isLast) {
1399
 
            tr.addClass(CLASS_LAST);
1400
 
        }
1401
 
 
1402
 
        for(; i<len; ++i) {
1403
 
            column = columns[i];
1404
 
            this._addTheadThNode({value:column.get("label"), column: column, tr:tr});
1405
 
        }
1406
 
 
1407
 
        return tr;
1408
 
    },
1409
 
 
1410
 
    /**
1411
 
    * Attaches header row element.
1412
 
    *
1413
 
    * @method _attachTheadTrNode
1414
 
    * @param o {Object} {thead, columns, tr}.
1415
 
    * @protected
1416
 
    */
1417
 
    _attachTheadTrNode: function(o) {
1418
 
        o.thead.appendChild(o.tr);
1419
 
    },
1420
 
 
1421
 
    /**
1422
 
    * Creates and attaches header cell element.
1423
 
    *
1424
 
    * @method _addTheadThNode
1425
 
    * @param o {Object} {value, column, tr}.
1426
 
    * @protected
1427
 
    */
1428
 
    _addTheadThNode: function(o) {
1429
 
        o.th = this._createTheadThNode(o);
1430
 
        this._attachTheadThNode(o);
1431
 
        //TODO: assign all node pointers: thNode, thLinerNode, thLabelNode
1432
 
        o.column.thNode = o.th;
1433
 
    },
1434
 
 
1435
 
    /**
1436
 
    * Creates header cell element.
1437
 
    *
1438
 
    * @method _createTheadThNode
1439
 
    * @param o {Object} {value, column, tr}.
1440
 
    * @protected
1441
 
    * @return {Node}
1442
 
    */
1443
 
    _createTheadThNode: function(o) {
1444
 
        var column = o.column;
1445
 
        
1446
 
        // Populate template object
1447
 
        o.id = column.get("id");//TODO: validate 1 column ID per document
1448
 
        o.colspan = column.colSpan;
1449
 
        o.rowspan = column.rowSpan;
1450
 
        o.abbr = column.get("abbr");
1451
 
        o.classnames = column.get("classnames");
1452
 
        o.value = fromTemplate(this.get("thValueTemplate"), o);
1453
 
 
1454
 
        /*TODO
1455
 
        // Clear minWidth on hidden Columns
1456
 
        if(column.get("hidden")) {
1457
 
            //this._clearMinWidth(column);
1458
 
        }
1459
 
        */
1460
 
        
1461
 
        return Ycreate(fromTemplate(this.thTemplate, o));
1462
 
    },
1463
 
 
1464
 
    /**
1465
 
    * Attaches header cell element.
1466
 
    *
1467
 
    * @method _attachTheadThNode
1468
 
    * @param o {Object} {value, column, tr}.
1469
 
    * @protected
1470
 
    */
1471
 
    _attachTheadThNode: function(o) {
1472
 
        o.tr.appendChild(o.th);
1473
 
    },
1474
 
 
1475
 
    ////////////////////////////////////////////////////////////////////////////
1476
 
    //
1477
 
    // TBODY/RECORDSET FUNCTIONALITY
1478
 
    //
1479
 
    ////////////////////////////////////////////////////////////////////////////
1480
 
    /**
1481
 
     * Updates TBODY.
1482
 
     *
1483
 
     * @method _uiSetRecordset
1484
 
     * @param rs {Recordset} New Recordset.
1485
 
     * @protected
1486
 
     */
1487
 
    _uiSetRecordset: function(rs) {
1488
 
        var self = this,
1489
 
            oldTbody = this._tbodyNode,
1490
 
            parent = oldTbody.get("parentNode"),
1491
 
            nextSibling = oldTbody.next(),
1492
 
            columns = this.get('columnset').keys,
1493
 
            cellValueTemplate = this.get('tdValueTemplate'),
1494
 
            o = {},
1495
 
            newTbody, i, len, column, formatter;
1496
 
 
1497
 
        // Replace TBODY with a new one
1498
 
        //TODO: split _addTbodyNode into create/attach
1499
 
        oldTbody.remove();
1500
 
        oldTbody = null;
1501
 
        newTbody = this._addTbodyNode(this._tableNode);
1502
 
        newTbody.remove();
1503
 
        this._tbodyNode = newTbody;
1504
 
        o.tbody = newTbody;
1505
 
 
1506
 
        o.rowTemplate = this.get('trTemplate');
1507
 
        o.columns = [];
1508
 
 
1509
 
        // Build up column data to avoid passing through Attribute APIs inside
1510
 
        // render loops for rows and cells
1511
 
        for (i = columns.length - 1; i >= 0; --i) {
1512
 
            column = columns[i];
1513
 
            o.columns[i] = {
1514
 
                column        : column,
1515
 
                fields        : column.get('field'),
1516
 
                classnames    : column.get('classnames'),
1517
 
                emptyCellValue: column.get('emptyCellValue')
1518
 
            }
1519
 
 
1520
 
            formatter = column.get('formatter');
1521
 
 
1522
 
            if (YLang.isFunction(formatter)) {
1523
 
                // function formatters need to run before checking if the value
1524
 
                // needs defaulting from column.emptyCellValue
1525
 
                formatter = Y.bind(this._functionFormatter, this, formatter);
1526
 
            } else {
1527
 
                if (!YLang.isString(formatter)) {
1528
 
                    formatter = cellValueTemplate;
1529
 
                }
1530
 
 
1531
 
                // string formatters need the value defaulted before processing
1532
 
                formatter = Y.bind(this._templateFormatter, this, formatter);
1533
 
            }
1534
 
 
1535
 
            o.columns[i].formatter = formatter;
1536
 
        }
1537
 
 
1538
 
 
1539
 
        // Iterate Recordset to use existing TR when possible or add new TR
1540
 
        // TODO i = this.get("state.offsetIndex")
1541
 
        // TODO len =this.get("state.pageLength")
1542
 
        for (i = 0, len = rs.size(); i < len; ++i) {
1543
 
            o.record = rs.item(i);
1544
 
            o.data   = o.record.get("data");
1545
 
            o.rowindex = i;
1546
 
            this._addTbodyTrNode(o); //TODO: sometimes rowindex != recordindex
1547
 
        }
1548
 
        
1549
 
        // TBODY to DOM
1550
 
        parent.insert(this._tbodyNode, nextSibling);
1551
 
    },
1552
 
 
1553
 
    _functionFormatter: function (formatter, o) {
1554
 
        var value = formatter.call(this, o);
1555
 
 
1556
 
        return (value !== undefined) ? value : o.emptyCellValue;
1557
 
    },
1558
 
 
1559
 
    _templateFormatter: function (template, o) {
1560
 
        if (o.value === undefined) {
1561
 
            o.value = o.emptyCellValue;
1562
 
        }
1563
 
 
1564
 
        return fromTemplate(template, o);
1565
 
    },
1566
 
 
1567
 
    /**
1568
 
    * Creates and attaches data row element.
1569
 
    *
1570
 
    * @method _addTbodyTrNode
1571
 
    * @param o {Object} {tbody, record}
1572
 
    * @protected
1573
 
    */
1574
 
    _addTbodyTrNode: function(o) {
1575
 
        var row = o.tbody.one("#" + o.record.get("id"));
1576
 
 
1577
 
        o.tr = row || this._createTbodyTrNode(o);
1578
 
 
1579
 
        this._attachTbodyTrNode(o);
1580
 
    },
1581
 
 
1582
 
    /**
1583
 
    * Creates data row element.
1584
 
    *
1585
 
    * @method _createTbodyTrNode
1586
 
    * @param o {Object} {tbody, record}
1587
 
    * @protected
1588
 
    * @return {Node}
1589
 
    */
1590
 
    _createTbodyTrNode: function(o) {
1591
 
        var columns = o.columns,
1592
 
            i, len, columnInfo;
1593
 
 
1594
 
        o.tr = Ycreate(fromTemplate(o.rowTemplate, { id: o.record.get('id') }));
1595
 
        
1596
 
        for (i = 0, len = columns.length; i < len; ++i) {
1597
 
            columnInfo      = columns[i];
1598
 
            o.column        = columnInfo.column;
1599
 
            o.field         = columnInfo.fields;
1600
 
            o.classnames    = columnInfo.classnames;
1601
 
            o.formatter     = columnInfo.formatter;
1602
 
            o.emptyCellValue= columnInfo.emptyCellValue;
1603
 
 
1604
 
            this._addTbodyTdNode(o);
1605
 
        }
1606
 
        
1607
 
        return o.tr;
1608
 
    },
1609
 
 
1610
 
    /**
1611
 
    * Attaches data row element.
1612
 
    *
1613
 
    * @method _attachTbodyTrNode
1614
 
    * @param o {Object} {tbody, record, tr}.
1615
 
    * @protected
1616
 
    */
1617
 
    _attachTbodyTrNode: function(o) {
1618
 
        var tbody = o.tbody,
1619
 
            tr = o.tr,
1620
 
            index = o.rowindex,
1621
 
            nextSibling = tbody.get("children").item(index) || null,
1622
 
            isOdd = (index % 2);
1623
 
            
1624
 
        if(isOdd) {
1625
 
            tr.replaceClass(CLASS_EVEN, CLASS_ODD);
1626
 
        } else {
1627
 
            tr.replaceClass(CLASS_ODD, CLASS_EVEN);
1628
 
        }
1629
 
        
1630
 
        tbody.insertBefore(tr, nextSibling);
1631
 
    },
1632
 
 
1633
 
    /**
1634
 
    * Creates and attaches data cell element.
1635
 
    *
1636
 
    * @method _addTbodyTdNode
1637
 
    * @param o {Object} {record, column, tr}.
1638
 
    * @protected
1639
 
    */
1640
 
    _addTbodyTdNode: function(o) {
1641
 
        o.td = this._createTbodyTdNode(o);
1642
 
        this._attachTbodyTdNode(o);
1643
 
        delete o.td;
1644
 
    },
1645
 
    
1646
 
    /**
1647
 
    Creates a TD Node from the tdTemplate property using the input object as
1648
 
    template {placeholder} values.  The created Node is also assigned to the
1649
 
    `td` property on the input object.
1650
 
 
1651
 
    If the input object already has a `td` property, it is returned an no new
1652
 
    Node is created.
1653
 
 
1654
 
    @method createCell
1655
 
    @param {Object} data Template values
1656
 
    @return {Node}
1657
 
    **/
1658
 
    createCell: function (data) {
1659
 
        return data && (data.td ||
1660
 
            (data.td = Ycreate(fromTemplate(this.tdTemplate, data))));
1661
 
    },
1662
 
 
1663
 
    /**
1664
 
    * Creates data cell element.
1665
 
    *
1666
 
    * @method _createTbodyTdNode
1667
 
    * @param o {Object} {record, column, tr}.
1668
 
    * @protected
1669
 
    * @return {Node}
1670
 
    */
1671
 
    _createTbodyTdNode: function(o) {
1672
 
        o.headers = o.column.headers;
1673
 
        o.value   = this.formatDataCell(o);
1674
 
 
1675
 
        return o.td || this.createCell(o);
1676
 
    },
1677
 
    
1678
 
    /**
1679
 
    * Attaches data cell element.
1680
 
    *
1681
 
    * @method _attachTbodyTdNode
1682
 
    * @param o {Object} {record, column, tr, headers, classnames, value}.
1683
 
    * @protected
1684
 
    */
1685
 
    _attachTbodyTdNode: function(o) {
1686
 
        o.tr.appendChild(o.td);
1687
 
    },
1688
 
 
1689
 
    /**
1690
 
     * Returns markup to insert into data cell element.
1691
 
     *
1692
 
     * @method formatDataCell
1693
 
     * @param @param o {Object} {record, column, tr, headers, classnames}.
1694
 
     */
1695
 
    formatDataCell: function (o) {
1696
 
        o.value = o.data[o.field];
1697
 
 
1698
 
        return o.formatter.call(this, o);
1699
 
    },
1700
 
 
1701
 
    _initRecordset: function () {
1702
 
        return new Y.Recordset({ records: [] });
1703
 
    }
1704
 
});
1705
 
 
1706
 
Y.namespace("DataTable").Base = DTBase;
1707
 
 
1708
 
 
1709
 
}, '3.5.0' ,{requires:['recordset-base','widget','substitute','event-mouseenter']});