3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('datatable-base-deprecated', function(Y) {
10 YisValue = YLang.isValue,
11 fromTemplate = Y.Lang.sub,
13 Ycreate = YNode.create,
14 YgetClassName = Y.ClassNameManager.getClassName,
16 DATATABLE = "datatable",
21 MOUSEENTER = "mouseenter",
22 MOUSELEAVE = "mouseleave",
24 MOUSEDOWN = "mousedown",
26 DBLCLICK = "dblclick",
28 CLASS_COLUMNS = YgetClassName(DATATABLE, "columns"),
29 CLASS_DATA = YgetClassName(DATATABLE, "data"),
30 CLASS_MSG = YgetClassName(DATATABLE, "msg"),
31 CLASS_LINER = YgetClassName(DATATABLE, "liner"),
32 CLASS_FIRST = YgetClassName(DATATABLE, "first"),
33 CLASS_LAST = YgetClassName(DATATABLE, "last"),
34 CLASS_EVEN = YgetClassName(DATATABLE, "even"),
35 CLASS_ODD = YgetClassName(DATATABLE, "odd"),
37 TEMPLATE_TABLE = '<table></table>',
38 TEMPLATE_COL = '<col></col>',
39 TEMPLATE_THEAD = '<thead class="'+CLASS_COLUMNS+'"></thead>',
40 TEMPLATE_TBODY = '<tbody class="'+CLASS_DATA+'"></tbody>',
41 TEMPLATE_TH = '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}" abbr="{abbr}"><div class="'+CLASS_LINER+'">{value}</div></th>',
42 TEMPLATE_TR = '<tr id="{id}"></tr>',
43 TEMPLATE_TD = '<td headers="{headers}" class="{classnames}"><div class="'+CLASS_LINER+'">{value}</div></td>',
44 TEMPLATE_VALUE = '{value}',
45 TEMPLATE_MSG = '<tbody class="'+CLASS_MSG+'"></tbody>';
50 * The Column class defines and manages attributes of Columns for DataTable.
56 function Column(config) {
57 Column.superclass.constructor.apply(this, arguments);
60 /////////////////////////////////////////////////////////////////////////////
64 /////////////////////////////////////////////////////////////////////////////
77 /////////////////////////////////////////////////////////////////////////////
81 /////////////////////////////////////////////////////////////////////////////
84 Unique internal identifier, used to stamp ID on TH element.
91 valueFn: "_defaultId",
96 User-supplied identifier. Defaults to id.
101 valueFn: "_defaultKey"
105 Points to underlying data field (for sorting or formatting, for
106 example). Useful when column doesn't hold any data itself, but is just
107 a visual representation of data from another column or record field.
112 @default (column key)
115 valueFn: "_defaultField"
119 Display label for column header. Defaults to key.
125 valueFn: "_defaultLabel"
129 Array of child column definitions (for nested headers).
150 //TODO: support custom classnames
154 getter: "_getClassnames"
158 Formating template string or function for cells in this column.
160 Function formatters receive a single object (described below) and are
161 expected to output the `innerHTML` of the cell.
163 String templates can include markup and {placeholder} tokens to be
164 filled in from the object passed to function formatters.
167 @type {String|Function}
168 @param {Object} data Data relevant to the rendering of this cell
169 @param {String} data.classnames CSS classes to add to the cell
170 @param {Column} data.column This Column instance
171 @param {Object} data.data The raw object data from the Record
172 @param {String} data.field This Column's "field" attribute value
173 @param {String} data.headers TH ids to reference in the cell's
175 @param {Record} data.record The Record instance for this row
176 @param {Number} data.rowindex The index for this row
177 @param {Node} data.tbody The TBODY Node that will house the cell
178 @param {Node} data.tr The row TR Node that will house the cell
179 @param {Any} data.value The raw Record data for this cell
184 The default markup to display in cells that have no corresponding record
185 data or content from formatters.
187 @attribute emptyCellValue
193 validator: Y.Lang.isString
196 //requires datatable-sort
200 //sortOptions:defaultDir, sortFn, field
202 //TODO: support editable columns
206 //TODO: support resizeable columns
207 //TODO: support setting widths
208 // requires datatable-colresize
217 /////////////////////////////////////////////////////////////////////////////
221 /////////////////////////////////////////////////////////////////////////////
222 Y.extend(Column, Y.Widget, {
223 /////////////////////////////////////////////////////////////////////////////
227 /////////////////////////////////////////////////////////////////////////////
229 * Return ID for instance.
235 _defaultId: function() {
240 * Return key for instance. Defaults to ID if one was not provided.
242 * @method _defaultKey
246 _defaultKey: function() {
251 * Return field for instance. Defaults to key if one was not provided.
253 * @method _defaultField
257 _defaultField: function() {
258 return this.get("key");
262 * Return label for instance. Defaults to key if one was not provided.
264 * @method _defaultLabel
268 _defaultLabel: function() {
269 return this.get("key");
273 * Updates the UI if changes are made to abbr.
275 * @method _afterAbbrChange
276 * @param e {Event} Custom event for the attribute change.
279 _afterAbbrChange: function (e) {
280 this._uiSetAbbr(e.newVal);
283 /////////////////////////////////////////////////////////////////////////////
287 /////////////////////////////////////////////////////////////////////////////
289 * Reference to Column's current position index within its Columnset's keys
290 * array, if applicable. This property only applies to non-nested and bottom-
291 * level child Columns. Value is set by Columnset code.
299 * Array of TH IDs associated with this column, for TD "headers" attribute.
300 * Value is set by Columnset code
308 * Number of cells the header spans. Value is set by Columnset code.
317 * Number of rows the header spans. Value is set by Columnset code.
326 * Column's parent Column instance, if applicable. Value is set by Columnset
335 * The Node reference to the associated TH element.
344 * The Node reference to the associated liner element.
346 * @property thLinerNode
351 /////////////////////////////////////////////////////////////////////////////
355 /////////////////////////////////////////////////////////////////////////////
359 * @method initializer
360 * @param config {Object} Config object.
363 initializer: function(config) {
372 destructor: function() {
376 * Returns classnames for Column.
378 * @method _getClassnames
381 _getClassnames: function () {
382 return Y.ClassNameManager.getClassName(COLUMN, this.get("key").replace(/[^\w\-]/g,""));
385 ////////////////////////////////////////////////////////////////////////////
389 ////////////////////////////////////////////////////////////////////////////
391 * Syncs UI to intial state.
397 this._uiSetAbbr(this.get("abbr"));
404 * @param val {String} New abbr.
407 _uiSetAbbr: function(val) {
408 this.thNode.set("abbr", val);
414 * The Columnset class defines and manages a collection of Columns.
420 function Columnset(config) {
421 Columnset.superclass.constructor.apply(this, arguments);
424 /////////////////////////////////////////////////////////////////////////////
428 /////////////////////////////////////////////////////////////////////////////
441 /////////////////////////////////////////////////////////////////////////////
445 /////////////////////////////////////////////////////////////////////////////
448 * @attribute definitions
449 * @description Array of column definitions that will populate this Columnset.
453 setter: "_setDefinitions"
459 /////////////////////////////////////////////////////////////////////////////
463 /////////////////////////////////////////////////////////////////////////////
464 Y.extend(Columnset, Y.Base, {
465 /////////////////////////////////////////////////////////////////////////////
469 /////////////////////////////////////////////////////////////////////////////
471 * @method _setDefinitions
472 * @description Clones definitions before setting.
473 * @param definitions {Array} Array of column definitions.
477 _setDefinitions: function(definitions) {
478 return Y.clone(definitions);
481 /////////////////////////////////////////////////////////////////////////////
485 /////////////////////////////////////////////////////////////////////////////
487 * Top-down tree representation of Column hierarchy. Used to create DOM
496 * Hash of all Columns by ID.
504 * Hash of all Columns by key.
512 * Array of only Columns that are meant to be displayed in DOM.
519 /////////////////////////////////////////////////////////////////////////////
523 /////////////////////////////////////////////////////////////////////////////
525 * Initializer. Generates all internal representations of the collection of
528 * @method initializer
529 * @param config {Object} Config object.
532 initializer: function() {
534 // DOM tree representation of all Columns
536 // Hash of all Columns by ID
538 // Hash of all Columns by key
540 // Flat representation of only Columns that are meant to display data
542 // Original definitions
543 definitions = this.get("definitions"),
547 // Internal recursive function to define Column instances
548 function parseColumns(depth, currentDefinitions, parent) {
550 len = currentDefinitions.length,
558 // Create corresponding dom node if not already there for this depth
563 // Parse each node at this depth for attributes and any children
565 currentDefinition = currentDefinitions[i];
567 currentDefinition = YLang.isString(currentDefinition) ? {key:currentDefinition} : currentDefinition;
569 // Instantiate a new Column for each node
570 column = new Y.Column(currentDefinition);
572 // Cross-reference Column ID back to the original object literal definition
573 currentDefinition.yuiColumnId = column.get("id");
575 // Add the new Column to the hash
576 idHash[column.get("id")] = column;
577 keyHash[column.get("key")] = column;
579 // Assign its parent as an attribute, if applicable
581 column.parent = parent;
584 // The Column has descendants
585 if(YLang.isArray(currentDefinition.children)) {
586 currentChildren = currentDefinition.children;
587 column._set("children", currentChildren);
589 self._setColSpans(column, currentDefinition);
591 self._cascadePropertiesToChildren(column, currentChildren);
593 // The children themselves must also be parsed for Column instances
597 parseColumns(depth, currentChildren, column);
599 // This Column does not have any children
601 column.keyIndex = keys.length;
602 // Default is already 1
603 //column.colSpan = 1;
607 // Add the Column to the top-down dom tree
608 tree[depth].push(column);
613 // Parse out Column instances from the array of object literals
614 parseColumns(-1, definitions);
617 // Save to the Columnset instance
619 this.idHash = idHash;
620 this.keyHash = keyHash;
633 destructor: function() {
636 /////////////////////////////////////////////////////////////////////////////
640 /////////////////////////////////////////////////////////////////////////////
642 * Cascade certain properties to children if not defined on their own.
644 * @method _cascadePropertiesToChildren
647 _cascadePropertiesToChildren: function(column, currentChildren) {
648 //TODO: this is all a giant todo
650 len = currentChildren.length,
653 // Cascade certain properties to children if not defined on their own
655 child = currentChildren[i];
656 if(column.get("className") && (child.className === undefined)) {
657 child.className = column.get("className");
659 if(column.get("editor") && (child.editor === undefined)) {
660 child.editor = column.get("editor");
662 if(column.get("formatter") && (child.formatter === undefined)) {
663 child.formatter = column.get("formatter");
665 if(column.get("resizeable") && (child.resizeable === undefined)) {
666 child.resizeable = column.get("resizeable");
668 if(column.get("sortable") && (child.sortable === undefined)) {
669 child.sortable = column.get("sortable");
671 if(column.get("hidden")) {
674 if(column.get("width") && (child.width === undefined)) {
675 child.width = column.get("width");
677 if(column.get("minWidth") && (child.minWidth === undefined)) {
678 child.minWidth = column.get("minWidth");
680 if(column.get("maxAutoWidth") && (child.maxAutoWidth === undefined)) {
681 child.maxAutoWidth = column.get("maxAutoWidth");
687 * @method _setColSpans
688 * @description Calculates and sets colSpan attribute on given Column.
689 * @param column {Array} Column instance.
690 * @param definition {Object} Column definition.
693 _setColSpans: function(column, definition) {
694 // Determine COLSPAN value for this Column
695 var terminalChildNodes = 0;
697 function countTerminalChildNodes(ancestor) {
698 var descendants = ancestor.children,
700 len = descendants.length;
702 // Drill down each branch and count terminal nodes
704 // Keep drilling down
705 if(YLang.isArray(descendants[i].children)) {
706 countTerminalChildNodes(descendants[i]);
708 // Reached branch terminus
710 terminalChildNodes++;
714 countTerminalChildNodes(definition);
715 column.colSpan = terminalChildNodes;
719 * @method _setRowSpans
720 * @description Calculates and sets rowSpan attribute on all Columns.
723 _setRowSpans: function() {
724 // Determine ROWSPAN value for each Column in the DOM tree
725 function parseDomTreeForRowSpan(tree) {
731 // Calculate the max depth of descendants for this row
732 function countMaxRowDepth(row, tmpRowDepth) {
733 tmpRowDepth = tmpRowDepth || 1;
741 // Column has children, so keep counting
742 if(YLang.isArray(col.children)) {
744 countMaxRowDepth(col.children, tmpRowDepth);
747 // Column has children, so keep counting
748 else if(col.get && YLang.isArray(col.get("children"))) {
750 countMaxRowDepth(col.get("children"), tmpRowDepth);
753 // No children, is it the max depth?
755 if(tmpRowDepth > maxRowDepth) {
756 maxRowDepth = tmpRowDepth;
762 // Count max row depth for each row
763 for(m=0; m<tree.length; m++) {
764 currentRow = tree[m];
765 countMaxRowDepth(currentRow);
767 // Assign the right ROWSPAN values to each Column in the row
768 for(p=0; p<currentRow.length; p++) {
769 currentColumn = currentRow[p];
770 if(!YLang.isArray(currentColumn.get("children"))) {
771 currentColumn.rowSpan = maxRowDepth;
773 // Default is already 1
774 // else currentColumn.rowSpan =1;
777 // Reset counter for next row
781 parseDomTreeForRowSpan(this.tree);
785 * @method _setHeaders
786 * @description Calculates and sets headers attribute on all Columns.
789 _setHeaders: function() {
792 i=0, len = allKeys.length;
794 function recurseAncestorsForHeaders(headers, column) {
795 headers.push(column.get("id"));
797 recurseAncestorsForHeaders(headers, column.parent);
803 recurseAncestorsForHeaders(headers, column);
804 column.headers = headers.reverse().join(" ");
809 getColumn: function() {
813 Y.Columnset = Columnset;
815 * The DataTable widget provides a progressively enhanced DHTML control for
816 * displaying tabular data across A-grade browsers.
823 * Provides the base DataTable implementation, which can be extended to add
824 * additional functionality, such as sorting or scrolling.
827 * @submodule datatable-base
831 * Base class for the DataTable widget.
832 * @class DataTable.Base
836 function DTBase(config) {
837 DTBase.superclass.constructor.apply(this, arguments);
840 /////////////////////////////////////////////////////////////////////////////
844 /////////////////////////////////////////////////////////////////////////////
858 /////////////////////////////////////////////////////////////////////////////
862 /////////////////////////////////////////////////////////////////////////////
865 * @attribute columnset
866 * @description Pointer to Columnset instance.
867 * @type Array | Y.Columnset
870 setter: "_setColumnset"
874 * @attribute recordset
875 * @description Pointer to Recordset instance.
876 * @type Array | Y.Recordset
879 valueFn: '_initRecordset',
880 setter: "_setRecordset"
885 * @description Internal state.
890 value: new Y.State(),
897 * @description Summary.
905 * @description Caption
912 * @attribute thValueTemplate
913 * @description Tokenized markup template for TH value.
918 value: TEMPLATE_VALUE
922 * @attribute tdValueTemplate
923 * @description Tokenized markup template for TD value.
928 value: TEMPLATE_VALUE
932 * @attribute trTemplate
933 * @description Tokenized markup template for TR node creation.
935 * @default '<tr id="{id}"></tr>'
942 /////////////////////////////////////////////////////////////////////////////
946 /////////////////////////////////////////////////////////////////////////////
948 /*caption: function (srcNode) {
954 /////////////////////////////////////////////////////////////////////////////
958 /////////////////////////////////////////////////////////////////////////////
959 Y.extend(DTBase, Y.Widget, {
961 * @property thTemplate
962 * @description Tokenized markup template for TH node creation.
964 * @default '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}" abbr="{abbr}"><div class="'+CLASS_LINER+'">{value}</div></th>'
966 thTemplate: TEMPLATE_TH,
969 * @property tdTemplate
970 * @description Tokenized markup template for TD node creation.
972 * @default '<td headers="{headers}" class="{classnames}"><div class="yui3-datatable-liner">{value}</div></td>'
974 tdTemplate: TEMPLATE_TD,
977 * @property _theadNode
978 * @description Pointer to THEAD node.
985 * @property _tbodyNode
986 * @description Pointer to TBODY node.
994 * @description Pointer to message display node.
1000 /////////////////////////////////////////////////////////////////////////////
1002 // ATTRIBUTE HELPERS
1004 /////////////////////////////////////////////////////////////////////////////
1006 * @method _setColumnset
1007 * @description Converts Array to Y.Columnset.
1008 * @param columns {Array | Y.Columnset}
1009 * @return {Columnset}
1012 _setColumnset: function(columns) {
1013 return YLang.isArray(columns) ? new Y.Columnset({definitions:columns}) : columns;
1017 * Updates the UI if Columnset is changed.
1019 * @method _afterColumnsetChange
1020 * @param e {Event} Custom event for the attribute change.
1023 _afterColumnsetChange: function (e) {
1024 this._uiSetColumnset(e.newVal);
1028 * @method _setRecordset
1029 * @description Converts Array to Y.Recordset.
1030 * @param records {Array | Recordset}
1031 * @return {Recordset}
1034 _setRecordset: function(rs) {
1035 if(YLang.isArray(rs)) {
1036 rs = new Y.Recordset({records:rs});
1044 * Updates the UI if Recordset is changed.
1046 * @method _afterRecordsetChange
1047 * @param e {Event} Custom event for the attribute change.
1050 _afterRecordsetChange: function (e) {
1051 this._uiSetRecordset(e.newVal);
1055 * Updates the UI if Recordset records are changed.
1057 * @method _afterRecordsChange
1058 * @param e {Event} Custom event for the attribute change.
1061 _afterRecordsChange: function (e) {
1062 this._uiSetRecordset(this.get('recordset'));
1066 * Updates the UI if summary is changed.
1068 * @method _afterSummaryChange
1069 * @param e {Event} Custom event for the attribute change.
1072 _afterSummaryChange: function (e) {
1073 this._uiSetSummary(e.newVal);
1077 * Updates the UI if caption is changed.
1079 * @method _afterCaptionChange
1080 * @param e {Event} Custom event for the attribute change.
1083 _afterCaptionChange: function (e) {
1084 this._uiSetCaption(e.newVal);
1087 ////////////////////////////////////////////////////////////////////////////
1091 ////////////////////////////////////////////////////////////////////////////
1096 * @method destructor
1099 destructor: function() {
1100 this.get("recordset").removeTarget(this);
1103 ////////////////////////////////////////////////////////////////////////////
1107 ////////////////////////////////////////////////////////////////////////////
1115 renderUI: function() {
1117 this._addTableNode(this.get("contentBox"));
1120 this._addColgroupNode(this._tableNode);
1123 this._addTheadNode(this._tableNode);
1126 this._addTbodyNode(this._tableNode);
1129 this._addMessageNode(this._tableNode);
1132 this._addCaptionNode(this._tableNode);
1136 * Creates and attaches TABLE element to given container.
1138 * @method _addTableNode
1139 * @param containerNode {Node} Parent node.
1143 _addTableNode: function(containerNode) {
1144 if (!this._tableNode) {
1145 this._tableNode = containerNode.appendChild(Ycreate(TEMPLATE_TABLE));
1147 return this._tableNode;
1151 * Creates and attaches COLGROUP element to given TABLE.
1153 * @method _addColgroupNode
1154 * @param tableNode {Node} Parent node.
1158 _addColgroupNode: function(tableNode) {
1159 // Add COLs to DOCUMENT FRAGMENT
1160 var len = this.get("columnset").keys.length,
1162 allCols = ["<colgroup>"];
1165 allCols.push(TEMPLATE_COL);
1168 allCols.push("</colgroup>");
1171 this._colgroupNode = tableNode.insertBefore(Ycreate(allCols.join("")), tableNode.get("firstChild"));
1173 return this._colgroupNode;
1177 * Creates and attaches THEAD element to given container.
1179 * @method _addTheadNode
1180 * @param tableNode {Node} Parent node.
1184 _addTheadNode: function(tableNode) {
1186 this._theadNode = tableNode.insertBefore(Ycreate(TEMPLATE_THEAD), this._colgroupNode.next());
1187 return this._theadNode;
1192 * Creates and attaches TBODY element to given container.
1194 * @method _addTbodyNode
1195 * @param tableNode {Node} Parent node.
1199 _addTbodyNode: function(tableNode) {
1200 this._tbodyNode = tableNode.appendChild(Ycreate(TEMPLATE_TBODY));
1201 return this._tbodyNode;
1205 * Creates and attaches message display element to given container.
1207 * @method _addMessageNode
1208 * @param tableNode {Node} Parent node.
1212 _addMessageNode: function(tableNode) {
1213 this._msgNode = tableNode.insertBefore(Ycreate(TEMPLATE_MSG), this._tbodyNode);
1214 return this._msgNode;
1218 * Creates and attaches CAPTION element to given container.
1220 * @method _addCaptionNode
1221 * @param tableNode {Node} Parent node.
1225 _addCaptionNode: function(tableNode) {
1226 this._captionNode = Y.Node.create('<caption></caption>');
1229 ////////////////////////////////////////////////////////////////////////////
1233 ////////////////////////////////////////////////////////////////////////////
1241 bindUI: function() {
1243 columnsetChange: this._afterColumnsetChange,
1244 summaryChange : this._afterSummaryChange,
1245 captionChange : this._afterCaptionChange,
1246 recordsetChange: this._afterRecordsChange,
1247 "recordset:tableChange": this._afterRecordsChange
1251 delegate: function(type) {
1252 //TODO: is this necessary?
1253 if(type==="dblclick") {
1254 this.get("boundingBox").delegate.apply(this.get("boundingBox"), arguments);
1257 this.get("contentBox").delegate.apply(this.get("contentBox"), arguments);
1262 ////////////////////////////////////////////////////////////////////////////
1266 ////////////////////////////////////////////////////////////////////////////
1269 * Syncs UI to intial state.
1274 syncUI: function() {
1276 this._uiSetColumnset(this.get("columnset"));
1278 this._uiSetRecordset(this.get("recordset"));
1280 this._uiSetSummary(this.get("summary"));
1282 this._uiSetCaption(this.get("caption"));
1288 * @method _uiSetSummary
1289 * @param val {String} New summary.
1292 _uiSetSummary: function(val) {
1293 val = YisValue(val) ? val : "";
1294 this._tableNode.set("summary", val);
1300 * @method _uiSetCaption
1301 * @param val {String} New caption.
1304 _uiSetCaption: function(val) {
1305 var caption = this._captionNode,
1306 inDoc = caption.inDoc(),
1307 method = val ? (!inDoc && 'prepend') : (inDoc && 'removeChild');
1309 caption.setContent(val || '');
1312 // prepend of remove necessary
1313 this._tableNode[method](caption);
1318 ////////////////////////////////////////////////////////////////////////////
1320 // THEAD/COLUMNSET FUNCTIONALITY
1322 ////////////////////////////////////////////////////////////////////////////
1326 * @method _uiSetColumnset
1327 * @param cs {Columnset} New Columnset.
1330 _uiSetColumnset: function(cs) {
1332 thead = this._theadNode,
1335 parent = thead.get("parentNode"),
1336 nextSibling = thead.next();
1338 // Move THEAD off DOM
1341 thead.get("children").remove(true);
1343 // Iterate tree of columns to add THEAD rows
1345 this._addTheadTrNode({
1348 id : '' // to avoid {id} leftovers from the trTemplate
1349 }, (i === 0), (i === len - 1));
1352 // Column helpers needs _theadNode to exist
1353 //this._createColumnHelpers();
1356 // Re-attach THEAD to DOM
1357 parent.insert(thead, nextSibling);
1362 * Creates and attaches header row element.
1364 * @method _addTheadTrNode
1365 * @param o {Object} {thead, columns}.
1366 * @param isFirst {Boolean} Is first row.
1367 * @param isFirst {Boolean} Is last row.
1370 _addTheadTrNode: function(o, isFirst, isLast) {
1371 o.tr = this._createTheadTrNode(o, isFirst, isLast);
1372 this._attachTheadTrNode(o);
1377 * Creates header row element.
1379 * @method _createTheadTrNode
1380 * @param o {Object} {thead, columns}.
1381 * @param isFirst {Boolean} Is first row.
1382 * @param isLast {Boolean} Is last row.
1386 _createTheadTrNode: function(o, isFirst, isLast) {
1387 //TODO: custom classnames
1388 var tr = Ycreate(fromTemplate(this.get("trTemplate"), o)),
1390 columns = o.columns,
1391 len = columns.length,
1394 // Set FIRST/LAST class
1396 tr.addClass(CLASS_FIRST);
1399 tr.addClass(CLASS_LAST);
1403 column = columns[i];
1404 this._addTheadThNode({value:column.get("label"), column: column, tr:tr});
1411 * Attaches header row element.
1413 * @method _attachTheadTrNode
1414 * @param o {Object} {thead, columns, tr}.
1417 _attachTheadTrNode: function(o) {
1418 o.thead.appendChild(o.tr);
1422 * Creates and attaches header cell element.
1424 * @method _addTheadThNode
1425 * @param o {Object} {value, column, tr}.
1428 _addTheadThNode: function(o) {
1429 o.th = this._createTheadThNode(o);
1430 this._attachTheadThNode(o);
1431 //TODO: assign all node pointers: thNode, thLinerNode, thLabelNode
1432 o.column.thNode = o.th;
1436 * Creates header cell element.
1438 * @method _createTheadThNode
1439 * @param o {Object} {value, column, tr}.
1443 _createTheadThNode: function(o) {
1444 var column = o.column;
1446 // Populate template object
1447 o.id = column.get("id");//TODO: validate 1 column ID per document
1448 o.colspan = column.colSpan;
1449 o.rowspan = column.rowSpan;
1450 o.abbr = column.get("abbr");
1451 o.classnames = column.get("classnames");
1452 o.value = fromTemplate(this.get("thValueTemplate"), o);
1455 // Clear minWidth on hidden Columns
1456 if(column.get("hidden")) {
1457 //this._clearMinWidth(column);
1461 return Ycreate(fromTemplate(this.thTemplate, o));
1465 * Attaches header cell element.
1467 * @method _attachTheadThNode
1468 * @param o {Object} {value, column, tr}.
1471 _attachTheadThNode: function(o) {
1472 o.tr.appendChild(o.th);
1475 ////////////////////////////////////////////////////////////////////////////
1477 // TBODY/RECORDSET FUNCTIONALITY
1479 ////////////////////////////////////////////////////////////////////////////
1483 * @method _uiSetRecordset
1484 * @param rs {Recordset} New Recordset.
1487 _uiSetRecordset: function(rs) {
1489 oldTbody = this._tbodyNode,
1490 parent = oldTbody.get("parentNode"),
1491 nextSibling = oldTbody.next(),
1492 columns = this.get('columnset').keys,
1493 cellValueTemplate = this.get('tdValueTemplate'),
1495 newTbody, i, len, column, formatter;
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 o.rowTemplate = this.get('trTemplate');
1509 // Build up column data to avoid passing through Attribute APIs inside
1510 // render loops for rows and cells
1511 for (i = columns.length - 1; i >= 0; --i) {
1512 column = columns[i];
1515 fields : column.get('field'),
1516 classnames : column.get('classnames'),
1517 emptyCellValue: column.get('emptyCellValue')
1520 formatter = column.get('formatter');
1522 if (YLang.isFunction(formatter)) {
1523 // function formatters need to run before checking if the value
1524 // needs defaulting from column.emptyCellValue
1525 formatter = Y.bind(this._functionFormatter, this, formatter);
1527 if (!YLang.isString(formatter)) {
1528 formatter = cellValueTemplate;
1531 // string formatters need the value defaulted before processing
1532 formatter = Y.bind(this._templateFormatter, this, formatter);
1535 o.columns[i].formatter = formatter;
1539 // Iterate Recordset to use existing TR when possible or add new TR
1540 // TODO i = this.get("state.offsetIndex")
1541 // TODO len =this.get("state.pageLength")
1542 for (i = 0, len = rs.size(); i < len; ++i) {
1543 o.record = rs.item(i);
1544 o.data = o.record.get("data");
1546 this._addTbodyTrNode(o); //TODO: sometimes rowindex != recordindex
1550 parent.insert(this._tbodyNode, nextSibling);
1553 _functionFormatter: function (formatter, o) {
1554 var value = formatter.call(this, o);
1556 return (value !== undefined) ? value : o.emptyCellValue;
1559 _templateFormatter: function (template, o) {
1560 if (o.value === undefined) {
1561 o.value = o.emptyCellValue;
1564 return fromTemplate(template, o);
1568 * Creates and attaches data row element.
1570 * @method _addTbodyTrNode
1571 * @param o {Object} {tbody, record}
1574 _addTbodyTrNode: function(o) {
1575 var row = o.tbody.one("#" + o.record.get("id"));
1577 o.tr = row || this._createTbodyTrNode(o);
1579 this._attachTbodyTrNode(o);
1583 * Creates data row element.
1585 * @method _createTbodyTrNode
1586 * @param o {Object} {tbody, record}
1590 _createTbodyTrNode: function(o) {
1591 var columns = o.columns,
1594 o.tr = Ycreate(fromTemplate(o.rowTemplate, { id: o.record.get('id') }));
1596 for (i = 0, len = columns.length; i < len; ++i) {
1597 columnInfo = columns[i];
1598 o.column = columnInfo.column;
1599 o.field = columnInfo.fields;
1600 o.classnames = columnInfo.classnames;
1601 o.formatter = columnInfo.formatter;
1602 o.emptyCellValue= columnInfo.emptyCellValue;
1604 this._addTbodyTdNode(o);
1611 * Attaches data row element.
1613 * @method _attachTbodyTrNode
1614 * @param o {Object} {tbody, record, tr}.
1617 _attachTbodyTrNode: function(o) {
1618 var tbody = o.tbody,
1621 nextSibling = tbody.get("children").item(index) || null,
1622 isOdd = (index % 2);
1625 tr.replaceClass(CLASS_EVEN, CLASS_ODD);
1627 tr.replaceClass(CLASS_ODD, CLASS_EVEN);
1630 tbody.insertBefore(tr, nextSibling);
1634 * Creates and attaches data cell element.
1636 * @method _addTbodyTdNode
1637 * @param o {Object} {record, column, tr}.
1640 _addTbodyTdNode: function(o) {
1641 o.td = this._createTbodyTdNode(o);
1642 this._attachTbodyTdNode(o);
1647 Creates a TD Node from the tdTemplate property using the input object as
1648 template {placeholder} values. The created Node is also assigned to the
1649 `td` property on the input object.
1651 If the input object already has a `td` property, it is returned an no new
1655 @param {Object} data Template values
1658 createCell: function (data) {
1659 return data && (data.td ||
1660 (data.td = Ycreate(fromTemplate(this.tdTemplate, data))));
1664 * Creates data cell element.
1666 * @method _createTbodyTdNode
1667 * @param o {Object} {record, column, tr}.
1671 _createTbodyTdNode: function(o) {
1672 o.headers = o.column.headers;
1673 o.value = this.formatDataCell(o);
1675 return o.td || this.createCell(o);
1679 * Attaches data cell element.
1681 * @method _attachTbodyTdNode
1682 * @param o {Object} {record, column, tr, headers, classnames, value}.
1685 _attachTbodyTdNode: function(o) {
1686 o.tr.appendChild(o.td);
1690 * Returns markup to insert into data cell element.
1692 * @method formatDataCell
1693 * @param @param o {Object} {record, column, tr, headers, classnames}.
1695 formatDataCell: function (o) {
1696 o.value = o.data[o.field];
1698 return o.formatter.call(this, o);
1701 _initRecordset: function () {
1702 return new Y.Recordset({ records: [] });
1706 Y.namespace("DataTable").Base = DTBase;
1709 }, '3.5.1' ,{requires:['recordset-base','widget','substitute','event-mouseenter']});