datatable.js revision 772c99f483000bc957c80ccb621d31f244e188f4
2521N/A///////////////////////////////////////////////////////////////////////////// 2521N/A///////////////////////////////////////////////////////////////////////////// 2521N/A // requires datatable-colresize 2521N/A * Returns classnames for Column. 3998N/A if(lang.isString(oColumn.className)) { 2521N/A allClasses = [oColumn.className]; 2521N/A else if(lang.isArray(oColumn.className)) { 2521N/A allClasses = oColumn.className; 2521N/A // Hook for setting width with via dynamic style uses key since ID is too disposable 2521N/A allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey(); 2521N/A // Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":" 2521N/A allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey(); 2521N/A var isSortedBy = this.get("sortedBy") || {}; 2521N/A if(oColumn.key === isSortedBy.key) { 2521N/A allClasses[allClasses.length] = isSortedBy.dir || ''; 2521N/A allClasses[allClasses.length] = DT.CLASS_HIDDEN; 2892N/A allClasses[allClasses.length] = DT.CLASS_SELECTED; 2521N/A allClasses[allClasses.length] = DT.CLASS_SORTABLE; 2521N/A allClasses[allClasses.length] = DT.CLASS_RESIZEABLE; 2892N/A allClasses[allClasses.length] = DT.CLASS_EDITABLE; 3998N/A allClasses = allClasses.concat(aAddClasses); 2925N/A return allClasses.join(' ');*/ 3998N/A///////////////////////////////////////////////////////////////////////////// 2892N/A///////////////////////////////////////////////////////////////////////////// 3998N/A // DOM tree representation of all Columns 2892N/A // Flat representation of all Columns 2892N/A // Hash of all Columns by ID 2892N/A // Flat representation of only Columns that are meant to display data 3998N/A/* Columnset extends Base */ 3998N/A // DOM tree representation of all Columns 3998N/A // Flat representation of all Columns 3998N/A // Hash of all Columns by ID 2521N/A // Flat representation of only Columns that are meant to display data 2892N/A // Internal recursive function to define Column instances 2521N/A // Create corresponding dom node if not already there for this depth 3998N/A // Parse each node at this depth for attributes and any children 3998N/A // Instantiate a new Column for each node 2892N/A // Cross-reference Column ID back to the original object literal definition 2892N/A // Add the new Column to the flat list 2892N/A // Add the new Column to the hash 2892N/A // Assign its parent as an attribute, if applicable 3998N/A // The Column has descendants 2892N/A // The children themselves must also be parsed for Column instances 2521N/A // This Column does not have any children 2521N/A // Add the Column to the top-down dom tree 2521N/A // Parse out Column instances from the array of object literals 2521N/A // Save to the Columnset instance 2521N/A // Cascade certain properties to children if not defined on their own 2892N/A // Determine COLSPAN value for this Column 2892N/A // Drill down each branch and count terminal nodes 2892N/A // Determine ROWSPAN value for each Column in the dom tree 3998N/A // Calculate the max depth of descendants for this row 2892N/A // Column has children, so keep counting 2892N/A // Column has children, so keep counting 2892N/A // No children, is it the max depth? 2892N/A // Count max row depth for each row 2892N/A // Assign the right ROWSPAN values to each Column in the row 2892N/A // Reset counter for next row 2892N/A //headers[i].push(oColumn.getSanitizedKey()); 2892N/A * The DataTable widget provides a progressively enhanced DHTML control for 2892N/A * displaying tabular data across A-grade browsers. 2892N/A * Provides the base DataTable implementation, which can be extended to add 2892N/A * additional functionality, such as sorting or scrolling. 2892N/A * @submodule datatable-base 2892N/A * Base class for the DataTable widget. 3998N/A TEMPLATE_TH =
'<th id="{id}" rowspan="{rowspan}" colspan="{colspan}" class="{classnames}"><div class="'+
CLASS_LINER+
'">{value}</div></th>',
3998N/A///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @value "dataSourceLocal" ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @description Pointer to Columnset instance. * @type Array | Y.Columnset * @description Pointer to Recordset instance. * @type Array | Y.Recordset * @description Internal state. * @description The collection of localizable strings used to label return Y.
Intl.
get(
"datatable-base");
* @attribute thValueTemplate * @description Tokenized markup template for TH value. * @attribute tdValueTemplate * @description Tokenized markup template for TD value. * @description Tokenized markup template for TR node creation. * @default '<tr id="{id}"></tr>' ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// /*caption: function (srcNode) { ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @description Tokenized markup template for TH node creation. * @default '<th id="{id}" rowspan="{rowspan}" colspan="{colspan}"><div class="'+CLASS_LINER+'">{value}</div></th>' * @description Tokenized markup template for TD node creation. * @default '<td headers="{headers}"><div class="'+CLASS_LINER+'">{value}</div></td>' * @description Pointer to THEAD node. * @description Pointer to TBODY node. * @description Pointer to message display node. ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @property _setColumnset * @description Converts Array to Y.Columnset. * @param columns {Array | Y.Columnset} * Updates the UI if changes are made to Columnset. * @method _afterColumnsetChange * @param e {Event} Custom event for the attribute change. * @property _setRecordset * @description Converts Array to Y.Recordset. * @param records {Array | Y.Recordset} * @property _afterRecordsetChange * @description Adds bubble target. * @param records {Array | Y.Recordset} ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @param config {Object} Config object. //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// * Creates and attaches TABLE element to given container. * @param containerNode {Y.Node} Parent node. * Creates and attaches COLGROUP element to given TABLE. * @method _addColgroupNode * @param tableNode {Y.Node} Parent node. // Add COLs to DOCUMENT FRAGMENT * Creates and attaches THEAD element to given container. * @param tableNode {Y.Node} Parent node. * Creates and attaches TBODY element to given container. * @param tableNode {Y.Node} Parent node. * Creates and attaches message display element to given container. * @method _addMessageNode * @param tableNode {Y.Node} Parent node. * Creates and attaches CAPTION element to given container. * @method _addCaptionNode * @param tableNode {Y.Node} Parent node. //TODO: node.createCaption //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // Define custom events that wrap DOM events. Simply pass through DOM //TODO: do we need queuable=true? //TODO: All the other events. // 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. * @param e {DOMEvent} The original DOM event facade. * @param type {String} Corresponding custom event to fire. //TODO: abstract this out this.
fire(
"theadRowClick", e);
this.
fire(
"theadClick", e);
//////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// * Syncs UI to intial state. * Updates the UI if changes are made to any of the strings in the strings * @method _afterStringsChange * @param e {Event} Custom event for the attribute change. * @param strings {Object} Collection of new strings. * @param val {String} New summary. * @param val {String} New caption. //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// * @method _uiSetColumnset * @param cs {Y.Columnset} New Columnset. //TODO: move thead off dom // Iterate tree of columns to add THEAD rows // 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. * Creates header row element. * @method _createTheadTrNode * @param o {Object} {thead, columns}. * @param isFirst {Boolean} Is first row. * @param isLast {Boolean} Is last row. //TODO: custom classnames * Attaches header row element. * @method _attachTheadTrNode * @param o {Object} {thead, columns, tr}. * Creates and attaches header cell element. * @method _addTheadThNode * @param o {Object} {value, column, tr}. * Creates header cell element. * @method _createTheadThNode * @param o {Object} {value, column, tr}. // Populate template object o.
id =
column.
get(
"id");
//TODO: validate 1 column ID per document //TODO o.abbr = column.get("abbr"); // 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}. //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// var i =
0,
//TODOthis.get("state.offsetIndex") o = {
tbody:
this.
_tbodyNode};
//TODO: not sure best time to do this -- depends on sdt // Iterate recordset to use existing or add new tr //TODO: attributes? or methods? },
'@VERSION@' ,{
requires:[
'intl',
'substitute',
'widget',
'recordset-base'],
lang:[
'en']});
YUI.
add(
'datatable-sort',
function(Y) {
//TODO: break out into own component var //getClassName = Y.ClassNameManager.getClassName, //DATATABLE = "datatable", //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>';
var dt =
this.
get(
"host");
// Wrap link around TH value this.
doBefore(
"_attachTheadThNode",
function(o) {
dt.
after(
"recordsetSort:sort",
function() {
dt.
after(
"sortedByChangeEvent",
function() {
//dt.after("recordset:mutation", function() {//reset sortedBy}); // Update UI after the fact (plug-then-render case) //TODO: normalize e.currentTarget to TH },
'@VERSION@' ,{
lang:[
'en'],
requires:[
'plugin',
'datatable-base',
'recordset-sort']});
YUI.
add(
'datatable-colresize',
function(Y) {
//CLASS_RESIZEABLE = GETCLASSNAME(DATATABLE, "resizeable"), NAME:
"dataTableColResize",
//TODO Set Column width... var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ? oColumn.minWidth : oColumn.width; if(DT._bDynStylesFallback) { elTh.firstChild.style.overflow = 'hidden'; elTh.firstChild.style.width = nWidth + 'px'; // ...for non fallback cases this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden'); },
'@VERSION@' ,{
requires:[
'plugin',
'dd',
'datatable-base']});
YUI.
add(
'datatable-scroll',
function(Y) {
* Extends DataTable base to enable x,y, and xy scrolling. * @submodule datatable-scroll TEMPLATE_TH =
'<th id="{id}" rowspan="{rowspan}" colspan="{colspan}"><div class="'+
CLASS_LINER+
'" style="width:100px">{value}</div></th>',
* @description The width for the table. Set to a string (ex: "200px", "20em") * @description The height for the table. Set to a string (ex: "200px", "20em") * @description The scrolling direction for the table. Can be set to 'x', 'y', or 'xy' * @description The hexadecimal colour value to set on the top-right of the table if a scrollbar exists. * @attribute COLOR_COLUMNFILLER * @description The base template for a td DOM element. * @description The base template for a th DOM element. * @description The table node created in datatable-base * @property _parentTableNode * @description The THEAD node which resides within the table node created in datatable-base * @property _parentTheadNode * @description The TBODY node which resides within the table node created in datatable-base * @property _parentTbodyNode * @description The TBODY Message node which resides within the table node created in datatable-base * @property _parentMsgNode * @description The contentBox specified for the datatable in datatable-base * @property _parentContainer * @description The DIV node that contains all the scrollable elements (a table with a tbody on it) * @property _bodyContainerNode * @description The DIV node that contains a table with a THEAD in it (which syncs its horizontal scroll with the _bodyContainerNode above) * @property _headerContainerNode //-------------------------------------- //-------------------------------------- var dt =
this.
get(
"host");
///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @description Set up methods to fire after host methods execute var dt =
this.
get(
'host');
if (
this.
get(
'scroll') ===
'y') {
* @description Stores the main <table> node provided by the host as a private property * @method _setUpParentTableNode * @description Stores the main <thead> node provided by the host as a private property * @method _setUpParentTheadNode * @description Stores the main <tbody> node provided by the host as a private property * @method _setUpParentTbodyNode * @description Stores the main <tbody> message node provided by the host as a private property * @method _setUpParentMessageNode ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @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. //Y.Profiler.start('render'); //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 //Y.Profiler.start('sync'); //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. //stylesheet = new YStyleSheet('columnsSheet'), 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 thLiner =
th.
item(i).
get(
'firstChild');
//TODO: use liner API - how? this is a node. 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 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. //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')}); //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? if (
ie &&
this.
get(
'scroll') ===
'y') {
* @description Adds the approriate width to the liner divs of the TH nodes before they are appended to DOM * @method _attachTheadThNode o.
th.
get(
'firstChild').
setStyles({
'width': w,
'overflow':
'hidden'});
//TODO: use liner API but liner is undefined here (not created?) * @description Adds the appropriate width to the liner divs of the TD nodes before they are appended to DOM * @method _attachTbodyTdNode 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'}); * @description Creates the body DIV that contains all the data. * @method _createBodyContainer * @description Creates the DIV that contains a <table> with all the headers. * @method _createHeaderContainer //hd.setStyle('backgroundColor',this.get("COLOR_COLUMNFILLER")); * @description Creates styles for the TBODY based on what type of table it is. * @method _setStylesForTbody w =
this.
get(
'width') ||
"",
h =
this.
get(
'height') ||
"",
styles = {
'width':
"",
'height':h};
//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[
'overflowY'] =
'hidden';
//Y-Scrolling tables should not have a X-Scrollbar so overflow-x is hidden. The width isn't neccessary because it can be auto. styles[
'overflowX'] =
'hidden';
//assume xy - the width must be set on xy. * @description Creates styles for the THEAD based on what type of datatable it is. * @method _setStylesForThead w =
this.
get(
'width') ||
"",
* @description Sets an auto width on the content box if it doesn't exist or if its a y-datatable. * @method _setContentBoxDimensions if (
this.
get(
'scroll') ===
'y' || (!
this.
get(
'width'))) {
///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @description Ensures that scrolling is synced across the two tables * @description Syncs padding around scrollable tables, including Column header right-padding * and container width and height. * @description Snaps container width for y-scrolling tables. // X-scrolling not enabled // Snap outer container width to content // but account for y-scrollbar since it is visible (
tBody.
get(
'parentNode').
get(
'clientWidth') +
19) +
"px" :
// no y-scrollbar, just borders (
tBody.
get(
'parentNode').
get(
'clientWidth') +
2) +
"px";
* @description Snaps container height for x-scrolling tables in IE. Syncs message TBODY width. (
tBody.
get(
'parentNode').
get(
'offsetHeight') +
18) +
"px" :
* @description Adds/removes Column header overhang as necesary. * @method _syncScrollOverhang //when its both x and y scrolling * @description Sets Column header overhang to given width. * @method _setOverhangValue * @param nBorderWidth {Number} Value of new border for overhang. //lastHeaders = cols[cols.length-1] || [], },
'@VERSION@' ,{
requires:[
'plugin',
'datatable-base',
'stylesheet']});
YUI.
add(
'datatable',
function(Y){},
'@VERSION@' ,{
use:[
'datatable-base',
'datatable-sort',
'datatable-colresize',
'datatable-scroll']});