datatable.js revision 772c99f483000bc957c80ccb621d31f244e188f4
var COLUMN = "column";
}
/**
* Class name.
*
* @property NAME
* @type String
* @static
* @final
* @value "column"
*/
/////////////////////////////////////////////////////////////////////////////
//
// Column Attributes
//
/////////////////////////////////////////////////////////////////////////////
id: {
valueFn: "_defaultId",
writeOnce: true
},
key: {
valueFn: "_defaultKey"
},
field: {
valueFn: "_defaultField"
},
label: {
valueFn: "_defaultLabel"
},
keyIndex: {
readOnly: true
},
parent: {
readOnly: true
},
children: {
},
colspan: {
readOnly: true
},
rowspan: {
readOnly: true
},
thNode: {
readOnly: true
},
thLinerNode: {
readOnly: true
},
thLabelNode: {
readOnly: true
},
abbr: {
value: null
},
headers: {}, // set by Columnset code
classnames: {
readOnly: true,
getter: "_getClassnames"
},
editor: {},
formatter: {},
// requires datatable-colresize
resizeable: {},
//requires datatable-sort
sortable: {},
hidden: {},
width: {},
minWidth: {},
maxAutoWidth: {}
};
/* Column extends Widget */
_defaultId: function() {
return Y.guid();
},
_defaultKey: function(key) {
},
_defaultField: function(field) {
},
_defaultLabel: function(label) {
},
initializer: function() {
},
destructor: function() {
},
syncUI: function() {
},
_afterAbbrChange: function (e) {
this._uiSetAbbr(e.newVal);
},
_uiSetAbbr: function(val) {
},
/**
* Returns classnames for Column.
*
* @method _getClassnames
* @private
*/
_getClassnames: function () {
/*var allClasses;
// Add CSS classes
if(lang.isString(oColumn.className)) {
// Single custom class
allClasses = [oColumn.className];
}
else if(lang.isArray(oColumn.className)) {
// Array of custom classes
allClasses = oColumn.className;
}
else {
// no custom classes
allClasses = [];
}
// Hook for setting width with via dynamic style uses key since ID is too disposable
allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey();
// Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
var isSortedBy = this.get("sortedBy") || {};
// Sorted
if(oColumn.key === isSortedBy.key) {
allClasses[allClasses.length] = isSortedBy.dir || '';
}
// Hidden
if(oColumn.hidden) {
allClasses[allClasses.length] = DT.CLASS_HIDDEN;
}
// Selected
if(oColumn.selected) {
allClasses[allClasses.length] = DT.CLASS_SELECTED;
}
// Sortable
if(oColumn.sortable) {
allClasses[allClasses.length] = DT.CLASS_SORTABLE;
}
// Resizeable
if(oColumn.resizeable) {
allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
}
// Editable
if(oColumn.editor) {
allClasses[allClasses.length] = DT.CLASS_EDITABLE;
}
if(aAddClasses) {
allClasses = allClasses.concat(aAddClasses);
}
return allClasses.join(' ');*/
}
});
}
/**
* Class name.
*
* @property NAME
* @type String
* @static
* @final
* @value "columnset"
*/
/////////////////////////////////////////////////////////////////////////////
//
// Columnset Attributes
//
/////////////////////////////////////////////////////////////////////////////
columns: {
setter: "_setColumns"
},
// DOM tree representation of all Columns
tree: {
readOnly: true,
value: []
},
//TODO: is this necessary?
// Flat representation of all Columns
flat: {
readOnly: true,
value: []
},
// Hash of all Columns by ID
hash: {
readOnly: true,
value: {}
},
// Flat representation of only Columns that are meant to display data
keys: {
readOnly: true,
value: []
}
};
/* Columnset extends Base */
_setColumns: function(columns) {
},
initializer: function() {
// DOM tree representation of all Columns
var tree = [],
// Flat representation of all Columns
flat = [],
// Hash of all Columns by ID
hash = {},
// Flat representation of only Columns that are meant to display data
keys = [],
// Original definitions
self = this;
// Internal recursive function to define Column instances
var i=0,
// One level down
depth++;
// Create corresponding dom node if not already there for this depth
}
// Parse each node at this depth for attributes and any children
for(; i<len; ++i) {
currentNode = nodeList[i];
// Instantiate a new Column for each node
// Cross-reference Column ID back to the original object literal definition
// Add the new Column to the flat list
// Add the new Column to the hash
// Assign its parent as an attribute, if applicable
if(parent) {
}
// The Column has descendants
// The children themselves must also be parsed for Column instances
}
}
// This Column does not have any children
else {
}
// Add the Column to the top-down dom tree
}
depth--;
}
// Parse out Column instances from the array of object literals
// Save to the Columnset instance
this._setRowSpans();
this._setHeaders();
},
destructor: function() {
},
var i = 0,
// Cascade certain properties to children if not defined on their own
for(; i<len; ++i) {
child = currentChildren[i];
}
}
}
}
}
}
}
}
}
}
},
// Determine COLSPAN value for this Column
var terminalChildNodes = 0;
function countTerminalChildNodes(ancestor) {
i = 0,
// Drill down each branch and count terminal nodes
for(; i<len; ++i) {
// Keep drilling down
}
// Reached branch terminus
else {
}
}
}
},
_setRowSpans: function() {
// Determine ROWSPAN value for each Column in the dom tree
function parseDomTreeForRowspan(tree) {
var maxRowDepth = 1,
m,p;
// Calculate the max depth of descendants for this row
var i = 0,
col;
for(; i<len; ++i) {
// Column has children, so keep counting
tmpRowDepth++;
tmpRowDepth--;
}
// Column has children, so keep counting
tmpRowDepth++;
tmpRowDepth--;
}
// No children, is it the max depth?
else {
if(tmpRowDepth > maxRowDepth) {
}
}
}
}
// Count max row depth for each row
currentRow = tree[m];
// Assign the right ROWSPAN values to each Column in the row
currentColumn = currentRow[p];
}
else {
}
}
// Reset counter for next row
maxRowDepth = 1;
}
}
},
_setHeaders: function() {
//headers[i].push(oColumn.getSanitizedKey());
}
}
for(; i<len; ++i) {
headers = [];
}
},
getColumn: function() {
}
});
/**
* The DataTable widget provides a progressively enhanced DHTML control for
* displaying tabular data across A-grade browsers.
*
* @module datatable
*/
/**
* Provides the base DataTable implementation, which can be extended to add
* additional functionality, such as sorting or scrolling.
*
* @module datatable
* @submodule datatable-base
*/
/**
* Base class for the DataTable widget.
* @class DataSource.Base
* @extends Widget
* @constructor
*/
DATATABLE = "datatable",
FOCUS = "focus",
KEYDOWN = "keydown",
MOUSEOVER = "mouseover",
MOUSEOUT = "mouseout",
MOUSEUP = "mouseup",
MOUSEDOWN = "mousedown",
CLICK = "click",
DOUBLECLICK = "doubleclick",
TEMPLATE_TABLE = '<table></table>',
TEMPLATE_COL = '<col></col>',
TEMPLATE_TH = '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}"><div class="'+CLASS_LINER+'">{value}</div></th>',
TEMPLATE_TR = '<tr id="{id}"></tr>',
TEMPLATE_TD = '<td headers="{headers}" class="{classnames}"><div class="'+CLASS_LINER+'">{value}</div></td>',
TEMPLATE_VALUE = '{value}',
}
/////////////////////////////////////////////////////////////////////////////
//
// STATIC PROPERTIES
//
/////////////////////////////////////////////////////////////////////////////
/**
* Class name.
*
* @property NAME
* @type String
* @static
* @final
* @value "dataSourceLocal"
*/
NAME: "dataTable",
/////////////////////////////////////////////////////////////////////////////
//
// ATTRIBUTES
//
/////////////////////////////////////////////////////////////////////////////
ATTRS: {
/**
* @attribute columnset
* @description Pointer to Columnset instance.
* @type Array | Y.Columnset
*/
columnset: {
setter: "_setColumnset"
},
/**
* @attribute recordset
* @description Pointer to Recordset instance.
* @type Array | Y.Recordset
*/
recordset: {
setter: "_setRecordset"
},
/*TODO
* @attribute state
* @description Internal state.
* @readonly
* @type
*/
/*state: {
value: new Y.State(),
readOnly: true
},*/
/**
* @attribute strings
* @description The collection of localizable strings used to label
* elements of the UI.
* @type Object
*/
strings: {
valueFn: function() {
}
},
/**
* @attribute thValueTemplate
* @description Tokenized markup template for TH value.
* @type String
* @default '{value}'
*/
},
/**
* @attribute tdValueTemplate
* @description Tokenized markup template for TD value.
* @type String
* @default '{value}'
*/
},
/**
* @attribute trTemplate
* @description Tokenized markup template for TR node creation.
* @type String
* @default '<tr id="{id}"></tr>'
*/
trTemplate: {
}
},
/////////////////////////////////////////////////////////////////////////////
//
// TODO: HTML_PARSER
//
/////////////////////////////////////////////////////////////////////////////
HTML_PARSER: {
/*caption: function (srcNode) {
}*/
}
});
/////////////////////////////////////////////////////////////////////////////
//
// PROTOTYPE
//
/////////////////////////////////////////////////////////////////////////////
/**
* @property thTemplate
* @description Tokenized markup template for TH node creation.
* @type String
* @default '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}"><div class="'+CLASS_LINER+'">{value}</div></th>'
*/
/**
* @property tdTemplate
* @description Tokenized markup template for TD node creation.
* @type String
* @default '<td headers="{headers}"><div class="'+CLASS_LINER+'">{value}</div></td>'
*/
/**
* @property _theadNode
* @description Pointer to THEAD node.
* @type Y.Node
* @private
*/
_theadNode: null,
/**
* @property _tbodyNode
* @description Pointer to TBODY node.
* @type Y.Node
* @private
*/
_tbodyNode: null,
/**
* @property _msgNode
* @description Pointer to message display node.
* @type Y.Node
* @private
*/
_msgNode: null,
/////////////////////////////////////////////////////////////////////////////
//
// ATTRIBUTE HELPERS
//
/////////////////////////////////////////////////////////////////////////////
/**
* @property _setColumnset
* @description Converts Array to Y.Columnset.
* @param columns {Array | Y.Columnset}
* @returns Y.Columnset
* @private
*/
_setColumnset: function(columns) {
},
/**
* Updates the UI if changes are made to Columnset.
*
* @method _afterColumnsetChange
* @param e {Event} Custom event for the attribute change.
* @private
*/
_afterColumnsetChange: function (e) {
this._uiSetColumnset(e.newVal);
},
/**
* @property _setRecordset
* @description Converts Array to Y.Recordset.
* @param records {Array | Y.Recordset}
* @returns Y.Recordset
* @private
*/
_setRecordset: function(rs) {
}
return rs;
},
/**
* @property _afterRecordsetChange
* @description Adds bubble target.
* @param records {Array | Y.Recordset}
* @returns Y.Recordset
* @private
*/
_afterRecordsetChange: function (e) {
this._uiSetRecordset(e.newVal);
},
/////////////////////////////////////////////////////////////////////////////
//
// METHODS
//
/////////////////////////////////////////////////////////////////////////////
/**
* Initializer.
*
* @method initializer
* @param config {Object} Config object.
* @private
*/
initializer: function(config) {
},
/**
* Destructor.
*
* @method destructor
* @private
*/
destructor: function() {
},
////////////////////////////////////////////////////////////////////////////
//
// RENDER
//
////////////////////////////////////////////////////////////////////////////
/**
* Renders UI.
*
* @method renderUI
* @private
*/
renderUI: function() {
var ok =
// TABLE
// COLGROUP
this._addColgroupNode(this._tableNode) &&
// THEAD
this._addTheadNode(this._tableNode) &&
// Primary TBODY
this._addTbodyNode(this._tableNode) &&
// Message TBODY
this._addMessageNode(this._tableNode) &&
// CAPTION
this._addCaptionNode(this._tableNode);
},
/**
* Creates and attaches TABLE element to given container.
*
* @method _addTableNode
* @param containerNode {Y.Node} Parent node.
* @protected
* @returns Y.Node
*/
_addTableNode: function(containerNode) {
if (!this._tableNode) {
}
return this._tableNode;
},
/**
* Creates and attaches COLGROUP element to given TABLE.
*
* @method _addColgroupNode
* @param tableNode {Y.Node} Parent node.
* @protected
* @returns Y.Node
*/
_addColgroupNode: function(tableNode) {
// Add COLs to DOCUMENT FRAGMENT
i = 0,
allCols = ["<colgroup>"];
for(; i<len; ++i) {
}
// Create COLGROUP
this._colgroupNode = tableNode.insertBefore(Ycreate(allCols.join("")), tableNode.get("firstChild"));
return this._colgroupNode;
},
/**
* Creates and attaches THEAD element to given container.
*
* @method _addTheadNode
* @param tableNode {Y.Node} Parent node.
* @protected
* @returns Y.Node
*/
_addTheadNode: function(tableNode) {
if(tableNode) {
return this._theadNode;
}
},
/**
* Creates and attaches TBODY element to given container.
*
* @method _addTbodyNode
* @param tableNode {Y.Node} Parent node.
* @protected
* @returns Y.Node
*/
_addTbodyNode: function(tableNode) {
return this._tbodyNode;
},
/**
* Creates and attaches message display element to given container.
*
* @method _addMessageNode
* @param tableNode {Y.Node} Parent node.
* @protected
* @returns Y.Node
*/
_addMessageNode: function(tableNode) {
return this._msgNode;
},
/**
* Creates and attaches CAPTION element to given container.
*
* @method _addCaptionNode
* @param tableNode {Y.Node} Parent node.
* @protected
* @returns Y.Node
*/
_addCaptionNode: function(tableNode) {
//TODO: node.createCaption
return this._captionNode;
},
////////////////////////////////////////////////////////////////////////////
//
// BIND
//
////////////////////////////////////////////////////////////////////////////
/**
* Binds events.
*
* @method bindUI
* @private
*/
bindUI: function() {
var tableNode = this._tableNode,
// Define custom events that wrap DOM events. Simply pass through DOM
// event facades.
//TODO: do we need queuable=true?
//TODO: All the other events.
this.publish("theadCellClick", {defaultFn: this._defTheadCellClickFn, emitFacade:false, queuable:true});
this.publish("theadRowClick", {defaultFn: this._defTheadRowClickFn, emitFacade:false, queuable:true});
// Bind to THEAD DOM events
// Since we can't listen for click and dblclick on the same element...
// Bind to TBODY DOM events
// Since we can't listen for click and dblclick on the same element...
// Bind to message TBODY DOM events
// Since we can't listen for click and dblclick on the same element...
},
/**
* On DOM event, fires corresponding custom event.
*
* @method _onDomEvent
* @param e {DOMEvent} The original DOM event facade.
* @param type {String} Corresponding custom event to fire.
* @private
*/
_onDomEvent: function(e, type) {
},
//TODO: abstract this out
_defTheadCellClickFn: function(e) {
this.fire("theadRowClick", e);
},
_defTheadRowClickFn: function(e) {
this.fire("theadClick", e);
},
_defTheadClickFn: function(e) {
},
////////////////////////////////////////////////////////////////////////////
//
// SYNC
//
////////////////////////////////////////////////////////////////////////////
/**
* Syncs UI to intial state.
*
* @method syncUI
* @private
*/
syncUI: function() {
// THEAD ROWS
// DATA ROWS
// STRINGS
},
/**
* Updates the UI if changes are made to any of the strings in the strings
* attribute.
*
* @method _afterStringsChange
* @param e {Event} Custom event for the attribute change.
* @protected
*/
_afterStringsChange: function (e) {
this._uiSetStrings(e.newVal);
},
/**
* Updates all strings.
*
* @method _uiSetStrings
* @param strings {Object} Collection of new strings.
* @protected
*/
_uiSetStrings: function (strings) {
},
/**
* Updates summary.
*
* @method _uiSetSummary
* @param val {String} New summary.
* @protected
*/
_uiSetSummary: function(val) {
},
/**
* Updates caption.
*
* @method _uiSetCaption
* @param val {String} New caption.
* @protected
*/
_uiSetCaption: function(val) {
},
////////////////////////////////////////////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////////////////
/**
* Updates THEAD.
*
* @method _uiSetColumnset
* @param cs {Y.Columnset} New Columnset.
* @protected
*/
_uiSetColumnset: function(cs) {
thead = this._theadNode,
i = 0,
//TODO: move thead off dom
// Iterate tree of columns to add THEAD rows
for(; i<len; ++i) {
}
// Column helpers needs _theadNode to exist
//this._createColumnHelpers();
//TODO: move thead on dom
},
/**
* Creates and attaches header row element.
*
* @method _addTheadTrNode
* @param o {Object} {thead, columns}.
* @param isFirst {Boolean} Is first row.
* @param isFirst {Boolean} Is last row.
* @protected
*/
this._attachTheadTrNode(o);
},
/**
* Creates header row element.
*
* @method _createTheadTrNode
* @param o {Object} {thead, columns}.
* @param isFirst {Boolean} Is first row.
* @param isLast {Boolean} Is last row.
* @protected
* @returns Y.Node
*/
//TODO: custom classnames
i = 0,
if(isFirst) {
}
if(isLast) {
}
for(; i<len; ++i) {
}
return tr;
},
/**
* Attaches header row element.
*
* @method _attachTheadTrNode
* @param o {Object} {thead, columns, tr}.
* @protected
*/
_attachTheadTrNode: function(o) {
},
/**
* Creates and attaches header cell element.
*
* @method _addTheadThNode
* @param o {Object} {value, column, tr}.
* @protected
*/
_addTheadThNode: function(o) {
o.th = this._createTheadThNode(o);
this._attachTheadThNode(o);
},
/**
* Creates header cell element.
*
* @method _createTheadThNode
* @param o {Object} {value, column, tr}.
* @protected
* @returns Y.Node
*/
_createTheadThNode: function(o) {
// Populate template object
//TODO o.abbr = column.get("abbr");
/*TODO
// Clear minWidth on hidden Columns
if(column.get("hidden")) {
//this._clearMinWidth(column);
}
*/
//column._set("thNode", o.th);
},
/**
* Attaches header cell element.
*
* @method _attachTheadTrNode
* @param o {Object} {value, column, tr}.
* @protected
*/
_attachTheadThNode: function(o) {
},
////////////////////////////////////////////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////////////////
_uiSetRecordset: function(rs) {
var i = 0,//TODOthis.get("state.offsetIndex")
// Iterate recordset to use existing or add new tr
for(; i<len; ++i) {
o.rowindex = i;
this._addTbodyTrNode(o); //TODO: sometimes rowindex != recordindex
}
},
_addTbodyTrNode: function(o) {
this._attachTbodyTrNode(o);
},
_createTbodyTrNode: function(o) {
i = 0,
for(; i<len; ++i) {
this._addTbodyTdNode(o);
}
return tr;
},
_attachTbodyTrNode: function(o) {
},
_addTbodyTdNode: function(o) {
o.td = this._createTbodyTdNode(o);
this._attachTbodyTdNode(o);
},
_createTbodyTdNode: function(o) {
//TODO: attributes? or methods?
o.value = this.formatDataCell(o);
},
_attachTbodyTdNode: function(o) {
},
formatDataCell: function(o) {
}
});
//TODO: break out into own component
var //getClassName = Y.ClassNameManager.getClassName,
//DATATABLE = "datatable",
ASC = "asc",
DESC = "desc",
//CLASS_ASC = getClassName(DATATABLE, "asc"),
//CLASS_DESC = getClassName(DATATABLE, "desc"),
TEMPLATE_TH_LINK = '<a class="{link_class}" title="{link_title}" href="{link_href}">{value}</a>';
function DataTableSort() {
}
Y.mix(DataTableSort, {
NS: "sort",
NAME: "dataTableSort",
ATTRS: {
trigger: {
value: "theadCellClick",
writeOnce: "initOnly"
},
sortedBy: {
value: null
}
}
});
initializer: function(config) {
// Wrap link around TH value
// Add class
this.doBefore("_attachTheadThNode", function(o) {
});
// Attach click handlers
// Attach UI hooks
});
alert('ok');
});
//TODO
//dt.after("recordset:mutation", function() {//reset sortedBy});
//TODO
//add Column sortFn ATTR
// Update UI after the fact (plug-then-render case)
}
},
_beforeCreateTheadThNode: function(o) {
link_class: "foo",
link_title: "bar",
link_href: "bat",
});
}
},
_onEventSortColumn: function(e) {
e.halt();
//TODO: normalize e.currentTarget to TH
dir = (prevSortedBy &&
}
}
});
DATATABLE = "datatable",
//CLASS_RESIZEABLE = GETCLASSNAME(DATATABLE, "resizeable"),
function DataTableColResize() {
}
Y.mix(DataTableColResize, {
NS: "colresize",
NAME: "dataTableColResize",
ATTRS: {
}
});
initializer: function(config) {
this.get("host").thTemplate = Y.substitute(this.get("host").thTemplate, {value: this.thLinerTemplate});
this.get("host").tdTemplate = Y.substitute(this.get("host").tdTemplate, {value: this.tdLinerTemplate});
//TODO Set Column width...
/*if(oColumn.width) {
// Validate minWidth
var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
oColumn.minWidth : oColumn.width;
// ...for fallback cases
if(DT._bDynStylesFallback) {
elTh.firstChild.style.overflow = 'hidden';
elTh.firstChild.style.width = nWidth + 'px';
}
// ...for non fallback cases
else {
this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden');
}
}*/
}
});
/**
* Extends DataTable base to enable x,y, and xy scrolling.
* @module datatable
* @submodule datatable-scroll
*/
YStyleSheet = Y.StyleSheet,
DATATABLE = "datatable",
TEMPLATE_TH = '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}"><div class="'+CLASS_LINER+'" style="width:100px">{value}</div></th>',
TEMPLATE_TD = '<td headers="{headers}"><div class="'+CLASS_LINER+'" style="width:100px">{value}</div></td>',
TEMPLATE_TABLE = '<table></table>';
function DataTableScroll() {
}
Y.mix(DataTableScroll, {
NS: "scroll",
NAME: "dataTableScroll",
ATTRS: {
/**
* @description The width for the table. Set to a string (ex: "200px", "20em")
*
* @attribute width
* @public
* @static
* @type string
*/
width: {
},
/**
* @description The height for the table. Set to a string (ex: "200px", "20em")
*
* @attribute height
* @public
* @static
* @type string
*/
height: {
},
/**
* @description The scrolling direction for the table. Can be set to 'x', 'y', or 'xy'
*
* @attribute scroll
* @public
* @static
* @type string
*/
scroll: {
value: 'y'
},
/**
* @description The hexadecimal colour value to set on the top-right of the table if a scrollbar exists.
*
* @attribute COLOR_COLUMNFILLER
* @public
* @static
* @type string
*/
value: '#f2f2f2',
if (this._headerContainerNode) {
}
}
}
}
});
/**
* @description The base template for a td DOM element.
*
* @property tdTemplate
* @type string
*/
/**
* @description The base template for a th DOM element.
*
* @property thTemplate
* @type string
*/
/**
* @description The table node created in datatable-base
*
* @property _parentTableNode
* @private
* @type Y.Node
*/
_parentTableNode: null,
/**
* @description The THEAD node which resides within the table node created in datatable-base
*
* @property _parentTheadNode
* @private
* @type Y.Node
*/
_parentTheadNode: null,
/**
* @description The TBODY node which resides within the table node created in datatable-base
*
* @property _parentTbodyNode
* @private
* @type Y.Node
*/
_parentTbodyNode: null,
/**
* @description The TBODY Message node which resides within the table node created in datatable-base
*
* @property _parentMsgNode
* @private
* @type Y.Node
*/
_parentMsgNode: null,
/**
* @description The contentBox specified for the datatable in datatable-base
*
* @property _parentContainer
* @private
* @type Y.Node
*/
_parentContainer: null,
/**
* @description The DIV node that contains all the scrollable elements (a table with a tbody on it)
*
* @property _bodyContainerNode
* @private
* @type Y.Node
*/
_bodyContainerNode: null,
/**
* @description The DIV node that contains a table with a THEAD in it (which syncs its horizontal scroll with the _bodyContainerNode above)
*
* @property _headerContainerNode
* @private
* @type Y.Node
*/
_headerContainerNode: null,
//--------------------------------------
// Methods
//--------------------------------------
initializer: function(config) {
this._setUpNodes();
},
/////////////////////////////////////////////////////////////////////////////
//
// Set up Table Nodes
//
/////////////////////////////////////////////////////////////////////////////
/**
* @description Set up methods to fire after host methods execute
*
* @method _setUpNodes
* @private
*/
_setUpNodes: function() {
}
},
/**
* @description Stores the main <table> node provided by the host as a private property
*
* @method _setUpParentTableNode
* @private
*/
_setUpParentTableNode: function() {
},
/**
* @description Stores the main <thead> node provided by the host as a private property
*
* @method _setUpParentTheadNode
* @private
*/
_setUpParentTheadNode: function() {
},
/**
* @description Stores the main <tbody> node provided by the host as a private property
*
* @method _setUpParentTbodyNode
* @private
*/
_setUpParentTbodyNode: function() {
},
/**
* @description Stores the main <tbody> message node provided by the host as a private property
*
* @method _setUpParentMessageNode
* @private
*/
_setUpParentMessageNode: function() {
},
/////////////////////////////////////////////////////////////////////////////
//
// Renderer
//
/////////////////////////////////////////////////////////////////////////////
/**
* @description Primary rendering method that takes the datatable rendered in
* the host, and splits it up into two separate <divs> each containing two
* separate tables (one containing the head and one containing the body).
* This method fires after renderUI is called on datatable-base.
*
* @method renderUI
* @public
*/
renderUI: function() {
//Y.Profiler.start('render');
this._createBodyContainer();
this._createHeaderContainer();
this._setContentBoxDimensions();
//Y.Profiler.stop('render');
//console.log(Y.Profiler.getReport("render"));
},
/**
* @description Post rendering method that is responsible for creating a column
* filler, and performing width and scroll synchronization between the <th>
* elements and the <td> elements.
* This method fires after syncUI is called on datatable-base
*
* @method syncUI
* @public
*/
syncUI: function() {
//Y.Profiler.start('sync');
this._syncWidths();
this._syncScroll();
//Y.Profiler.stop('sync');
//console.log(Y.Profiler.getReport("sync"));
},
/**
* @description Adjusts the width of the TH and the TDs to make sure that the two are in sync
*
* Implementation Details:
* Compares the width of the TH liner div to the the width of the TD node. The TD liner width
* is not actually used because the TD often stretches past the liner if the parent DIV is very
* large. Measuring the TD width is more accurate.
*
* Instead of measuring via .get('width'), 'clientWidth' is used, as it returns a number, whereas
* 'width' returns a string, In IE6, 'clientWidth' is not supported, so 'offsetWidth' is used.
* 'offsetWidth' is not as accurate on Chrome,FF as 'clientWidth' - thus the need for the fork.
*
* @method _syncWidths
* @public
*/
_syncWidths: function() {
var th = YNode.all('#'+this._parentContainer.get('id')+' .yui3-datatable-hd table thead th'), //nodelist of all THs
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
i,
len,
//stylesheet = new YStyleSheet('columnsSheet'),
//className;
/*
This for loop goes through the first row of TDs in the table.
In a table, the width of the row is equal to the width of the longest cell in that column.
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)
*/
//className = '.'+td.item(i).get('classList')._nodes[0];
//If a width has not been already set on the TD:
//if (td.item(i).get('firstChild').getStyle('width') === "auto") {
//Get the liners for the TH and the TD cell in question
/*
If browser is not IE - get the clientWidth of the Liner div and the TD.
Note: We are not getting the width of the TDLiner, we are getting the width of the actual cell.
Why? Because when the table is set to auto width, the cell will grow to try to fit the table in its container.
The liner could potentially be much smaller than the cell width.
TODO: Explore if there is a better way using only LINERS widths
*/
if (!ie) {
thWidth = thLiner.get('clientWidth'); //TODO: this should actually be done with getComputedStyle('width') but this messes up columns. Explore this option.
}
//IE wasn't recognizing clientWidths, so we are using offsetWidths.
//TODO: should use getComputedStyle('width') because offsetWidth will screw up when padding is changed.
else {
//thWidth = parseFloat(thLiner.getComputedStyle('width').split('px')[0]);
//tdWidth = parseFloat(td.item(i).getComputedStyle('width').split('px')[0]); /* TODO: for some reason, using tdLiner.get('clientWidth') doesn't work - why not? */
}
//if TH is bigger than TD, enlarge TD Liner
//stylesheet.set(className,{'width': (thWidth - 20 + 'px')});
}
//if TD is bigger than TH, enlarge TH Liner
//stylesheet.set(className,{'width': (tdWidth - 20 + 'px')});
}
//}
}
//stylesheet.enable();
//After the widths have synced, there is a wrapping issue in the headerContainer in IE6. The header does not span the full
//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.
//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?
}
},
/**
* @description Adds the approriate width to the liner divs of the TH nodes before they are appended to DOM
*
* @method _attachTheadThNode
* @public
*/
_attachTheadThNode: function(o) {
if (w !== 'auto') {
o.th.get('firstChild').setStyles({'width': w, 'overflow':'hidden'}); //TODO: use liner API but liner is undefined here (not created?)
}
return o;
},
/**
* @description Adds the appropriate width to the liner divs of the TD nodes before they are appended to DOM
*
* @method _attachTbodyTdNode
* @public
*/
_attachTbodyTdNode: function(o) {
if (w !== 'auto') {
o.td.get('firstChild').setStyles({'width': w, 'overflow': 'hidden'}); //TODO: use liner API but liner is undefined here (not created?)
//o.td.setStyles({'width': width, 'overflow': 'hidden'});
}
return o;
},
/**
* @description Creates the body DIV that contains all the data.
*
* @method _createBodyContainer
* @private
*/
_createBodyContainer: function() {
this._bodyContainerNode = bd;
this._setStylesForTbody();
},
/**
* @description Creates the DIV that contains a <table> with all the headers.
*
* @method _createHeaderContainer
* @private
*/
_createHeaderContainer: function() {
this._headerContainerNode = hd;
//hd.setStyle('backgroundColor',this.get("COLOR_COLUMNFILLER"));
this._setStylesForThead();
},
/**
* @description Creates styles for the TBODY based on what type of table it is.
*
* @method _setStylesForTbody
* @private
*/
_setStylesForTbody: function() {
el = this._bodyContainerNode,
if (dir === 'x') {
//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.
styles['width'] = w;
}
else if (dir === 'y') {
//Y-Scrolling tables should not have a X-Scrollbar so overflow-x is hidden. The width isn't neccessary because it can be auto.
}
else {
//assume xy - the width must be set on xy.
styles['width'] = w;
}
return el;
},
/**
* @description Creates styles for the THEAD based on what type of datatable it is.
*
* @method _setStylesForThead
* @private
*/
_setStylesForThead: function() {
el = this._headerContainerNode;
if (dir !== 'y') {
}
},
/**
* @description Sets an auto width on the content box if it doesn't exist or if its a y-datatable.
*
* @method _setContentBoxDimensions
* @private
*/
_setContentBoxDimensions: function() {
}
},
/////////////////////////////////////////////////////////////////////////////
//
// Scroll Syncing
//
/////////////////////////////////////////////////////////////////////////////
/**
* @description Ensures that scrolling is synced across the two tables
*
* @method _onScroll
* @private
*/
_onScroll: function() {
},
/**
* @description Syncs padding around scrollable tables, including Column header right-padding
* and container width and height.
*
* @method _syncScroll
* @private
*/
_syncScroll : function() {
this._syncScrollX();
this._syncScrollY();
this._syncScrollOverhang();
// Bug 1925874
if(!this.get("width")) {
// Bug 1926125
}
}
},
/**
* @description Snaps container width for y-scrolling tables.
*
* @method _syncScrollY
* @private
*/
_syncScrollY : function() {
var tBody = this._parentTbodyNode,
tBodyContainer = this._bodyContainerNode,
w;
// X-scrolling not enabled
if(!this.get("width")) {
// Snap outer container width to content
// but account for y-scrollbar since it is visible
// no y-scrollbar, just borders
}
},
/**
* @description Snaps container height for x-scrolling tables in IE. Syncs message TBODY width.
* Taken from YUI2 ScrollingDataTable.js
*
* @method _syncScrollX
* @private
*/
_syncScrollX: function() {
var tBody = this._parentTbodyNode,
tBodyContainer = this._bodyContainerNode,
w;
}
this._parentMsgNode.get('parentNode').setStyle('width', this._parentTheadNode.get('parentNode').get('offsetWidth')+'px');
}
else {
}
},
/**
* Taken from YUI2 ScrollingDataTable.js
*
* @method _syncScrollOverhang
* @private
*/
_syncScrollOverhang: function() {
var tBodyContainer = this._bodyContainerNode,
padding = 1;
//when its both x and y scrolling
if ((tBodyContainer.get('scrollHeight') > tBodyContainer.get('clientHeight')) || (tBodyContainer.get('scrollWidth') > tBodyContainer.get('clientWidth'))) {
padding = 18;
}
this._setOverhangValue(padding);
},
/**
* @description Sets Column header overhang to given width.
* Taken from YUI2 ScrollingDataTable.js with minor modifications
*
* @method _setOverhangValue
* @param nBorderWidth {Number} Value of new border for overhang.
* @private
*/
_setOverhangValue: function(borderWidth) {
//lastHeaders = cols[cols.length-1] || [],
}
});
YUI.add('datatable', function(Y){}, '@VERSION@' ,{use:['datatable-base','datatable-sort','datatable-colresize','datatable-scroll']});