2
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.com/yui/license.html
8
YUI.add('datatable-base', function(Y) {
11
YisValue = YLang.isValue,
12
Ysubstitute = Y.Lang.substitute,
14
Ycreate = YNode.create,
15
YgetClassName = Y.ClassNameManager.getClassName,
17
DATATABLE = "datatable",
22
MOUSEENTER = "mouseenter",
23
MOUSELEAVE = "mouseleave",
25
MOUSEDOWN = "mousedown",
27
DBLCLICK = "dblclick",
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"),
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>';
52
* The Column class defines and manages attributes of Columns for DataTable.
58
function Column(config) {
59
Column.superclass.constructor.apply(this, arguments);
62
/////////////////////////////////////////////////////////////////////////////
66
/////////////////////////////////////////////////////////////////////////////
79
/////////////////////////////////////////////////////////////////////////////
83
/////////////////////////////////////////////////////////////////////////////
87
* @description Unique internal identifier, used to stamp ID on TH element.
92
valueFn: "_defaultId",
98
* @description User-supplied identifier. Defaults to id.
102
valueFn: "_defaultKey"
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.
114
valueFn: "_defaultField"
119
* @description Display label for column header. Defaults to key.
123
valueFn: "_defaultLabel"
127
* @attribute children
128
* @description Array of child column definitions (for nested headers).
137
* @description TH abbr attribute.
144
//TODO: support custom classnames
148
getter: "_getClassnames"
154
//requires datatable-sort
158
//sortOptions:defaultDir, sortFn, field
160
//TODO: support editable columns
164
//TODO: support resizeable columns
165
//TODO: support setting widths
166
// requires datatable-colresize
175
/////////////////////////////////////////////////////////////////////////////
179
/////////////////////////////////////////////////////////////////////////////
180
Y.extend(Column, Y.Widget, {
181
/////////////////////////////////////////////////////////////////////////////
185
/////////////////////////////////////////////////////////////////////////////
188
* @description Return ID for instance.
192
_defaultId: function() {
197
* @method _defaultKey
198
* @description Return key for instance. Defaults to ID if one was not
203
_defaultKey: function(key) {
204
return key || Y.guid();
208
* @method _defaultField
209
* @description Return field for instance. Defaults to key if one was not
214
_defaultField: function(field) {
215
return field || this.get("key");
219
* @method _defaultLabel
220
* @description Return label for instance. Defaults to key if one was not
225
_defaultLabel: function(label) {
226
return label || this.get("key");
230
* Updates the UI if changes are made to abbr.
232
* @method _afterAbbrChange
233
* @param e {Event} Custom event for the attribute change.
236
_afterAbbrChange: function (e) {
237
this._uiSetAbbr(e.newVal);
240
/////////////////////////////////////////////////////////////////////////////
244
/////////////////////////////////////////////////////////////////////////////
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.
257
* @description Array of TH IDs associated with this column, for TD "headers"
258
* attribute. Value is set by Columnset code
264
* Number of cells the header spans. Value is set by Columnset code.
273
* Number of rows the header spans. Value is set by Columnset code.
282
* Column's parent Column instance, if applicable. Value is set by Columnset
291
* The Node reference to the associated TH element.
300
* The Node reference to the associated liner element.
302
* @property thLinerNode
307
/////////////////////////////////////////////////////////////////////////////
311
/////////////////////////////////////////////////////////////////////////////
315
* @method initializer
316
* @param config {Object} Config object.
319
initializer: function(config) {
328
destructor: function() {
332
* Returns classnames for Column.
334
* @method _getClassnames
337
_getClassnames: function () {
338
return Y.ClassNameManager.getClassName(COLUMN, this.get("id"));
342
if(lang.isString(oColumn.className)) {
343
// Single custom class
344
allClasses = [oColumn.className];
346
else if(lang.isArray(oColumn.className)) {
347
// Array of custom classes
348
allClasses = oColumn.className;
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();
358
// Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
359
allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
361
var isSortedBy = this.get("sortedBy") || {};
363
if(oColumn.key === isSortedBy.key) {
364
allClasses[allClasses.length] = isSortedBy.dir || '';
368
allClasses[allClasses.length] = DT.CLASS_HIDDEN;
371
if(oColumn.selected) {
372
allClasses[allClasses.length] = DT.CLASS_SELECTED;
375
if(oColumn.sortable) {
376
allClasses[allClasses.length] = DT.CLASS_SORTABLE;
379
if(oColumn.resizeable) {
380
allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
384
allClasses[allClasses.length] = DT.CLASS_EDITABLE;
387
// Addtnl classes, including First/Last
389
allClasses = allClasses.concat(aAddClasses);
392
return allClasses.join(' ');*/
395
////////////////////////////////////////////////////////////////////////////
399
////////////////////////////////////////////////////////////////////////////
401
* Syncs UI to intial state.
407
this._uiSetAbbr(this.get("abbr"));
414
* @param val {String} New abbr.
417
_uiSetAbbr: function(val) {
418
this.thNode.set("abbr", val);
425
* The Columnset class defines and manages a collection of Columns.
431
function Columnset(config) {
432
Columnset.superclass.constructor.apply(this, arguments);
435
/////////////////////////////////////////////////////////////////////////////
439
/////////////////////////////////////////////////////////////////////////////
452
/////////////////////////////////////////////////////////////////////////////
456
/////////////////////////////////////////////////////////////////////////////
459
* @attribute definitions
460
* @description Array of column definitions that will populate this Columnset.
464
setter: "_setDefinitions"
470
/////////////////////////////////////////////////////////////////////////////
474
/////////////////////////////////////////////////////////////////////////////
475
Y.extend(Columnset, Y.Base, {
476
/////////////////////////////////////////////////////////////////////////////
480
/////////////////////////////////////////////////////////////////////////////
482
* @method _setDefinitions
483
* @description Clones definitions before setting.
484
* @param definitions {Array} Array of column definitions.
488
_setDefinitions: function(definitions) {
489
return Y.clone(definitions);
492
/////////////////////////////////////////////////////////////////////////////
496
/////////////////////////////////////////////////////////////////////////////
498
* Top-down tree representation of Column hierarchy. Used to create DOM
507
* Hash of all Columns by ID.
515
* Hash of all Columns by key.
523
* Array of only Columns that are meant to be displayed in DOM.
530
/////////////////////////////////////////////////////////////////////////////
534
/////////////////////////////////////////////////////////////////////////////
536
* Initializer. Generates all internal representations of the collection of
539
* @method initializer
540
* @param config {Object} Config object.
543
initializer: function() {
545
// DOM tree representation of all Columns
547
// Hash of all Columns by ID
549
// Hash of all Columns by key
551
// Flat representation of only Columns that are meant to display data
553
// Original definitions
554
definitions = this.get("definitions"),
558
// Internal recursive function to define Column instances
559
function parseColumns(depth, currentDefinitions, parent) {
561
len = currentDefinitions.length,
569
// Create corresponding dom node if not already there for this depth
574
// Parse each node at this depth for attributes and any children
576
currentDefinition = currentDefinitions[i];
578
currentDefinition = YLang.isString(currentDefinition) ? {key:currentDefinition} : currentDefinition;
580
// Instantiate a new Column for each node
581
column = new Y.Column(currentDefinition);
583
// Cross-reference Column ID back to the original object literal definition
584
currentDefinition.yuiColumnId = column.get("id");
586
// Add the new Column to the hash
587
idHash[column.get("id")] = column;
588
keyHash[column.get("key")] = column;
590
// Assign its parent as an attribute, if applicable
592
column.parent = parent;
595
// The Column has descendants
596
if(YLang.isArray(currentDefinition.children)) {
597
currentChildren = currentDefinition.children;
598
column._set("children", currentChildren);
600
self._setColSpans(column, currentDefinition);
602
self._cascadePropertiesToChildren(column, currentChildren);
604
// The children themselves must also be parsed for Column instances
608
parseColumns(depth, currentChildren, column);
610
// This Column does not have any children
612
column.keyIndex = keys.length;
613
// Default is already 1
614
//column.colSpan = 1;
618
// Add the Column to the top-down dom tree
619
tree[depth].push(column);
624
// Parse out Column instances from the array of object literals
625
parseColumns(-1, definitions);
628
// Save to the Columnset instance
630
this.idHash = idHash;
631
this.keyHash = keyHash;
644
destructor: function() {
647
/////////////////////////////////////////////////////////////////////////////
651
/////////////////////////////////////////////////////////////////////////////
653
* Cascade certain properties to children if not defined on their own.
655
* @method _cascadePropertiesToChildren
658
_cascadePropertiesToChildren: function(column, currentChildren) {
659
//TODO: this is all a giant todo
661
len = currentChildren.length,
664
// Cascade certain properties to children if not defined on their own
666
child = currentChildren[i];
667
if(column.get("className") && (child.className === undefined)) {
668
child.className = column.get("className");
670
if(column.get("editor") && (child.editor === undefined)) {
671
child.editor = column.get("editor");
673
if(column.get("formatter") && (child.formatter === undefined)) {
674
child.formatter = column.get("formatter");
676
if(column.get("resizeable") && (child.resizeable === undefined)) {
677
child.resizeable = column.get("resizeable");
679
if(column.get("sortable") && (child.sortable === undefined)) {
680
child.sortable = column.get("sortable");
682
if(column.get("hidden")) {
685
if(column.get("width") && (child.width === undefined)) {
686
child.width = column.get("width");
688
if(column.get("minWidth") && (child.minWidth === undefined)) {
689
child.minWidth = column.get("minWidth");
691
if(column.get("maxAutoWidth") && (child.maxAutoWidth === undefined)) {
692
child.maxAutoWidth = column.get("maxAutoWidth");
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.
704
_setColSpans: function(column, definition) {
705
// Determine COLSPAN value for this Column
706
var terminalChildNodes = 0;
708
function countTerminalChildNodes(ancestor) {
709
var descendants = ancestor.children,
711
len = descendants.length;
713
// Drill down each branch and count terminal nodes
715
// Keep drilling down
716
if(YLang.isArray(descendants[i].children)) {
717
countTerminalChildNodes(descendants[i]);
719
// Reached branch terminus
721
terminalChildNodes++;
725
countTerminalChildNodes(definition);
726
column.colSpan = terminalChildNodes;
730
* @method _setRowSpans
731
* @description Calculates and sets rowSpan attribute on all Columns.
734
_setRowSpans: function() {
735
// Determine ROWSPAN value for each Column in the DOM tree
736
function parseDomTreeForRowSpan(tree) {
742
// Calculate the max depth of descendants for this row
743
function countMaxRowDepth(row, tmpRowDepth) {
744
tmpRowDepth = tmpRowDepth || 1;
752
// Column has children, so keep counting
753
if(YLang.isArray(col.children)) {
755
countMaxRowDepth(col.children, tmpRowDepth);
758
// Column has children, so keep counting
759
else if(col.get && YLang.isArray(col.get("children"))) {
761
countMaxRowDepth(col.get("children"), tmpRowDepth);
764
// No children, is it the max depth?
766
if(tmpRowDepth > maxRowDepth) {
767
maxRowDepth = tmpRowDepth;
773
// Count max row depth for each row
774
for(m=0; m<tree.length; m++) {
775
currentRow = tree[m];
776
countMaxRowDepth(currentRow);
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;
784
// Default is already 1
785
// else currentColumn.rowSpan =1;
788
// Reset counter for next row
792
parseDomTreeForRowSpan(this.tree);
796
* @method _setHeaders
797
* @description Calculates and sets headers attribute on all Columns.
800
_setHeaders: function() {
803
i=0, len = allKeys.length;
805
function recurseAncestorsForHeaders(headers, column) {
806
headers.push(column.get("id"));
808
recurseAncestorsForHeaders(headers, column.parent);
814
recurseAncestorsForHeaders(headers, column);
815
column.headers = headers.reverse().join(" ");
820
getColumn: function() {
824
Y.Columnset = Columnset;
827
* The DataTable widget provides a progressively enhanced DHTML control for
828
* displaying tabular data across A-grade browsers.
834
* Provides the base DataTable implementation, which can be extended to add
835
* additional functionality, such as sorting or scrolling.
838
* @submodule datatable-base
842
* Base class for the DataTable widget.
843
* @class DataTable.Base
847
function DTBase(config) {
848
DTBase.superclass.constructor.apply(this, arguments);
851
/////////////////////////////////////////////////////////////////////////////
855
/////////////////////////////////////////////////////////////////////////////
869
/////////////////////////////////////////////////////////////////////////////
873
/////////////////////////////////////////////////////////////////////////////
876
* @attribute columnset
877
* @description Pointer to Columnset instance.
878
* @type Array | Y.Columnset
881
setter: "_setColumnset"
885
* @attribute recordset
886
* @description Pointer to Recordset instance.
887
* @type Array | Y.Recordset
890
value: new Y.Recordset({records:[]}),
891
setter: "_setRecordset"
896
* @description Internal state.
901
value: new Y.State(),
908
* @description Summary.
916
* @description Caption
923
* @attribute thValueTemplate
924
* @description Tokenized markup template for TH value.
929
value: TEMPLATE_VALUE
933
* @attribute tdValueTemplate
934
* @description Tokenized markup template for TD value.
939
value: TEMPLATE_VALUE
943
* @attribute trTemplate
944
* @description Tokenized markup template for TR node creation.
946
* @default '<tr id="{id}"></tr>'
953
/////////////////////////////////////////////////////////////////////////////
957
/////////////////////////////////////////////////////////////////////////////
959
/*caption: function (srcNode) {
965
/////////////////////////////////////////////////////////////////////////////
969
/////////////////////////////////////////////////////////////////////////////
970
Y.extend(DTBase, Y.Widget, {
972
* @property thTemplate
973
* @description Tokenized markup template for TH node creation.
975
* @default '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}" abbr="{abbr}"><div class="'+CLASS_LINER+'">{value}</div></th>'
977
thTemplate: TEMPLATE_TH,
980
* @property tdTemplate
981
* @description Tokenized markup template for TD node creation.
983
* @default '<td headers="{headers}"><div class="'+CLASS_LINER+'">{value}</div></td>'
985
tdTemplate: TEMPLATE_TD,
988
* @property _theadNode
989
* @description Pointer to THEAD node.
996
* @property _tbodyNode
997
* @description Pointer to TBODY node.
1004
* @property _msgNode
1005
* @description Pointer to message display node.
1011
/////////////////////////////////////////////////////////////////////////////
1013
// ATTRIBUTE HELPERS
1015
/////////////////////////////////////////////////////////////////////////////
1017
* @method _setColumnset
1018
* @description Converts Array to Y.Columnset.
1019
* @param columns {Array | Y.Columnset}
1020
* @returns Y.Columnset
1023
_setColumnset: function(columns) {
1024
return YLang.isArray(columns) ? new Y.Columnset({definitions:columns}) : columns;
1028
* Updates the UI if Columnset is changed.
1030
* @method _afterColumnsetChange
1031
* @param e {Event} Custom event for the attribute change.
1034
_afterColumnsetChange: function (e) {
1035
if(this.get("rendered")) {
1036
this._uiSetColumnset(e.newVal);
1041
* @method _setRecordset
1042
* @description Converts Array to Y.Recordset.
1043
* @param records {Array | Y.Recordset}
1044
* @returns Y.Recordset
1047
_setRecordset: function(rs) {
1048
if(YLang.isArray(rs)) {
1049
rs = new Y.Recordset({records:rs});
1057
* Updates the UI if Recordset is changed.
1059
* @method _afterRecordsetChange
1060
* @param e {Event} Custom event for the attribute change.
1063
_afterRecordsetChange: function (e) {
1064
if(this.get("rendered")) {
1065
this._uiSetRecordset(e.newVal);
1070
* Updates the UI if summary is changed.
1072
* @method _afterSummaryChange
1073
* @param e {Event} Custom event for the attribute change.
1076
_afterSummaryChange: function (e) {
1077
if(this.get("rendered")) {
1078
this._uiSetSummary(e.newVal);
1083
* Updates the UI if caption is changed.
1085
* @method _afterCaptionChange
1086
* @param e {Event} Custom event for the attribute change.
1089
_afterCaptionChange: function (e) {
1090
if(this.get("rendered")) {
1091
this._uiSetCaption(e.newVal);
1095
/////////////////////////////////////////////////////////////////////////////
1099
/////////////////////////////////////////////////////////////////////////////
1103
* @method initializer
1104
* @param config {Object} Config object.
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);
1117
* @method destructor
1120
destructor: function() {
1121
this.get("recordset").removeTarget(this);
1124
////////////////////////////////////////////////////////////////////////////
1128
////////////////////////////////////////////////////////////////////////////
1136
renderUI: function() {
1138
return (this._addTableNode(this.get("contentBox")) &&
1140
this._addColgroupNode(this._tableNode) &&
1142
this._addTheadNode(this._tableNode) &&
1144
this._addTbodyNode(this._tableNode) &&
1146
this._addMessageNode(this._tableNode) &&
1148
this._addCaptionNode(this._tableNode));
1152
* Creates and attaches TABLE element to given container.
1154
* @method _addTableNode
1155
* @param containerNode {Y.Node} Parent node.
1159
_addTableNode: function(containerNode) {
1160
if (!this._tableNode) {
1161
this._tableNode = containerNode.appendChild(Ycreate(TEMPLATE_TABLE));
1163
return this._tableNode;
1167
* Creates and attaches COLGROUP element to given TABLE.
1169
* @method _addColgroupNode
1170
* @param tableNode {Y.Node} Parent node.
1174
_addColgroupNode: function(tableNode) {
1175
// Add COLs to DOCUMENT FRAGMENT
1176
var len = this.get("columnset").keys.length,
1178
allCols = ["<colgroup>"];
1181
allCols.push(TEMPLATE_COL);
1184
allCols.push("</colgroup>");
1187
this._colgroupNode = tableNode.insertBefore(Ycreate(allCols.join("")), tableNode.get("firstChild"));
1189
return this._colgroupNode;
1193
* Creates and attaches THEAD element to given container.
1195
* @method _addTheadNode
1196
* @param tableNode {Y.Node} Parent node.
1200
_addTheadNode: function(tableNode) {
1202
this._theadNode = tableNode.insertBefore(Ycreate(TEMPLATE_THEAD), this._colgroupNode.next());
1203
return this._theadNode;
1208
* Creates and attaches TBODY element to given container.
1210
* @method _addTbodyNode
1211
* @param tableNode {Y.Node} Parent node.
1215
_addTbodyNode: function(tableNode) {
1216
this._tbodyNode = tableNode.appendChild(Ycreate(TEMPLATE_TBODY));
1217
return this._tbodyNode;
1221
* Creates and attaches message display element to given container.
1223
* @method _addMessageNode
1224
* @param tableNode {Y.Node} Parent node.
1228
_addMessageNode: function(tableNode) {
1229
this._msgNode = tableNode.insertBefore(Ycreate(TEMPLATE_MSG), this._tbodyNode);
1230
return this._msgNode;
1234
* Creates and attaches CAPTION element to given container.
1236
* @method _addCaptionNode
1237
* @param tableNode {Y.Node} Parent node.
1241
_addCaptionNode: function(tableNode) {
1242
this._captionNode = tableNode.createCaption();
1243
return this._captionNode;
1246
////////////////////////////////////////////////////////////////////////////
1250
////////////////////////////////////////////////////////////////////////////
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";
1264
delegate: function(type) {
1265
//TODO: is this necessary?
1266
if(type==="dblclick") {
1267
this.get("boundingBox").delegate.apply(this.get("boundingBox"), arguments);
1270
this.get("contentBox").delegate.apply(this.get("contentBox"), arguments);
1275
////////////////////////////////////////////////////////////////////////////
1279
////////////////////////////////////////////////////////////////////////////
1282
* Syncs UI to intial state.
1287
syncUI: function() {
1289
this._uiSetColumnset(this.get("columnset"));
1291
this._uiSetRecordset(this.get("recordset"));
1293
this._uiSetSummary(this.get("summary"));
1295
this._uiSetCaption(this.get("caption"));
1301
* @method _uiSetSummary
1302
* @param val {String} New summary.
1305
_uiSetSummary: function(val) {
1306
val = YisValue(val) ? val : "";
1307
this._tableNode.set("summary", val);
1313
* @method _uiSetCaption
1314
* @param val {String} New caption.
1317
_uiSetCaption: function(val) {
1318
val = YisValue(val) ? val : "";
1319
this._captionNode.setContent(val);
1323
////////////////////////////////////////////////////////////////////////////
1325
// THEAD/COLUMNSET FUNCTIONALITY
1327
////////////////////////////////////////////////////////////////////////////
1331
* @method _uiSetColumnset
1332
* @param cs {Y.Columnset} New Columnset.
1335
_uiSetColumnset: function(cs) {
1337
thead = this._theadNode,
1340
parent = thead.get("parentNode"),
1341
nextSibling = thead.next();
1343
// Move THEAD off DOM
1346
thead.get("children").remove(true);
1348
// Iterate tree of columns to add THEAD rows
1350
this._addTheadTrNode({thead:thead, columns:tree[i]}, (i === 0), (i === len-1));
1353
// Column helpers needs _theadNode to exist
1354
//this._createColumnHelpers();
1357
// Re-attach THEAD to DOM
1358
parent.insert(thead, nextSibling);
1363
* Creates and attaches header row element.
1365
* @method _addTheadTrNode
1366
* @param o {Object} {thead, columns}.
1367
* @param isFirst {Boolean} Is first row.
1368
* @param isFirst {Boolean} Is last row.
1371
_addTheadTrNode: function(o, isFirst, isLast) {
1372
o.tr = this._createTheadTrNode(o, isFirst, isLast);
1373
this._attachTheadTrNode(o);
1378
* Creates header row element.
1380
* @method _createTheadTrNode
1381
* @param o {Object} {thead, columns}.
1382
* @param isFirst {Boolean} Is first row.
1383
* @param isLast {Boolean} Is last row.
1387
_createTheadTrNode: function(o, isFirst, isLast) {
1388
//TODO: custom classnames
1389
var tr = Ycreate(Ysubstitute(this.get("trTemplate"), o)),
1391
columns = o.columns,
1392
len = columns.length,
1395
// Set FIRST/LAST class
1397
tr.addClass(CLASS_FIRST);
1400
tr.addClass(CLASS_LAST);
1404
column = columns[i];
1405
this._addTheadThNode({value:column.get("label"), column: column, tr:tr});
1412
* Attaches header row element.
1414
* @method _attachTheadTrNode
1415
* @param o {Object} {thead, columns, tr}.
1418
_attachTheadTrNode: function(o) {
1419
o.thead.appendChild(o.tr);
1423
* Creates and attaches header cell element.
1425
* @method _addTheadThNode
1426
* @param o {Object} {value, column, tr}.
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;
1437
* Creates header cell element.
1439
* @method _createTheadThNode
1440
* @param o {Object} {value, column, tr}.
1444
_createTheadThNode: function(o) {
1445
var column = o.column;
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);
1456
// Clear minWidth on hidden Columns
1457
if(column.get("hidden")) {
1458
//this._clearMinWidth(column);
1462
return Ycreate(Ysubstitute(this.thTemplate, o));
1466
* Attaches header cell element.
1468
* @method _attachTheadThNode
1469
* @param o {Object} {value, column, tr}.
1472
_attachTheadThNode: function(o) {
1473
o.tr.appendChild(o.th);
1476
////////////////////////////////////////////////////////////////////////////
1478
// TBODY/RECORDSET FUNCTIONALITY
1480
////////////////////////////////////////////////////////////////////////////
1484
* @method _uiSetRecordset
1485
* @param rs {Y.Recordset} New Recordset.
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(),
1497
// Replace TBODY with a new one
1498
//TODO: split _addTbodyNode into create/attach
1501
newTbody = this._addTbodyNode(this._tableNode);
1503
this._tbodyNode = newTbody;
1506
// Iterate Recordset to use existing TR when possible or add new TR
1508
o.record = rs.getRecord(i);
1510
this._addTbodyTrNode(o); //TODO: sometimes rowindex != recordindex
1514
parent.insert(this._tbodyNode, nextSibling);
1518
* Creates and attaches data row element.
1520
* @method _addTbodyTrNode
1521
* @param o {Object} {tbody, record}
1524
_addTbodyTrNode: function(o) {
1525
var tbody = o.tbody,
1527
o.tr = tbody.one("#"+record.get("id")) || this._createTbodyTrNode(o);
1528
this._attachTbodyTrNode(o);
1532
* Creates data row element.
1534
* @method _createTbodyTrNode
1535
* @param o {Object} {tbody, record}
1539
_createTbodyTrNode: function(o) {
1540
var tr = Ycreate(Ysubstitute(this.get("trTemplate"), {id:o.record.get("id")})),
1542
allKeys = this.get("columnset").keys,
1543
len = allKeys.length;
1548
o.column = allKeys[i];
1549
this._addTbodyTdNode(o);
1556
* Attaches data row element.
1558
* @method _attachTbodyTrNode
1559
* @param o {Object} {tbody, record, tr}.
1562
_attachTbodyTrNode: function(o) {
1563
var tbody = o.tbody,
1566
nextSibling = tbody.get("children").item(index) || null,
1567
isEven = (index%2===0);
1570
tr.replaceClass(CLASS_ODD, CLASS_EVEN);
1573
tr.replaceClass(CLASS_EVEN, CLASS_ODD);
1576
tbody.insertBefore(tr, nextSibling);
1580
* Creates and attaches data cell element.
1582
* @method _addTbodyTdNode
1583
* @param o {Object} {record, column, tr}.
1586
_addTbodyTdNode: function(o) {
1587
o.td = this._createTbodyTdNode(o);
1588
this._attachTbodyTdNode(o);
1592
* Creates data cell element.
1594
* @method _createTbodyTdNode
1595
* @param o {Object} {record, column, tr}.
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));
1609
* Attaches data cell element.
1611
* @method _attachTbodyTdNode
1612
* @param o {Object} {record, column, tr, headers, classnames, value}.
1615
_attachTbodyTdNode: function(o) {
1616
o.tr.appendChild(o.td);
1620
* Returns markup to insert into data cell element.
1622
* @method formatDataCell
1623
* @param @param o {Object} {record, column, tr, headers, classnames}.
1625
formatDataCell: function(o) {
1626
var record = o.record,
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
1639
Y.namespace("DataTable").Base = DTBase;
1643
}, '3.3.0' ,{requires:['recordset-base','widget','substitute','event-mouseenter']});