~veebers/sloecode/jquery-changeover

« back to all changes in this revision

Viewing changes to sloecode/public/yui/3.3.0/build/datatable/datatable-debug.js

  • Committer: Christopher Lee
  • Date: 2012-03-03 05:04:34 UTC
  • Revision ID: veebers@gmail.com-20120303050434-smeo0c6n7gz53thy
Removed unused YUI javascript (still using css libraries)

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']});
1644
 
 
1645
 
YUI.add('datatable-datasource', function(Y) {
1646
 
 
1647
 
/**
1648
 
 * Plugs DataTable with DataSource integration.
1649
 
 *
1650
 
 * @module datatable
1651
 
 * @submodule datatable-datasource
1652
 
 */
1653
 
 
1654
 
/**
1655
 
 * Adds DataSource integration to DataTable.
1656
 
 * @class DataTableDataSource
1657
 
 * @extends Plugin.Base
1658
 
 */
1659
 
function DataTableDataSource() {
1660
 
    DataTableDataSource.superclass.constructor.apply(this, arguments);
1661
 
}
1662
 
 
1663
 
/////////////////////////////////////////////////////////////////////////////
1664
 
//
1665
 
// STATIC PROPERTIES
1666
 
//
1667
 
/////////////////////////////////////////////////////////////////////////////
1668
 
Y.mix(DataTableDataSource, {
1669
 
    /**
1670
 
     * The namespace for the plugin. This will be the property on the host which
1671
 
     * references the plugin instance.
1672
 
     *
1673
 
     * @property NS
1674
 
     * @type String
1675
 
     * @static
1676
 
     * @final
1677
 
     * @value "datasource"
1678
 
     */
1679
 
    NS: "datasource",
1680
 
 
1681
 
    /**
1682
 
     * Class name.
1683
 
     *
1684
 
     * @property NAME
1685
 
     * @type String
1686
 
     * @static
1687
 
     * @final
1688
 
     * @value "dataTableDataSource"
1689
 
     */
1690
 
    NAME: "dataTableDataSource",
1691
 
 
1692
 
/////////////////////////////////////////////////////////////////////////////
1693
 
//
1694
 
// ATTRIBUTES
1695
 
//
1696
 
/////////////////////////////////////////////////////////////////////////////
1697
 
    ATTRS: {
1698
 
        /**
1699
 
        * @attribute datasource
1700
 
        * @description Pointer to DataSource instance.
1701
 
        * @type Y.DataSource
1702
 
        */
1703
 
        datasource: {
1704
 
            setter: "_setDataSource"
1705
 
        },
1706
 
        
1707
 
        /**
1708
 
        * @attribute initialRequest
1709
 
        * @description Request sent to DataSource immediately upon initialization.
1710
 
        * @type Object
1711
 
        */
1712
 
        initialRequest: {
1713
 
            setter: "_setInitialRequest"
1714
 
        }
1715
 
    }
1716
 
});
1717
 
 
1718
 
/////////////////////////////////////////////////////////////////////////////
1719
 
//
1720
 
// PROTOTYPE
1721
 
//
1722
 
/////////////////////////////////////////////////////////////////////////////
1723
 
Y.extend(DataTableDataSource, Y.Plugin.Base, {
1724
 
    /////////////////////////////////////////////////////////////////////////////
1725
 
    //
1726
 
    // ATTRIBUTE HELPERS
1727
 
    //
1728
 
    /////////////////////////////////////////////////////////////////////////////
1729
 
    /**
1730
 
    * @method _setDataSource
1731
 
    * @description Creates new DataSource instance if one is not provided.
1732
 
    * @param ds {Object | Y.DataSource}
1733
 
    * @returns Y.DataSource
1734
 
    * @private
1735
 
    */
1736
 
    _setDataSource: function(ds) {
1737
 
        return ds || new Y.DataSource.Local(ds);
1738
 
    },
1739
 
 
1740
 
    /**
1741
 
    * @method _setInitialRequest
1742
 
    * @description Sends request to DataSource.
1743
 
    * @param request {Object} DataSource request.
1744
 
    * @private
1745
 
    */
1746
 
    _setInitialRequest: function(request) {
1747
 
    },
1748
 
 
1749
 
    /////////////////////////////////////////////////////////////////////////////
1750
 
    //
1751
 
    // METHODS
1752
 
    //
1753
 
    /////////////////////////////////////////////////////////////////////////////
1754
 
    /**
1755
 
    * Initializer.
1756
 
    *
1757
 
    * @method initializer
1758
 
    * @param config {Object} Config object.
1759
 
    * @private
1760
 
    */
1761
 
    initializer: function(config) {
1762
 
        if(!Y.Lang.isUndefined(config.initialRequest)) {
1763
 
            this.load({request:config.initialRequest});
1764
 
        }
1765
 
    },
1766
 
 
1767
 
    ////////////////////////////////////////////////////////////////////////////
1768
 
    //
1769
 
    // DATA
1770
 
    //
1771
 
    ////////////////////////////////////////////////////////////////////////////
1772
 
 
1773
 
    /**
1774
 
     * Load data by calling DataSource's sendRequest() method under the hood.
1775
 
     *
1776
 
     * @method load
1777
 
     * @param config {object} Optional configuration parameters:
1778
 
     *
1779
 
     * <dl>
1780
 
     * <dt>request</dt><dd>Pass in a new request, or initialRequest is used.</dd>
1781
 
     * <dt>callback</dt><dd>Pass in DataSource callback object, or the following default is used:
1782
 
     *    <dl>
1783
 
     *      <dt>success</dt><dd>datatable.onDataReturnInitializeTable</dd>
1784
 
     *      <dt>failure</dt><dd>datatable.onDataReturnInitializeTable</dd>
1785
 
     *      <dt>scope</dt><dd>datatable</dd>
1786
 
     *      <dt>argument</dt><dd>datatable.getState()</dd>
1787
 
     *    </dl>
1788
 
     * </dd>
1789
 
     * <dt>datasource</dt><dd>Pass in a new DataSource instance to override the current DataSource for this transaction.</dd>
1790
 
     * </dl>
1791
 
     */
1792
 
    load: function(config) {
1793
 
        config = config || {};
1794
 
        config.request = config.request || this.get("initialRequest");
1795
 
        config.callback = config.callback || {
1796
 
            success: Y.bind(this.onDataReturnInitializeTable, this),
1797
 
            failure: Y.bind(this.onDataReturnInitializeTable, this),
1798
 
            argument: this.get("host").get("state") //TODO
1799
 
        };
1800
 
 
1801
 
        var ds = (config.datasource || this.get("datasource"));
1802
 
        if(ds) {
1803
 
            ds.sendRequest(config);
1804
 
        }
1805
 
    },
1806
 
 
1807
 
    /**
1808
 
     * Callback function passed to DataSource's sendRequest() method populates
1809
 
     * an entire DataTable with new data, clearing previous data, if any.
1810
 
     *
1811
 
     * @method onDataReturnInitializeTable
1812
 
     * @param e {Event.Facade} DataSource Event Facade object.
1813
 
     */
1814
 
    onDataReturnInitializeTable : function(e) {
1815
 
        this.get("host").set("recordset", new Y.Recordset({records: e.response.results}));
1816
 
    }
1817
 
});
1818
 
 
1819
 
Y.namespace("Plugin").DataTableDataSource = DataTableDataSource;
1820
 
 
1821
 
 
1822
 
 
1823
 
 
1824
 
 
1825
 
 
1826
 
}, '3.3.0' ,{requires:['datatable-base','plugin','datasource-local']});
1827
 
 
1828
 
