~bjornt/lazr-js/prefetch-yui-3.5

« back to all changes in this revision

Viewing changes to src-js/lazrjs/yui/datatable/datatable-base.js

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-01-14 23:32:29 UTC
  • mfrom: (197.1.7 yui-3.3.0)
  • Revision ID: launchpad@pqm.canonical.com-20110114233229-r6i4cazdiiw18o7p
Upgrade to YUI 3.3.0 [r=mars]

Show diffs side-by-side

added added

removed removed

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