YUI.add('datatable-sort', function(Y) {
1829
 
 
1830
 
/**
1831
 
 * Plugs DataTable with sorting functionality.
1832
 
 *
1833
 
 * @module datatable
1834
 
 * @submodule datatable-sort
1835
 
 */
1836
 
 
1837
 
/**
1838
 
 * Adds column sorting to DataTable.
1839
 
 * @class DataTableSort
1840
 
 * @extends Plugin.Base
1841
 
 */
1842
 
var YgetClassName = Y.ClassNameManager.getClassName,
1843
 
 
1844
 
    DATATABLE = "datatable",
1845
 
    COLUMN = "column",
1846
 
    ASC = "asc",
1847
 
    DESC = "desc",
1848
 
 
1849
 
    //TODO: Don't use hrefs - use tab/arrow/enter
1850
 
    TEMPLATE = '<a class="{link_class}" title="{link_title}" href="{link_href}">{value}</a>';
1851
 
 
1852
 
 
1853
 
function DataTableSort() {
1854
 
    DataTableSort.superclass.constructor.apply(this, arguments);
1855
 
}
1856
 
 
1857
 
/////////////////////////////////////////////////////////////////////////////
1858
 
//
1859
 
// STATIC PROPERTIES
1860
 
//
1861
 
/////////////////////////////////////////////////////////////////////////////
1862
 
Y.mix(DataTableSort, {
1863
 
    /**
1864
 
     * The namespace for the plugin. This will be the property on the host which
1865
 
     * references the plugin instance.
1866
 
     *
1867
 
     * @property NS
1868
 
     * @type String
1869
 
     * @static
1870
 
     * @final
1871
 
     * @value "sort"
1872
 
     */
1873
 
    NS: "sort",
1874
 
 
1875
 
    /**
1876
 
     * Class name.
1877
 
     *
1878
 
     * @property NAME
1879
 
     * @type String
1880
 
     * @static
1881
 
     * @final
1882
 
     * @value "dataTableSort"
1883
 
     */
1884
 
    NAME: "dataTableSort",
1885
 
 
1886
 
/////////////////////////////////////////////////////////////////////////////
1887
 
//
1888
 
// ATTRIBUTES
1889
 
//
1890
 
/////////////////////////////////////////////////////////////////////////////
1891
 
    ATTRS: {
1892
 
        /**
1893
 
        * @attribute trigger
1894
 
        * @description Defines the trigger that causes a column to be sorted:
1895
 
        * {event, selector}, where "event" is an event type and "selector" is
1896
 
        * is a node query selector.
1897
 
        * @type Object
1898
 
        * @default {event:"click", selector:"th"}
1899
 
        * @writeOnce "initOnly"
1900
 
        */
1901
 
        trigger: {
1902
 
            value: {event:"click", selector:"th"},
1903
 
            writeOnce: "initOnly"
1904
 
        },
1905
 
        
1906
 
        /**
1907
 
        * @attribute lastSortedBy
1908
 
        * @description Describes last known sort state: {key,dir}, where
1909
 
        * "key" is column key and "dir" is either "asc" or "desc".
1910
 
        * @type Object
1911
 
        */
1912
 
        lastSortedBy: {
1913
 
            setter: "_setLastSortedBy",
1914
 
            lazyAdd: false
1915
 
        },
1916
 
        
1917
 
        /**
1918
 
        * @attribute template
1919
 
        * @description Tokenized markup template for TH sort element.
1920
 
        * @type String
1921
 
        * @default '<a class="{link_class}" title="{link_title}" href="{link_href}">{value}</a>'
1922
 
        */
1923
 
        template: {
1924
 
            value: TEMPLATE
1925
 
        }
1926
 
    }
1927
 
});
1928
 
 
1929
 
/////////////////////////////////////////////////////////////////////////////
1930
 
//
1931
 
// PROTOTYPE
1932
 
//
1933
 
/////////////////////////////////////////////////////////////////////////////
1934
 
Y.extend(DataTableSort, Y.Plugin.Base, {
1935
 
 
1936
 
    /////////////////////////////////////////////////////////////////////////////
1937
 
    //
1938
 
    // METHODS
1939
 
    //
1940
 
    /////////////////////////////////////////////////////////////////////////////
1941
 
    /**
1942
 
    * Initializer.
1943
 
    *
1944
 
    * @method initializer
1945
 
    * @param config {Object} Config object.
1946
 
    * @private
1947
 
    */
1948
 
    initializer: function(config) {
1949
 
        var dt = this.get("host"),
1950
 
            trigger = this.get("trigger");
1951
 
            
1952
 
        dt.get("recordset").plug(Y.Plugin.RecordsetSort, {dt: dt});
1953
 
        dt.get("recordset").sort.addTarget(dt);
1954
 
        
1955
 
        // Wrap link around TH value
1956
 
        this.doBefore("_createTheadThNode", this._beforeCreateTheadThNode);
1957
 
        
1958
 
        // Add class
1959
 
        this.doBefore("_attachTheadThNode", this._beforeAttachTheadThNode);
1960
 
        this.doBefore("_attachTbodyTdNode", this._beforeAttachTbodyTdNode);
1961
 
 
1962
 
        // Attach trigger handlers
1963
 
        dt.delegate(trigger.event, Y.bind(this._onEventSortColumn,this), trigger.selector);
1964
 
 
1965
 
        // Attach UI hooks
1966
 
        dt.after("recordsetSort:sort", function() {
1967
 
            this._uiSetRecordset(this.get("recordset"));
1968
 
        });
1969
 
        this.on("lastSortedByChange", function(e) {
1970
 
            this._uiSetLastSortedBy(e.prevVal, e.newVal, dt);
1971
 
        });
1972
 
 
1973
 
        //TODO
1974
 
        //dt.after("recordset:mutation", function() {//reset lastSortedBy});
1975
 
        
1976
 
        //TODO
1977
 
        //add Column sortFn ATTR
1978
 
        
1979
 
        // Update UI after the fact (render-then-plug case)
1980
 
        if(dt.get("rendered")) {
1981
 
            dt._uiSetColumnset(dt.get("columnset"));
1982
 
            this._uiSetLastSortedBy(null, this.get("lastSortedBy"), dt);
1983
 
        }
1984
 
    },
1985
 
 
1986
 
    /**
1987
 
    * @method _setLastSortedBy
1988
 
    * @description Normalizes lastSortedBy
1989
 
    * @param val {String | Object} {key, dir} or "key"
1990
 
    * @returns {key, dir, notdir}
1991
 
    * @private
1992
 
    */
1993
 
    _setLastSortedBy: function(val) {
1994
 
        if(Y.Lang.isString(val)) {
1995
 
            return {key:val, dir:"asc", notdir:"desc"};
1996
 
        }
1997
 
        else if (val && val.key) {
1998
 
            if(val.dir === "desc") {
1999
 
                return {key:val.key, dir:"desc", notdir:"asc"};
2000
 
            }
2001
 
            else {
2002
 
                return {key:val.key, dir:"asc", notdir:"desc"};
2003
 
            }
2004
 
        }
2005
 
        else {
2006
 
            return null;
2007
 
        }
2008
 
    },
2009
 
 
2010
 
    /**
2011
 
     * Updates sort UI.
2012
 
     *
2013
 
     * @method _uiSetLastSortedBy
2014
 
     * @param val {Object} New lastSortedBy object {key,dir}.
2015
 
     * @param dt {Y.DataTable.Base} Host.
2016
 
     * @protected
2017
 
     */
2018
 
    _uiSetLastSortedBy: function(prevVal, newVal, dt) {
2019
 
        var prevKey = prevVal && prevVal.key,
2020
 
            prevDir = prevVal && prevVal.dir,
2021
 
            newKey = newVal && newVal.key,
2022
 
            newDir = newVal && newVal.dir,
2023
 
            cs = dt.get("columnset"),
2024
 
            prevColumn = cs.keyHash[prevKey],
2025
 
            newColumn = cs.keyHash[newKey],
2026
 
            tbodyNode = dt._tbodyNode,
2027
 
            prevRowList, newRowList;
2028
 
 
2029
 
        // Clear previous UI
2030
 
        if(prevColumn) {
2031
 
            prevColumn.thNode.removeClass(YgetClassName(DATATABLE, prevDir));
2032
 
            prevRowList = tbodyNode.all("."+YgetClassName(COLUMN, prevColumn.get("id")));
2033
 
            prevRowList.removeClass(YgetClassName(DATATABLE, prevDir));
2034
 
        }
2035
 
 
2036
 
        // Add new sort UI
2037
 
        if(newColumn) {
2038
 
            newColumn.thNode.addClass(YgetClassName(DATATABLE, newDir));
2039
 
            newRowList = tbodyNode.all("."+YgetClassName(COLUMN, newColumn.get("id")));
2040
 
            newRowList.addClass(YgetClassName(DATATABLE, newDir));
2041
 
        }
2042
 
    },
2043
 
 
2044
 
    /**
2045
 
    * Before header cell element is created, inserts link markup around {value}.
2046
 
    *
2047
 
    * @method _beforeCreateTheadThNode
2048
 
    * @param o {Object} {value, column, tr}.
2049
 
    * @protected
2050
 
    */
2051
 
    _beforeCreateTheadThNode: function(o) {
2052
 
        if(o.column.get("sortable")) {
2053
 
            o.value = Y.substitute(this.get("template"), {
2054
 
                link_class: o.link_class || "",
2055
 
                link_title: "title",
2056
 
                link_href: "#",
2057
 
                value: o.value
2058
 
            });
2059
 
        }
2060
 
    },
2061
 
 
2062
 
    /**
2063
 
    * Before header cell element is attached, sets applicable class names.
2064
 
    *
2065
 
    * @method _beforeAttachTheadThNode
2066
 
    * @param o {Object} {value, column, tr}.
2067
 
    * @protected
2068
 
    */
2069
 
    _beforeAttachTheadThNode: function(o) {
2070
 
        var lastSortedBy = this.get("lastSortedBy"),
2071
 
            key = lastSortedBy && lastSortedBy.key,
2072
 
            dir = lastSortedBy && lastSortedBy.dir,
2073
 
            notdir = lastSortedBy && lastSortedBy.notdir;
2074
 
 
2075
 
        // This Column is sortable
2076
 
        if(o.column.get("sortable")) {
2077
 
            o.th.addClass(YgetClassName(DATATABLE, "sortable"));
2078
 
        }
2079
 
        // This Column is currently sorted
2080
 
        if(key && (key === o.column.get("key"))) {
2081
 
            o.th.replaceClass(YgetClassName(DATATABLE, notdir), YgetClassName(DATATABLE, dir));
2082
 
        }
2083
 
    },
2084
 
 
2085
 
    /**
2086
 
    * Before header cell element is attached, sets applicable class names.
2087
 
    *
2088
 
    * @method _before_beforeAttachTbodyTdNode
2089
 
    * @param o {Object} {record, column, tr, headers, classnames, value}.
2090
 
    * @protected
2091
 
    */
2092
 
    _beforeAttachTbodyTdNode: function(o) {
2093
 
        var lastSortedBy = this.get("lastSortedBy"),
2094
 
            key = lastSortedBy && lastSortedBy.key,
2095
 
            dir = lastSortedBy && lastSortedBy.dir,
2096
 
            notdir = lastSortedBy && lastSortedBy.notdir;
2097
 
 
2098
 
        // This Column is sortable
2099
 
        if(o.column.get("sortable")) {
2100
 
            o.td.addClass(YgetClassName(DATATABLE, "sortable"));
2101
 
        }
2102
 
        // This Column is currently sorted
2103
 
        if(key && (key === o.column.get("key"))) {
2104
 
            o.td.replaceClass(YgetClassName(DATATABLE, notdir), YgetClassName(DATATABLE, dir));
2105
 
        }
2106
 
    },
2107
 
    /**
2108
 
    * In response to the "trigger" event, sorts the underlying Recordset and
2109
 
    * updates the lastSortedBy attribute.
2110
 
    *
2111
 
    * @method _onEventSortColumn
2112
 
    * @param o {Object} {value, column, tr}.
2113
 
    * @protected
2114
 
    */
2115
 
    _onEventSortColumn: function(e) {
2116
 
        e.halt();
2117
 
        //TODO: normalize e.currentTarget to TH
2118
 
        var dt = this.get("host"),
2119
 
            column = dt.get("columnset").idHash[e.currentTarget.get("id")],
2120
 
            key = column.get("key"),
2121
 
            field = column.get("field"),
2122
 
            lastSortedBy = this.get("lastSortedBy"),
2123
 
            dir = (lastSortedBy &&
2124
 
                lastSortedBy.key === key &&
2125
 
                lastSortedBy.dir === ASC) ? DESC : ASC,
2126
 
            sorter = column.get("sortFn");
2127
 
        if(column.get("sortable")) {
2128
 
            dt.get("recordset").sort.sort(field, dir === DESC, sorter);
2129
 
            this.set("lastSortedBy", {key: key, dir: dir});
2130
 
        }
2131
 
    }
2132
 
});
2133
 
 
2134
 
Y.namespace("Plugin").DataTableSort = DataTableSort;
2135
 
 
2136
 
 
2137
 
 
2138
 
 
2139
 
 
2140
 
 
2141
 
}, '3.3.0' ,{lang:['en'], requires:['datatable-base','plugin','recordset-sort']});
2142
 
 
2143
 
YUI.add('datatable-scroll', function(Y) {
2144
 
 
2145
 
/**
2146
 
 * Extends DataTable base to enable x,y, and xy scrolling.
2147
 
 * @module datatable
2148
 
 * @submodule datatable-scroll
2149
 
 */
2150
 
 
2151
 
 
2152
 
var YNode = Y.Node,
2153
 
        YLang = Y.Lang,
2154
 
        YUA = Y.UA,
2155
 
        YgetClassName = Y.ClassNameManager.getClassName,
2156
 
        DATATABLE = "datatable",
2157
 
        CLASS_HEADER = YgetClassName(DATATABLE, "hd"),
2158
 
        CLASS_BODY = YgetClassName(DATATABLE, "bd"),
2159
 
        CLASS_SCROLLABLE = YgetClassName(DATATABLE, "scrollable"),
2160
 
        CONTAINER_HEADER = '<div class="'+CLASS_HEADER+'"></div>',
2161
 
        CONTAINER_BODY = '<div class="'+CLASS_BODY+'"></div>',
2162
 
        TEMPLATE_TABLE = '<table></table>';
2163
 
        
2164
 
/**
2165
 
 * Adds scrolling to DataTable.
2166
 
 * @class DataTableScroll
2167
 
 * @extends Plugin.Base
2168
 
 */
2169
 
function DataTableScroll() {
2170
 
    DataTableScroll.superclass.constructor.apply(this, arguments);
2171
 
}
2172
 
 
2173
 
Y.mix(DataTableScroll, {
2174
 
    NS: "scroll",
2175
 
 
2176
 
    NAME: "dataTableScroll",
2177
 
 
2178
 
    ATTRS: {
2179
 
        
2180
 
                /**
2181
 
            * @description The width for the table. Set to a string (ex: "200px", "20em") if you want the table to scroll in the x direction.
2182
 
            *
2183
 
            * @attribute width
2184
 
            * @public
2185
 
            * @type string
2186
 
            */
2187
 
        width: {
2188
 
                        value: undefined,
2189
 
                        writeOnce: "initOnly"
2190
 
                },
2191
 
                
2192
 
                /**
2193
 
            * @description The height for the table. Set to a string (ex: "200px", "20em") if you want the table to scroll in the y-direction.
2194
 
            *
2195
 
            * @attribute height
2196
 
            * @public
2197
 
            * @type string
2198
 
            */
2199
 
                height: {
2200
 
                        value: undefined,
2201
 
                        writeOnce: "initOnly"
2202
 
                },
2203
 
                
2204
 
                
2205
 
                /**
2206
 
            * @description The scrolling direction for the table.
2207
 
            *
2208
 
            * @attribute scroll
2209
 
            * @private
2210
 
            * @type string
2211
 
            */
2212
 
                _scroll: {
2213
 
                        //value: 'y',
2214
 
                        valueFn: function() {
2215
 
                            var w = this.get('width'),
2216
 
                            h = this.get('height');
2217
 
                            
2218
 
                            if (w && h) {
2219
 
                                return 'xy';
2220
 
                            }
2221
 
                            else if (w) {
2222
 
                                return 'x';
2223
 
                            }
2224
 
                            else if (h) {
2225
 
                                return 'y';
2226
 
                            }
2227
 
                            else {
2228
 
                                return null;
2229
 
                            }
2230
 
                        }
2231
 
                },
2232
 
                
2233
 
                
2234
 
                /**
2235
 
            * @description The hexadecimal colour value to set on the top-right of the table if a scrollbar exists. 
2236
 
            *
2237
 
            * @attribute COLOR_COLUMNFILLER
2238
 
            * @public
2239
 
            * @type string
2240
 
            */
2241
 
                COLOR_COLUMNFILLER: {
2242
 
                        value: '#f2f2f2',
2243
 
                        validator: YLang.isString,
2244
 
                        setter: function(param) {
2245
 
                                if (this._headerContainerNode) {
2246
 
                                        this._headerContainerNode.setStyle('backgroundColor', param);
2247
 
                                }
2248
 
                        }
2249
 
                }
2250
 
    }
2251
 
});
2252
 
 
2253
 
Y.extend(DataTableScroll, Y.Plugin.Base, {
2254
 
        
2255
 
        /**
2256
 
    * @description The table node created in datatable-base
2257
 
    *
2258
 
    * @property _parentTableNode
2259
 
        * @private
2260
 
    * @type Y.Node
2261
 
    */
2262
 
        _parentTableNode: null,
2263
 
        
2264
 
        
2265
 
        /**
2266
 
    * @description The THEAD node which resides within the table node created in datatable-base
2267
 
    *
2268
 
    * @property _parentTheadNode
2269
 
        * @private
2270
 
    * @type Y.Node
2271
 
    */
2272
 
        _parentTheadNode: null,
2273
 
        
2274
 
        
2275
 
        /**
2276
 
    * @description The TBODY node which resides within the table node created in datatable-base
2277
 
    *
2278
 
    * @property _parentTbodyNode
2279
 
        * @private
2280
 
    * @type Y.Node
2281
 
    */
2282
 
        _parentTbodyNode: null,
2283
 
        
2284
 
        
2285
 
        /**
2286
 
    * @description The TBODY Message node which resides within the table node created in datatable-base
2287
 
    *
2288
 
    * @property _parentMsgNode
2289
 
        * @private
2290
 
    * @type Y.Node
2291
 
    */
2292
 
        _parentMsgNode: null,
2293
 
        
2294
 
        
2295
 
        /**
2296
 
    * @description The contentBox specified for the datatable in datatable-base
2297
 
    *
2298
 
    * @property _parentContainer
2299
 
        * @private
2300
 
    * @type Y.Node
2301
 
    */
2302
 
        _parentContainer: null,
2303
 
        
2304
 
        
2305
 
        /**
2306
 
    * @description The DIV node that contains all the scrollable elements (a table with a tbody on it)
2307
 
    *
2308
 
    * @property _bodyContainerNode
2309
 
        * @private
2310
 
    * @type Y.Node
2311
 
    */
2312
 
        _bodyContainerNode: null,
2313
 
        
2314
 
        
2315
 
        /**
2316
 
    * @description The DIV node that contains a table with a THEAD in it (which syncs its horizontal scroll with the _bodyContainerNode above)
2317
 
    *
2318
 
    * @property _headerContainerNode
2319
 
        * @private
2320
 
    * @type Y.Node
2321
 
    */
2322
 
        _headerContainerNode: null,
2323
 
        
2324
 
        
2325
 
        //--------------------------------------
2326
 
    //  Methods
2327
 
    //--------------------------------------
2328
 
 
2329
 
 
2330
 
        
2331
 
        initializer: function(config) {
2332
 
        var dt = this.get("host");
2333
 
                this._parentContainer = dt.get('contentBox');
2334
 
                this._parentContainer.addClass(CLASS_SCROLLABLE);
2335
 
                this._setUpNodes();
2336
 
        },
2337
 
        
2338
 
        /////////////////////////////////////////////////////////////////////////////
2339
 
        //
2340
 
        // Set up Table Nodes
2341
 
        //
2342
 
        /////////////////////////////////////////////////////////////////////////////
2343
 
        
2344
 
        /**
2345
 
    * @description Set up methods to fire after host methods execute
2346
 
    *
2347
 
    * @method _setUpNodes
2348
 
    * @private
2349
 
    */                  
2350
 
        _setUpNodes: function() {
2351
 
                
2352
 
                this.afterHostMethod("_addTableNode", this._setUpParentTableNode);
2353
 
                this.afterHostMethod("_addTheadNode", this._setUpParentTheadNode); 
2354
 
                this.afterHostMethod("_addTbodyNode", this._setUpParentTbodyNode);
2355
 
                this.afterHostMethod("_addMessageNode", this._setUpParentMessageNode);
2356
 
                //this.beforeHostMethod('renderUI', this._removeCaptionNode);
2357
 
                this.afterHostMethod("renderUI", this.renderUI);
2358
 
                this.afterHostMethod("syncUI", this.syncUI);
2359
 
                
2360
 
                if (this.get('_scroll') !== 'x') {
2361
 
                        this.afterHostMethod('_attachTheadThNode', this._attachTheadThNode);
2362
 
                        this.afterHostMethod('_attachTbodyTdNode', this._attachTbodyTdNode);
2363
 
                }
2364
 
                
2365
 
        },
2366
 
                
2367
 
        /**
2368
 
    * @description Stores the main &lt;table&gt; node provided by the host as a private property
2369
 
    *
2370
 
    * @method _setUpParentTableNode
2371
 
    * @private
2372
 
    */
2373
 
        _setUpParentTableNode: function() {
2374
 
                this._parentTableNode = this.get('host')._tableNode;
2375
 
        },
2376
 
        
2377
 
        
2378
 
        /**
2379
 
    * @description Stores the main &lt;thead&gt; node provided by the host as a private property
2380
 
    *
2381
 
    * @method _setUpParentTheadNode
2382
 
    * @private
2383
 
    */
2384
 
        _setUpParentTheadNode: function() {
2385
 
                this._parentTheadNode = this.get('host')._theadNode;
2386
 
        },
2387
 
        
2388
 
        /**
2389
 
    * @description Stores the main &lt;tbody&gt; node provided by the host as a private property
2390
 
    *
2391
 
    * @method _setUpParentTbodyNode
2392
 
    * @private
2393
 
    */
2394
 
        _setUpParentTbodyNode: function() {
2395
 
                this._parentTbodyNode = this.get('host')._tbodyNode;
2396
 
        },
2397
 
        
2398
 
        
2399
 
        /**
2400
 
    * @description Stores the main &lt;tbody&gt; message node provided by the host as a private property
2401
 
    *
2402
 
    * @method _setUpParentMessageNode
2403
 
    * @private
2404
 
    */
2405
 
        _setUpParentMessageNode: function() {
2406
 
                this._parentMsgNode = this.get('host')._msgNode;
2407
 
        },
2408
 
        
2409
 
        /////////////////////////////////////////////////////////////////////////////
2410
 
        //
2411
 
        // Renderer
2412
 
        //
2413
 
        /////////////////////////////////////////////////////////////////////////////
2414
 
        
2415
 
        /**
2416
 
    * @description Primary rendering method that takes the datatable rendered in
2417
 
    * the host, and splits it up into two separate &lt;divs&gt; each containing two 
2418
 
        * separate tables (one containing the head and one containing the body). 
2419
 
        * This method fires after renderUI is called on datatable-base.
2420
 
        * 
2421
 
    * @method renderUI
2422
 
    * @public
2423
 
    */
2424
 
        renderUI: function() {
2425
 
                //Y.Profiler.start('render');
2426
 
                this._createBodyContainer();
2427
 
                this._createHeaderContainer();
2428
 
                this._setContentBoxDimensions();
2429
 
                //Y.Profiler.stop('render');
2430
 
                //console.log(Y.Profiler.getReport("render"));
2431
 
        },
2432
 
        
2433
 
        
2434
 
        /**
2435
 
    * @description Post rendering method that is responsible for creating a column
2436
 
        * filler, and performing width and scroll synchronization between the &lt;th&gt; 
2437
 
        * elements and the &lt;td&gt; elements.
2438
 
        * This method fires after syncUI is called on datatable-base
2439
 
        * 
2440
 
    * @method syncUI
2441
 
    * @public
2442
 
    */
2443
 
        syncUI: function() {
2444
 
                //Y.Profiler.start('sync');
2445
 
                this._removeCaptionNode();
2446
 
                this._syncWidths();
2447
 
                this._syncScroll();
2448
 
                //Y.Profiler.stop('sync');
2449
 
                //console.log(Y.Profiler.getReport("sync"));
2450
 
                
2451
 
        },
2452
 
        
2453
 
        /**
2454
 
    * @description Remove the caption created in base. Scrolling datatables dont support captions.
2455
 
        * 
2456
 
    * @method _removeCaptionNode
2457
 
    * @private
2458
 
    */
2459
 
    _removeCaptionNode: function() {
2460
 
        this.get('host')._captionNode.remove();
2461
 
        //Y.DataTable.Base.prototype.createCaption = function(v) {/*do nothing*/};
2462
 
                //Y.DataTable.Base.prototype._uiSetCaption = function(v) {/*do nothing*/};
2463
 
    },
2464
 
 
2465
 
        /**
2466
 
    * @description Adjusts the width of the TH and the TDs to make sure that the two are in sync
2467
 
        * 
2468
 
        * Implementation Details: 
2469
 
        *       Compares the width of the TH liner div to the the width of the TD node. The TD liner width
2470
 
        *       is not actually used because the TD often stretches past the liner if the parent DIV is very
2471
 
        *       large. Measuring the TD width is more accurate.
2472
 
        *       
2473
 
        *       Instead of measuring via .get('width'), 'clientWidth' is used, as it returns a number, whereas
2474
 
        *       'width' returns a string, In IE6, 'clientWidth' is not supported, so 'offsetWidth' is used.
2475
 
        *       'offsetWidth' is not as accurate on Chrome,FF as 'clientWidth' - thus the need for the fork.
2476
 
        * 
2477
 
    * @method _syncWidths
2478
 
    * @private
2479
 
    */
2480
 
        _syncWidths: function() {
2481
 
                var th = YNode.all('#'+this._parentContainer.get('id')+' .yui3-datatable-hd table thead th'), //nodelist of all THs
2482
 
                        td = YNode.one('#'+this._parentContainer.get('id')+' .yui3-datatable-bd table .yui3-datatable-data').get('firstChild').get('children'), //nodelist of all TDs in 1st row
2483
 
                        i,
2484
 
                        len,
2485
 
                        thWidth, tdWidth, thLiner, tdLiner,
2486
 
                        ie = YUA.ie;
2487
 
                        //stylesheet = new YStyleSheet('columnsSheet'),
2488
 
                        //className;
2489
 
                        
2490
 
                        /*
2491
 
                        This for loop goes through the first row of TDs in the table.
2492
 
                        In a table, the width of the row is equal to the width of the longest cell in that column.
2493
 
                        Therefore, we can observe the widths of the cells in the first row only, as they will be the same in all the cells below (in each respective column)
2494
 
                        */
2495
 
                        for (i=0, len = th.size(); i<len; i++) { 
2496
 
                                
2497
 
                                //className = '.'+td.item(i).get('classList')._nodes[0];
2498
 
                                //If a width has not been already set on the TD:
2499
 
                                //if (td.item(i).get('firstChild').getStyle('width') === "auto") {
2500
 
                                        
2501
 
                                        //Get the liners for the TH and the TD cell in question
2502
 
                                        thLiner = th.item(i).get('firstChild'); //TODO: use liner API - how? this is a node.
2503
 
                                        tdLiner = td.item(i).get('firstChild');
2504
 
                                        
2505
 
                                        /*
2506
 
                                        If browser is not IE - get the clientWidth of the Liner div and the TD.
2507
 
                                        Note:   We are not getting the width of the TDLiner, we are getting the width of the actual cell.
2508
 
                                                        Why? Because when the table is set to auto width, the cell will grow to try to fit the table in its container.
2509
 
                                                        The liner could potentially be much smaller than the cell width.
2510
 
                                                        
2511
 
                                                        TODO: Explore if there is a better way using only LINERS widths
2512
 
                                        */
2513
 
                                        if (!ie) {
2514
 
                                                thWidth = thLiner.get('clientWidth'); //TODO: this should actually be done with getComputedStyle('width') but this messes up columns. Explore this option.
2515
 
                                                tdWidth = td.item(i).get('clientWidth');
2516
 
                                        }
2517
 
                                        
2518
 
                                        //IE wasn't recognizing clientWidths, so we are using offsetWidths.
2519
 
                                        //TODO: should use getComputedStyle('width') because offsetWidth will screw up when padding is changed.
2520
 
                                        else {
2521
 
                                                thWidth = thLiner.get('offsetWidth');
2522
 
                                                tdWidth = td.item(i).get('offsetWidth');
2523
 
                                                //thWidth = parseFloat(thLiner.getComputedStyle('width').split('px')[0]);
2524
 
                                                //tdWidth = parseFloat(td.item(i).getComputedStyle('width').split('px')[0]); /* TODO: for some reason, using tdLiner.get('clientWidth') doesn't work - why not? */
2525
 
                                        }
2526
 
                                                                                
2527
 
                                        //if TH is bigger than TD, enlarge TD Liner
2528
 
                                        if (thWidth > tdWidth) {
2529
 
                                                tdLiner.setStyle('width', (thWidth - 20 + 'px'));
2530
 
                                                //thLiner.setStyle('width', (tdWidth - 20 + 'px'));
2531
 
                                                //stylesheet.set(className,{'width': (thWidth - 20 + 'px')});
2532
 
                                        }
2533
 
                                        
2534
 
                                        //if TD is bigger than TH, enlarge TH Liner
2535
 
                                        else if (tdWidth > thWidth) {
2536
 
                                                thLiner.setStyle('width', (tdWidth - 20 + 'px'));
2537
 
                                                tdLiner.setStyle('width', (tdWidth - 20 + 'px')); //if you don't set an explicit width here, when the width is set in line 368, it will auto-shrink the widths of the other cells (because they dont have an explicit width)
2538
 
                                                //stylesheet.set(className,{'width': (tdWidth - 20 + 'px')});
2539
 
                                        }
2540
 
                                        
2541
 
                                //}
2542
 
 
2543
 
                        }
2544
 
                        
2545
 
                        //stylesheet.enable();
2546
 
 
2547
 
        },
2548
 
        
2549
 
        /**
2550
 
    * @description Adds the approriate width to the liner divs of the TH nodes before they are appended to DOM
2551
 
        *
2552
 
    * @method _attachTheadThNode
2553
 
    * @private
2554
 
    */
2555
 
        _attachTheadThNode: function(o) {
2556
 
                var w = o.column.get('width') || 'auto';
2557
 
                
2558
 
                if (w !== 'auto') {
2559
 
                        o.th.get('firstChild').setStyles({width: w, overflow:'hidden'}); //TODO: use liner API but liner is undefined here (not created?)
2560
 
                }
2561
 
                return o;
2562
 
        },
2563
 
        
2564
 
        /**
2565
 
    * @description Adds the appropriate width to the liner divs of the TD nodes before they are appended to DOM
2566
 
        *
2567
 
    * @method _attachTbodyTdNode
2568
 
    * @private
2569
 
    */
2570
 
        _attachTbodyTdNode: function(o) {
2571
 
                var w = o.column.get('width') || 'auto';
2572
 
                
2573
 
                if (w !== 'auto') {
2574
 
                        o.td.get('firstChild').setStyles({width: w, overflow: 'hidden'}); //TODO: use liner API but liner is undefined here (not created?)
2575
 
                        //o.td.setStyles({'width': w, 'overflow': 'hidden'});
2576
 
                }
2577
 
                return o;
2578
 
        },
2579
 
        
2580
 
        /**
2581
 
    * @description Creates the body DIV that contains all the data. 
2582
 
        *
2583
 
    * @method _createBodyContainer
2584
 
    * @private
2585
 
    */
2586
 
        _createBodyContainer: function() {
2587
 
                var     bd = YNode.create(CONTAINER_BODY),
2588
 
                        onScrollFn = Y.bind("_onScroll", this);
2589
 
                        
2590
 
                this._bodyContainerNode = bd;           
2591
 
                this._setStylesForTbody();
2592
 
                
2593
 
                bd.appendChild(this._parentTableNode);
2594
 
                this._parentContainer.appendChild(bd);
2595
 
                bd.on('scroll', onScrollFn);
2596
 
        },
2597
 
        
2598
 
        /**
2599
 
    * @description Creates the DIV that contains a &lt;table&gt; with all the headers. 
2600
 
        *
2601
 
    * @method _createHeaderContainer
2602
 
    * @private
2603
 
    */
2604
 
        _createHeaderContainer: function() {
2605
 
                var hd = YNode.create(CONTAINER_HEADER),
2606
 
                        tbl = YNode.create(TEMPLATE_TABLE);
2607
 
                        
2608
 
                this._headerContainerNode = hd;
2609
 
                
2610
 
                //hd.setStyle('backgroundColor',this.get("COLOR_COLUMNFILLER"));
2611
 
                this._setStylesForThead();
2612
 
                tbl.appendChild(this._parentTheadNode);
2613
 
                hd.appendChild(tbl);
2614
 
                this._parentContainer.prepend(hd);
2615
 
                
2616
 
        },
2617
 
        
2618
 
        /**
2619
 
    * @description Creates styles for the TBODY based on what type of table it is.
2620
 
        *
2621
 
    * @method _setStylesForTbody
2622
 
    * @private
2623
 
    */
2624
 
        _setStylesForTbody: function() {
2625
 
                var dir = this.get('_scroll'),
2626
 
                        w = this.get('width') || "",
2627
 
                        h = this.get('height') || "",
2628
 
                        el = this._bodyContainerNode,
2629
 
                        styles = {width:"", height:h};
2630
 
                                
2631
 
                if (dir === 'x') {
2632
 
                        //X-Scrolling tables should not have a Y-Scrollbar so overflow-y is hidden. THe width on x-scrolling tables must be set by user.
2633
 
                        styles.overflowY = 'hidden';
2634
 
                        styles.width = w;
2635
 
                }
2636
 
                else if (dir === 'y') {
2637
 
                        //Y-Scrolling tables should not have a X-Scrollbar so overflow-x is hidden. The width isn't neccessary because it can be auto.
2638
 
                        styles.overflowX = 'hidden';
2639
 
                }
2640
 
                
2641
 
                else if (dir === 'xy') {
2642
 
                        styles.width = w;
2643
 
                }
2644
 
                
2645
 
                else {
2646
 
                    //scrolling is set to 'null' - ie: width and height are not set. Don't have any type of scrolling.
2647
 
                    styles.overflowX = 'hidden';
2648
 
                    styles.overflowY = 'hidden';
2649
 
                    styles.width = w;
2650
 
                }
2651
 
                
2652
 
                el.setStyles(styles);
2653
 
                return el;
2654
 
        },
2655
 
        
2656
 
        
2657
 
        /**
2658
 
    * @description Creates styles for the THEAD based on what type of datatable it is.
2659
 
        *
2660
 
    * @method _setStylesForThead
2661
 
    * @private
2662
 
    */
2663
 
        _setStylesForThead: function() {
2664
 
                var w = this.get('width') || "",
2665
 
                        el = this._headerContainerNode;
2666
 
                
2667
 
                //if (dir !== 'y') {
2668
 
                        el.setStyles({'width': w, 'overflow': 'hidden'});
2669
 
                // }
2670
 
        },
2671
 
        
2672
 
        /**
2673
 
    * @description Sets an auto width on the content box if it doesn't exist or if its a y-datatable.
2674
 
        *
2675
 
    * @method _setContentBoxDimensions
2676
 
    * @private
2677
 
    */
2678
 
        _setContentBoxDimensions: function() {
2679
 
                
2680
 
                if (this.get('_scroll') === 'y' || (!this.get('width'))) {
2681
 
                        this._parentContainer.setStyle('width', 'auto');
2682
 
                }
2683
 
                
2684
 
        },
2685
 
        
2686
 
        /////////////////////////////////////////////////////////////////////////////
2687
 
        //
2688
 
        // Scroll Syncing
2689
 
        //
2690
 
        /////////////////////////////////////////////////////////////////////////////
2691
 
        
2692
 
        /**
2693
 
    * @description Ensures that scrolling is synced across the two tables
2694
 
        *
2695
 
    * @method _onScroll
2696
 
    * @private
2697
 
    */
2698
 
        _onScroll: function() {
2699
 
                this._headerContainerNode.set('scrollLeft', this._bodyContainerNode.get('scrollLeft'));
2700
 
        },
2701
 
        
2702
 
        /**
2703
 
         * @description Syncs padding around scrollable tables, including Column header right-padding
2704
 
         * and container width and height.
2705
 
         *
2706
 
         * @method _syncScroll
2707
 
         * @private 
2708
 
         */
2709
 
        _syncScroll : function() {
2710
 
                this._syncScrollX();
2711
 
                this._syncScrollY();
2712
 
                this._syncScrollOverhang();
2713
 
                if(YUA.opera) {
2714
 
                        // Bug 1925874
2715
 
                        this._headerContainerNode.set('scrollLeft', this._bodyContainerNode.get('scrollLeft'));
2716
 
                        
2717
 
                        if(!this.get("width")) {
2718
 
                                // Bug 1926125
2719
 
                                document.body.style += '';
2720
 
                        }
2721
 
                }
2722
 
        },
2723
 
        
2724
 
        /**
2725
 
        * @description Snaps container width for y-scrolling tables.
2726
 
        *
2727
 
        * @method _syncScrollY
2728
 
        * @private
2729
 
        */
2730
 
        _syncScrollY : function() {
2731
 
                var tBody = this._parentTbodyNode,
2732
 
                    tBodyContainer = this._bodyContainerNode,
2733
 
                        w;
2734
 
                    // X-scrolling not enabled
2735
 
                        if(!this.get("width")) {
2736
 
                        // Snap outer container width to content
2737
 
                        w = (tBodyContainer.get('scrollHeight') > tBodyContainer.get('clientHeight')) ?
2738
 
                        // but account for y-scrollbar since it is visible
2739
 
                                        (tBody.get('parentNode').get('clientWidth') + 19) + "px" :
2740
 
                                // no y-scrollbar, just borders
2741
 
                        (tBody.get('parentNode').get('clientWidth') + 2) + "px";
2742
 
                                this._parentContainer.setStyle('width', w);
2743
 
                }
2744
 
        },
2745
 
                
2746
 
        /**
2747
 
         * @description Snaps container height for x-scrolling tables in IE. Syncs message TBODY width. 
2748
 
         * Taken from YUI2 ScrollingDataTable.js
2749
 
         *
2750
 
         * @method _syncScrollX
2751
 
         * @private
2752
 
         */
2753
 
        _syncScrollX: function() {
2754
 
                var tBody = this._parentTbodyNode,
2755
 
                        tBodyContainer = this._bodyContainerNode,
2756
 
                        w;
2757
 
                        this._headerContainerNode.set('scrollLeft', this._bodyContainerNode.get('scrollLeft'));
2758
 
                        
2759
 
                        if(!this.get('height') && (YUA.ie)) {
2760
 
                                                w = (tBodyContainer.get('scrollWidth') > tBodyContainer.get('offsetWidth')) ?
2761
 
                                    (tBody.get('parentNode').get('offsetHeight') + 18) + "px" : 
2762
 
                                    tBody.get('parentNode').get('offsetHeight') + "px";
2763
 
                                                
2764
 
                                                tBodyContainer.setStyle('height', w);
2765
 
                                        }
2766
 
                        
2767
 
                if (tBody.get('rows').length === 0) {
2768
 
                        this._parentMsgNode.get('parentNode').setStyle('width', this._parentTheadNode.get('parentNode').get('offsetWidth')+'px');
2769
 
                }
2770
 
                else {
2771
 
                        this._parentMsgNode.get('parentNode').setStyle('width', "");
2772
 
                }
2773
 
                        
2774
 
        },
2775
 
        
2776
 
        /**
2777
 
         * @description Adds/removes Column header overhang as necesary.
2778
 
         * Taken from YUI2 ScrollingDataTable.js
2779
 
         *
2780
 
         * @method _syncScrollOverhang
2781
 
         * @private
2782
 
         */
2783
 
        _syncScrollOverhang: function() {
2784
 
                var tBodyContainer = this._bodyContainerNode,
2785
 
                        padding = 1;
2786
 
                
2787
 
                //when its both x and y scrolling
2788
 
                if ((tBodyContainer.get('scrollHeight') > tBodyContainer.get('clientHeight')) || (tBodyContainer.get('scrollWidth') > tBodyContainer.get('clientWidth'))) {
2789
 
                        padding = 18;
2790
 
                }
2791
 
                
2792
 
                this._setOverhangValue(padding);
2793
 
                
2794
 
                //After the widths have synced, there is a wrapping issue in the headerContainer in IE6. The header does not span the full
2795
 
                //length of the table (does not cover all of the y-scrollbar). By adding this line in when there is a y-scroll, the header will span correctly.
2796
 
                //TODO: this should not really occur on this.get('_scroll') === y - it should occur when scrollHeight > clientHeight, but clientHeight is not getting recognized in IE6?
2797
 
                if (YUA.ie !== 0 && this.get('_scroll') === 'y' && this._bodyContainerNode.get('scrollHeight') > this._bodyContainerNode.get('offsetHeight'))
2798
 
                {
2799
 
                        this._headerContainerNode.setStyle('width', this._parentContainer.get('width'));
2800
 
                }
2801
 
        },
2802
 
        
2803
 
        
2804
 
        /**
2805
 
         * @description Sets Column header overhang to given width.
2806
 
         * Taken from YUI2 ScrollingDataTable.js with minor modifications
2807
 
         *
2808
 
         * @method _setOverhangValue
2809
 
         * @param nBorderWidth {Number} Value of new border for overhang. 
2810
 
         * @private
2811
 
         */ 
2812
 
        _setOverhangValue: function(borderWidth) {
2813
 
                var host = this.get('host'),
2814
 
                        cols = host.get('columnset').get('definitions'),
2815
 
                        //lastHeaders = cols[cols.length-1] || [],
2816
 
                len = cols.length,
2817
 
                value = borderWidth + "px solid " + this.get("COLOR_COLUMNFILLER"),
2818
 
                        children = YNode.all('#'+this._parentContainer.get('id')+ ' .' + CLASS_HEADER + ' table thead th');
2819
 
 
2820
 
                children.item(len-1).setStyle('borderRight', value);
2821
 
        }
2822
 
        
2823
 
});
2824
 
 
2825
 
Y.namespace("Plugin").DataTableScroll = DataTableScroll;
2826
 
 
2827
 
 
2828
 
 
2829
 
 
2830
 
 
2831
 
 
2832
 
}, '3.3.0' ,{requires:['datatable-base','plugin','stylesheet']});
2833
 
 
2834
 
 
2835
 
 
2836
 
YUI.add('datatable', function(Y){}, '3.3.0' ,{use:['datatable-base','datatable-datasource','datatable-sort','datatable-scroll']});
2837