/* Copyright (c) 2007, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.3.0 */ /** * The DataTable widget provides a progressively enhanced DHTML control for * displaying tabular data across A-grade browsers. * * @module datatable * @requires yahoo, dom, event, datasource * @optional dragdrop * @title DataTable Widget * @beta */ /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /** * DataTable class for the YUI DataTable widget. * * @namespace YAHOO.widget * @class DataTable * @uses YAHOO.util.EventProvider * @constructor * @param elContainer {HTMLElement} Container element for the TABLE. * @param aColumnDefs {Object[]} Array of object literal Column definitions. * @param oDataSource {YAHOO.util.DataSource} DataSource instance. * @param oConfigs {object} (optional) Object literal of configuration values. */ YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) { // Internal vars this._nIndex = YAHOO.widget.DataTable._nCount; this._sName = "instance" + this._nIndex; this.id = "yui-dt"+this._nIndex; // Initialize container element this._initContainerEl(elContainer); if(!this._elContainer) { YAHOO.log("Could not instantiate DataTable due to an invalid container element", "error", this.toString()); return; } // Initialize configs this._initConfigs(oConfigs); // Initialize ColumnSet this._initColumnSet(aColumnDefs); if(!this._oColumnSet) { YAHOO.log("Could not instantiate DataTable due to an invalid ColumnSet", "error", this.toString()); return; } // Initialize RecordSet this._initRecordSet(); if(!this._oRecordSet) { YAHOO.log("Could not instantiate DataTable due to an invalid RecordSet", "error", this.toString()); return; } // Initialize DataSource this._initDataSource(oDataSource); if(!this._oDataSource) { YAHOO.log("Could not instantiate DataTable due to an invalid DataSource", "error", this.toString()); return; } // Progressive enhancement special case if(this._oDataSource.dataType == YAHOO.util.DataSource.TYPE_HTMLTABLE) { this._oDataSource.sendRequest(this.get("initialRequest"), this._onDataReturnEnhanceTable, this); } else { // Initialize DOM elements this._initTableEl(); if(!this._elTable || !this._elThead || !this._elTbody) { YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString()); return; } // Call Element's constructor after DOM elements are created // but *before* table is populated with data YAHOO.widget.DataTable.superclass.constructor.call(this, this._elContainer, this._oConfigs); //HACK: Set the Paginator values here via updatePaginator if(this._oConfigs && this._oConfigs.paginator) { this.updatePaginator(this._oConfigs.paginator); } // Send out for data in an asynchronous request this._oDataSource.sendRequest(this.get("initialRequest"), this.onDataReturnInitializeTable, this); } // Initialize inline Cell editing this._initCellEditorEl(); // Initialize Column sort this._initColumnSort(); // Initialize DOM event listeners this._initDomEvents(); YAHOO.widget.DataTable._nCount++; YAHOO.log("DataTable initialized", "info", this.toString()); }; if(YAHOO.util.Element) { YAHOO.lang.extend(YAHOO.widget.DataTable, YAHOO.util.Element); } else { YAHOO.log("Missing dependency: YAHOO.util.Element","error",this.toString()); } ///////////////////////////////////////////////////////////////////////////// // // Superclass methods // ///////////////////////////////////////////////////////////////////////////// /** * Implementation of Element's abstract method. Sets up config values. * * @method initAttributes * @param oConfigs {Object} (Optional) Object literal definition of configuration values. * @private */ YAHOO.widget.DataTable.prototype.initAttributes = function(oConfigs) { oConfigs = oConfigs || {}; YAHOO.widget.DataTable.superclass.initAttributes.call(this, oConfigs); /** * @config summary * @description Value for the SUMMARY attribute. * @type String */ this.setAttributeConfig("summary", { value: null, validator: YAHOO.lang.isString, method: function(sSummary) { this._elTable.summary = sSummary; } }); /** * @config selectionMode * @description Specifies row or cell selection mode. Accepts the following strings: *
*
"standard"
*
Standard row selection with support for modifier keys to enable * multiple selections.
* *
"single"
*
Row selection with modifier keys disabled to not allow * multiple selections.
* *
"singlecell"
*
Cell selection with modifier keys disabled to not allow * multiple selections.
* *
"cellblock"
*
Cell selection with support for modifier keys to enable multiple * selections in a block-fashion, like a spreadsheet.
* *
"cellrange"
*
Cell selection with support for modifier keys to enable multiple * selections in a range-fashion, like a calendar.
*
* * @default "standard" * @type String */ this.setAttributeConfig("selectionMode", { value: "standard", validator: YAHOO.lang.isString }); /** * @config initialRequest * @description Defines the initial request that gets sent to the DataSource. * @type String */ this.setAttributeConfig("initialRequest", { value: "", validator: YAHOO.lang.isString }); /** * @config sortedBy * @description Object literal provides metadata for initial sort values if * data will arrive pre-sorted: *
*
sortedBy.key
*
Key of sorted Column
*
sortedBy.dir
*
Initial sort direction, either "asc" or "desc"
*
* @type Object */ this.setAttributeConfig("sortedBy", { value: null, // TODO: accepted array for nested sorts validator: function(oNewSortedBy) { return (oNewSortedBy && (oNewSortedBy.constructor == Object) && oNewSortedBy.key); }, method: function(oNewSortedBy) { // Remove ASC/DESC from TH var oOldSortedBy = this.get("sortedBy"); if(oOldSortedBy && (oOldSortedBy.constructor == Object) && oOldSortedBy.key) { var oldColumn = this._oColumnSet.getColumn(oOldSortedBy.key); var oldThEl = this.getThEl(oldColumn); YAHOO.util.Dom.removeClass(oldThEl, YAHOO.widget.DataTable.CLASS_ASC); YAHOO.util.Dom.removeClass(oldThEl, YAHOO.widget.DataTable.CLASS_DESC); } // Set ASC/DESC on TH var column = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key); if(column) { var newClass = (oNewSortedBy.dir && (oNewSortedBy.dir != "asc")) ? YAHOO.widget.DataTable.CLASS_DESC : YAHOO.widget.DataTable.CLASS_ASC; YAHOO.util.Dom.addClass(this.id + "-col" + column.getId(), newClass); } } }); /** * @config paginator * @description Object literal of pagination values. * @default
* { containers:[], // UI container elements
* rowsPerPage:500, // 500 rows
* currentPage:1, // page one
* pageLinks:0, // show all links
* pageLinksStart:1, // first link is page 1
* dropdownOptions:null, // no dropdown
* links: [], // links elements
* dropdowns: [] } //dropdown elements * * @type Object */ this.setAttributeConfig("paginator", { value: { rowsPerPage:500, // 500 rows per page currentPage:1, // show page one startRecordIndex:0, // start with first Record totalRecords:0, // how many Records total totalPages:0, // how many pages total rowsThisPage:0, // how many rows this page pageLinks:0, // show all links pageLinksStart:1, // first link is page 1 dropdownOptions: null, //no dropdown containers:[], // Paginator container element references dropdowns: [], //dropdown element references, links: [] // links elements }, validator: function(oNewPaginator) { if(oNewPaginator && (oNewPaginator.constructor == Object)) { // Check for incomplete set of values if((oNewPaginator.rowsPerPage !== undefined) && (oNewPaginator.currentPage !== undefined) && (oNewPaginator.startRecordIndex !== undefined) && (oNewPaginator.totalRecords !== undefined) && (oNewPaginator.totalPages !== undefined) && (oNewPaginator.rowsThisPage !== undefined) && (oNewPaginator.pageLinks !== undefined) && (oNewPaginator.pageLinksStart !== undefined) && (oNewPaginator.dropdownOptions !== undefined) && (oNewPaginator.containers !== undefined) && (oNewPaginator.dropdowns !== undefined) && (oNewPaginator.links !== undefined)) { // Validate each value if(YAHOO.lang.isNumber(oNewPaginator.rowsPerPage) && YAHOO.lang.isNumber(oNewPaginator.currentPage) && YAHOO.lang.isNumber(oNewPaginator.startRecordIndex) && YAHOO.lang.isNumber(oNewPaginator.totalRecords) && YAHOO.lang.isNumber(oNewPaginator.totalPages) && YAHOO.lang.isNumber(oNewPaginator.rowsThisPage) && YAHOO.lang.isNumber(oNewPaginator.pageLinks) && YAHOO.lang.isNumber(oNewPaginator.pageLinksStart) && YAHOO.lang.isArray(oNewPaginator.dropdownOptions) && YAHOO.lang.isArray(oNewPaginator.containers) && YAHOO.lang.isArray(oNewPaginator.dropdowns) && YAHOO.lang.isArray(oNewPaginator.links)) { return true; } } } return false; } }); /** * @config paginated * @description True if built-in client-side pagination is enabled * @default false * @type Boolean */ this.setAttributeConfig("paginated", { value: false, validator: YAHOO.lang.isBoolean, method: function(oParam) { var oPaginator = this.get("paginator"); var aContainerEls = oPaginator.containers; // Paginator is enabled if(oParam) { // No containers found, create two from scratch if(aContainerEls.length === 0) { // One before TABLE var pag0 = document.createElement("span"); pag0.id = this.id + "-paginator0"; YAHOO.util.Dom.addClass(pag0, YAHOO.widget.DataTable.CLASS_PAGINATOR); pag0 = this._elContainer.insertBefore(pag0, this._elTable); aContainerEls.push(pag0); // One after TABLE var pag1 = document.createElement("span"); pag1.id = this.id + "-paginator1"; YAHOO.util.Dom.addClass(pag1, YAHOO.widget.DataTable.CLASS_PAGINATOR); pag1 = this._elContainer.insertBefore(pag1, this._elTable.nextSibling); aContainerEls.push(pag1); // Add containers directly to tracker this._configs.paginator.value.containers = [pag0, pag1]; } else { // Show each container for(var i=0; i -1) { var aLinkEls = oPaginator.links; // No links containers found, create from scratch if(aLinkEls.length === 0) { for(i=0; i 0) { // Destroy or just hide? // Hide each container for(i=0; i" + sLabel + ""; if(!this._sFirstLabelLinkId) { this._sFirstLabelLinkId = sLabelLinkId; } } else { elTheadLabel.innerHTML = sLabel; } }; /** * Creates HTML markup for Cell Editor. * * @method _initCellEditorEl * @private */ YAHOO.widget.DataTable.prototype._initCellEditorEl = function() { // Attach Cell Editor container element to body var elCellEditor = document.createElement("div"); elCellEditor.id = this.id + "-celleditor"; elCellEditor.style.display = "none"; YAHOO.util.Dom.addClass(elCellEditor, YAHOO.widget.DataTable.CLASS_EDITOR); elCellEditor = document.body.appendChild(elCellEditor); // Internal tracker of Cell Editor values var oCellEditor = {}; oCellEditor.container = elCellEditor; oCellEditor.value = null; oCellEditor.isActive = false; this._oCellEditor = oCellEditor; // Handle ESC key this.subscribe("editorKeydownEvent", function(oArgs) { var e = oArgs.event; var elTarget = YAHOO.util.Event.getTarget(e); // ESC hides Cell Editor if((e.keyCode == 27)) { this.cancelCellEditor(); } }); }; /** * Initializes Column sorting. * * @method _initColumnSort * @private */ YAHOO.widget.DataTable.prototype._initColumnSort = function() { this.subscribe("headerCellClickEvent", this.onEventSortColumn); }; /** * Initializes DOM event listeners. * * @method _initDomEvents * @private */ YAHOO.widget.DataTable.prototype._initDomEvents = function() { var elTable = this._elTable; var elThead = this._elThead; var elTbody = this._elTbody; var elContainer = this._elContainer; YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this); YAHOO.util.Event.addListener(document, "keydown", this._onDocumentKeydown, this); YAHOO.util.Event.addListener(elTable, "focus", this._onTableFocus, this); YAHOO.util.Event.addListener(elTable, "mouseover", this._onTableMouseover, this); YAHOO.util.Event.addListener(elTable, "mouseout", this._onTableMouseout, this); YAHOO.util.Event.addListener(elTable, "mousedown", this._onTableMousedown, this); YAHOO.util.Event.addListener(elTable, "keydown", this._onTableKeydown, this); YAHOO.util.Event.addListener(elTable, "keypress", this._onTableKeypress, this); // Since we can't listen for click and dblclick on the same element... YAHOO.util.Event.addListener(elTable, "dblclick", this._onTableDblclick, this); YAHOO.util.Event.addListener(elThead, "click", this._onTheadClick, this); YAHOO.util.Event.addListener(elTbody, "click", this._onTbodyClick, this); YAHOO.util.Event.addListener(elContainer, "scroll", this._onScroll, this); // for IE YAHOO.util.Event.addListener(elTbody, "scroll", this._onScroll, this); // for everyone else }; // DOM MUTATION FUNCTIONS /** * Adds a TR element to the primary TBODY at the page row index if given, otherwise * at the end of the page. Formats TD elements within the TR element using data * from the given Record. * * @method _addTrEl * @param oRecord {YAHOO.widget.Record} Record instance. * @param index {Number} (optional) The page row index at which to add the TR * element. * @return {String} ID of the added TR element, or null. * @private */ YAHOO.widget.DataTable.prototype._addTrEl = function(oRecord, index) { this.hideTableMessage(); // It's an append if no index provided, or index is negative or too big var append = (!YAHOO.lang.isNumber(index) || (index < 0) || (index >= (this._elTbody.rows.length))) ? true : false; var oColumnSet = this._oColumnSet; var oRecordSet = this._oRecordSet; var isSortedBy = this.get("sortedBy"); var sortedColKeyIndex = null; var sortedDir, newClass; if(isSortedBy) { sortedColKeyIndex = (isSortedBy.column) ? isSortedBy.column.getKeyIndex() : this._oColumnSet.getColumn(isSortedBy.key).getKeyIndex(); sortedDir = isSortedBy.dir; newClass = (sortedDir === "desc") ? YAHOO.widget.DataTable.CLASS_DESC : YAHOO.widget.DataTable.CLASS_ASC; } var elRow = (append) ? this._elTbody.appendChild(document.createElement("tr")) : this._elTbody.insertBefore(document.createElement("tr"),this._elTbody.rows[index]); elRow.id = this.id+"-bdrow"+this._nTrCount; this._nTrCount++; elRow.yuiRecordId = oRecord.getId(); // Create TD cells for(var j=0; j -2) && (rowIndex < this._elTbody.rows.length)) { this._elTbody.deleteRow(rowIndex); return true; } else { return false; } }; // CSS/STATE FUNCTIONS /** * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element * of the DataTable page and updates internal tracker. * * @method _setFirstRow * @private */ YAHOO.widget.DataTable.prototype._setFirstRow = function() { var rowEl = this.getFirstTrEl(); if(rowEl) { // Remove FIRST if(this._sFirstTrId) { YAHOO.util.Dom.removeClass(this._sFirstTrId, YAHOO.widget.DataTable.CLASS_FIRST); } // Set FIRST YAHOO.util.Dom.addClass(rowEl, YAHOO.widget.DataTable.CLASS_FIRST); this._sFirstTrId = rowEl.id; } else { this._sFirstTrId = null; } }; /** * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element * of the DataTable page and updates internal tracker. * * @method _setLastRow * @private */ YAHOO.widget.DataTable.prototype._setLastRow = function() { var rowEl = this.getLastTrEl(); if(rowEl) { // Unassign previous class if(this._sLastTrId) { YAHOO.util.Dom.removeClass(this._sLastTrId, YAHOO.widget.DataTable.CLASS_LAST); } // Assign class YAHOO.util.Dom.addClass(rowEl, YAHOO.widget.DataTable.CLASS_LAST); this._sLastTrId = rowEl.id; } else { this._sLastTrId = null; } }; /** * Assigns the classes YAHOO.widget.DataTable.CLASS_EVEN and * YAHOO.widget.DataTable.CLASS_ODD to alternating TR elements of the DataTable * page. For performance, a subset of rows may be specified. * * @method _setRowStripes * @param row {HTMLElement | String | Number} (optional) HTML TR element reference * or string ID, or page row index of where to start striping. * @param range {Number} (optional) If given, how many rows to stripe, otherwise * stripe all the rows until the end. * @private */ YAHOO.widget.DataTable.prototype._setRowStripes = function(row, range) { // Default values stripe all rows var allRows = this._elTbody.rows; var nStartIndex = 0; var nEndIndex = allRows.length; // Stripe a subset if((row !== null) && (row !== undefined)) { // Validate given start row var elStartRow = this.getTrEl(row); if(elStartRow) { nStartIndex = elStartRow.sectionRowIndex; // Validate given range if(YAHOO.lang.isNumber(range) && (range > 1)) { nEndIndex = nStartIndex + range; } } } for(var i=nStartIndex; i 36) && (nKey < 41)) { YAHOO.util.Event.stopEvent(e); } else { return; } var sMode = oSelf.get("selectionMode"); var allRows = oSelf._elTbody.rows; var anchorId = oSelf._sSelectionAnchorId; var anchorEl = YAHOO.util.Dom.get(anchorId); var newSelectedEl, trIndex, tdIndex, startIndex, endIndex, i, anchorPos; //////////////////////////////////////////////////////////////////////// // // SHIFT cell block selection // //////////////////////////////////////////////////////////////////////// if(bSHIFT && (sMode == "cellblock")) { trIndex = lastSelectedEl.parentNode.sectionRowIndex; tdIndex = lastSelectedEl.yuiCellIndex; // Arrow DOWN if(nKey == 40) { // Is the anchor cell above, below, or same row if(anchorEl.parentNode.sectionRowIndex > trIndex) { anchorPos = 1; } else if(anchorEl.parentNode.sectionRowIndex < trIndex) { anchorPos = -1; } else { anchorPos = 0; } // Is the anchor cell left or right startIndex = Math.min(anchorEl.yuiCellIndex, tdIndex); endIndex = Math.max(anchorEl.yuiCellIndex, tdIndex); // Selecting away from anchor cell if(anchorPos <= 0) { // Select the horiz block on the next row if(trIndex < allRows.length-1) { for(i=startIndex; i<=endIndex; i++) { newSelectedEl = allRows[trIndex+1].cells[i]; oSelf.selectCell(newSelectedEl); } oSelf._sLastSelectedId = allRows[trIndex+1].cells[tdIndex].id; } } // Unselecting towards anchor cell else { // Unselect the horiz block on this row towards the next row for(i=startIndex; i<=endIndex; i++) { oSelf.unselectCell(allRows[trIndex].cells[i]); } oSelf._sLastSelectedId = allRows[trIndex+1].cells[tdIndex].id; } } // Arrow up else if(nKey == 38) { // Is the anchor cell above, below, or same row if(anchorEl.parentNode.sectionRowIndex > trIndex) { anchorPos = 1; } else if(anchorEl.parentNode.sectionRowIndex < trIndex) { anchorPos = -1; } else { anchorPos = 0; } // Is the anchor cell left or right? startIndex = Math.min(anchorEl.yuiCellIndex, tdIndex); endIndex = Math.max(anchorEl.yuiCellIndex, tdIndex); // Selecting away from anchor cell if(anchorPos >= 0) { // Select the horiz block on the previous row if(trIndex > 0) { for(i=startIndex; i<=endIndex; i++) { newSelectedEl = allRows[trIndex-1].cells[i]; oSelf.selectCell(newSelectedEl); } oSelf._sLastSelectedId = allRows[trIndex-1].cells[tdIndex].id; } } // Unselecting towards anchor cell else { // Unselect the horiz block on this row towards the previous row for(i=startIndex; i<=endIndex; i++) { oSelf.unselectCell(allRows[trIndex].cells[i]); } oSelf._sLastSelectedId = allRows[trIndex-1].cells[tdIndex].id; } } // Arrow right else if(nKey == 39) { // Is the anchor cell left, right, or same column if(anchorEl.yuiCellIndex > tdIndex) { anchorPos = 1; } else if(anchorEl.yuiCellIndex < tdIndex) { anchorPos = -1; } else { anchorPos = 0; } // Selecting away from anchor cell if(anchorPos <= 0) { //Select the next vert block to the right if(tdIndex < allRows[trIndex].cells.length-1) { startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex); endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex); for(i=startIndex; i<=endIndex; i++) { newSelectedEl = allRows[i].cells[tdIndex+1]; oSelf.selectCell(newSelectedEl); } oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id; } } // Unselecting towards anchor cell else { // Unselect the vert block on this column towards the right startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex); endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex); for(i=startIndex; i<=endIndex; i++) { oSelf.unselectCell(allRows[i].cells[tdIndex]); } oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id; } } // Arrow left else if(nKey == 37) { // Is the anchor cell left, right, or same column if(anchorEl.yuiCellIndex > tdIndex) { anchorPos = 1; } else if(anchorEl.yuiCellIndex < tdIndex) { anchorPos = -1; } else { anchorPos = 0; } // Selecting away from anchor cell if(anchorPos >= 0) { //Select the previous vert block to the left if(tdIndex > 0) { startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex); endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex); for(i=startIndex; i<=endIndex; i++) { newSelectedEl = allRows[i].cells[tdIndex-1]; oSelf.selectCell(newSelectedEl); } oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id; } } // Unselecting towards anchor cell else { // Unselect the vert block on this column towards the left startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex); endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex); for(i=startIndex; i<=endIndex; i++) { oSelf.unselectCell(allRows[i].cells[tdIndex]); } oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id; } } } //////////////////////////////////////////////////////////////////////// // // SHIFT cell range selection // //////////////////////////////////////////////////////////////////////// else if(bSHIFT && (sMode == "cellrange")) { trIndex = lastSelectedEl.parentNode.sectionRowIndex; tdIndex = lastSelectedEl.yuiCellIndex; // Is the anchor cell above, below, or same row if(anchorEl.parentNode.sectionRowIndex > trIndex) { anchorPos = 1; } else if(anchorEl.parentNode.sectionRowIndex < trIndex) { anchorPos = -1; } else { anchorPos = 0; } // Arrow down if(nKey == 40) { // Selecting away from anchor cell if(anchorPos <= 0) { // Select all cells to the end of this row for(i=tdIndex+1; i= 0) { // Select all the cells to the beginning of this row for(i=tdIndex-1; i>-1; i--){ newSelectedEl = allRows[trIndex].cells[i]; oSelf.selectCell(newSelectedEl); } // Select some of the cells from the end of the previous row if(trIndex > 0) { for(i=allRows[trIndex].cells.length-1; i>=tdIndex; i--){ newSelectedEl = allRows[trIndex-1].cells[i]; oSelf.selectCell(newSelectedEl); } } } // Unselecting towards anchor cell else { // Unselect all the cells to the beginning of this row for(i=tdIndex; i>-1; i--){ oSelf.unselectCell(allRows[trIndex].cells[i]); } // Unselect some of the cells from the end of the previous row for(i=allRows[trIndex].cells.length-1; i>tdIndex; i--){ oSelf.unselectCell(allRows[trIndex-1].cells[i]); } oSelf._sLastSelectedId = allRows[trIndex-1].cells[tdIndex].id; } } // Arrow right else if(nKey == 39) { // Selecting away from anchor cell if(anchorPos < 0) { // Select the next cell to the right if(tdIndex < allRows[trIndex].cells.length-1) { newSelectedEl = allRows[trIndex].cells[tdIndex+1]; oSelf.selectCell(newSelectedEl); } // Select the first cell of the next row else if(trIndex < allRows.length-1) { newSelectedEl = allRows[trIndex+1].cells[0]; oSelf.selectCell(newSelectedEl); } } // Unselecting towards anchor cell else if(anchorPos > 0) { oSelf.unselectCell(allRows[trIndex].cells[tdIndex]); // Unselect this cell towards the right if(tdIndex < allRows[trIndex].cells.length-1) { oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id; } // Unselect this cells towards the first cell of the next row else { oSelf._sLastSelectedId = allRows[trIndex+1].cells[0].id; } } // Anchor is on this row else { // Selecting away from anchor if(anchorEl.yuiCellIndex <= tdIndex) { // Select the next cell to the right if(tdIndex < allRows[trIndex].cells.length-1) { newSelectedEl = allRows[trIndex].cells[tdIndex+1]; oSelf.selectCell(newSelectedEl); } // Select the first cell on the next row else if(trIndex < allRows.length-1){ newSelectedEl = allRows[trIndex+1].cells[0]; oSelf.selectCell(newSelectedEl); } } // Unselecting towards anchor else { // Unselect this cell towards the right oSelf.unselectCell(allRows[trIndex].cells[tdIndex]); oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id; } } } // Arrow left else if(nKey == 37) { // Unselecting towards the anchor if(anchorPos < 0) { oSelf.unselectCell(allRows[trIndex].cells[tdIndex]); // Unselect this cell towards the left if(tdIndex > 0) { oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id; } // Unselect this cell towards the last cell of the previous row else { oSelf._sLastSelectedId = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1].id; } } // Selecting towards the anchor else if(anchorPos > 0) { // Select the next cell to the left if(tdIndex > 0) { newSelectedEl = allRows[trIndex].cells[tdIndex-1]; oSelf.selectCell(newSelectedEl); } // Select the last cell of the previous row else if(trIndex > 0){ newSelectedEl = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1]; oSelf.selectCell(newSelectedEl); } } // Anchor is on this row else { // Selecting away from anchor cell if(anchorEl.yuiCellIndex >= tdIndex) { // Select the next cell to the left if(tdIndex > 0) { newSelectedEl = allRows[trIndex].cells[tdIndex-1]; oSelf.selectCell(newSelectedEl); } // Select the last cell of the previous row else if(trIndex > 0){ newSelectedEl = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1]; oSelf.selectCell(newSelectedEl); } } // Unselecting towards anchor cell else { oSelf.unselectCell(allRows[trIndex].cells[tdIndex]); // Unselect this cell towards the left if(tdIndex > 0) { oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id; } // Unselect this cell towards the last cell of the previous row else { oSelf._sLastSelectedId = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1].id; } } } } } //////////////////////////////////////////////////////////////////////// // // Simple single cell selection // //////////////////////////////////////////////////////////////////////// else if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) { trIndex = lastSelectedEl.parentNode.sectionRowIndex; tdIndex = lastSelectedEl.yuiCellIndex; // Arrow down if(nKey == 40) { oSelf.unselectAllCells(); // Select the next cell down if(trIndex < allRows.length-1) { newSelectedEl = allRows[trIndex+1].cells[tdIndex]; oSelf.selectCell(newSelectedEl); } // Select only the bottom cell else { newSelectedEl = lastSelectedEl; oSelf.selectCell(newSelectedEl); } oSelf._sSelectionAnchorId = newSelectedEl.id; } // Arrow up else if(nKey == 38) { oSelf.unselectAllCells(); // Select the next cell up if(trIndex > 0) { newSelectedEl = allRows[trIndex-1].cells[tdIndex]; oSelf.selectCell(newSelectedEl); } // Select only the top cell else { newSelectedEl = lastSelectedEl; oSelf.selectCell(newSelectedEl); } oSelf._sSelectionAnchorId = newSelectedEl.id; } // Arrow right else if(nKey == 39) { oSelf.unselectAllCells(); // Select the next cell to the right if(tdIndex < lastSelectedEl.parentNode.cells.length-1) { newSelectedEl = lastSelectedEl.parentNode.cells[tdIndex+1]; oSelf.selectCell(newSelectedEl); } // Select only the right cell else { newSelectedEl = lastSelectedEl; oSelf.selectCell(newSelectedEl); } oSelf._sSelectionAnchorId = newSelectedEl.id; } // Arrow left else if(nKey == 37) { oSelf.unselectAllCells(); // Select the next cell to the left if(tdIndex > 0) { newSelectedEl = lastSelectedEl.parentNode.cells[tdIndex-1]; oSelf.selectCell(newSelectedEl); } // Select only the left cell else { newSelectedEl = lastSelectedEl; oSelf.selectCell(newSelectedEl); } oSelf._sSelectionAnchorId = newSelectedEl.id; } } //////////////////////////////////////////////////////////////////////// // // SHIFT row selection // //////////////////////////////////////////////////////////////////////// else if(bSHIFT && (sMode != "single")) { trIndex = lastSelectedEl.sectionRowIndex; if(anchorEl.sectionRowIndex > trIndex) { anchorPos = 1; } else if(anchorEl.sectionRowIndex < trIndex) { anchorPos = -1; } else { anchorPos = 0; } // Arrow down if(nKey == 40) { // Selecting away from anchor row if(anchorPos <= 0) { // Select the next row down if(trIndex < allRows.length-1) { oSelf.selectRow(trIndex+1); } } // Unselecting toward anchor row else { // Unselect this row towards the anchor row down oSelf.unselectRow(lastSelectedEl); oSelf._sLastSelectedId = allRows[trIndex+1].id; } } // Arrow up else if(nKey == 38) { // Selecting away from anchor row if(anchorPos >= 0) { // Select the next row up if(trIndex > 0) { oSelf.selectRow(trIndex-1); } } // Unselect this row towards the anchor row up else { oSelf.unselectRow(lastSelectedEl); oSelf._sLastSelectedId = allRows[trIndex-1].id; } } // Arrow right else if(nKey == 39) { // Do nothing } // Arrow left else if(nKey == 37) { // Do nothing } } //////////////////////////////////////////////////////////////////////// // // Simple single row selection // //////////////////////////////////////////////////////////////////////// else { trIndex = lastSelectedEl.sectionRowIndex; // Arrow down if(nKey == 40) { oSelf.unselectAllRows(); // Select the next row if(trIndex < allRows.length-1) { newSelectedEl = allRows[trIndex+1]; oSelf.selectRow(newSelectedEl); } // Select only the last row else { newSelectedEl = lastSelectedEl; oSelf.selectRow(lastSelectedEl); } oSelf._sSelectionAnchorId = newSelectedEl.id; } // Arrow up else if(nKey == 38) { oSelf.unselectAllRows(); // Select the previous row if(trIndex > 0) { newSelectedEl = allRows[trIndex-1]; oSelf.selectRow(newSelectedEl); } // Select only the first row else { newSelectedEl = lastSelectedEl; oSelf.selectRow(newSelectedEl); } oSelf._sSelectionAnchorId = newSelectedEl.id; } // Arrow right else if(nKey == 39) { // Do nothing } // Arrow left else if(nKey == 37) { // Do nothing } } } }; /** * Handles keypress events on the TABLE. Mainly to support stopEvent on Mac. * * @method _onTableKeypress * @param e {HTMLEvent} The key event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onTableKeypress = function(e, oSelf) { var isMac = (navigator.userAgent.toLowerCase().indexOf("mac") != -1); if(isMac) { var nKey = YAHOO.util.Event.getCharCode(e); // arrow down if(nKey == 40) { YAHOO.util.Event.stopEvent(e); } // arrow up else if(nKey == 38) { YAHOO.util.Event.stopEvent(e); } } }; /** * Handles click events on the THEAD element. * * @method _onTheadClick * @param e {HTMLEvent} The click event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onTheadClick = function(e, oSelf) { var elTarget = YAHOO.util.Event.getTarget(e); var elTag = elTarget.tagName.toLowerCase(); if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) { oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); } while(elTarget && (elTag != "thead")) { switch(elTag) { case "body": break; case "span": if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) { oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e}); } break; case "th": oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e}); break; case "tr": oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e}); break; default: break; } elTarget = elTarget.parentNode; if(elTarget) { elTag = elTarget.tagName.toLowerCase(); } } oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elTable),event:e}); }; /** * Handles click events on the primary TBODY element. * * @method _onTbodyClick * @param e {HTMLEvent} The click event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onTbodyClick = function(e, oSelf) { var elTarget = YAHOO.util.Event.getTarget(e); var elTag = elTarget.tagName.toLowerCase(); if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) { oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); } while(elTarget && (elTag != "table")) { switch(elTag) { case "body": break; case "input": if(elTarget.type.toLowerCase() == "checkbox") { oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e}); } else if(elTarget.type.toLowerCase() == "radio") { oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e}); } break; case "a": oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e}); break; case "button": oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e}); break; case "td": oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e}); break; case "tr": oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e}); break; default: break; } elTarget = elTarget.parentNode; if(elTarget) { elTag = elTarget.tagName.toLowerCase(); } } oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elTable),event:e}); }; /*TODO: delete * Handles keyup events on the TBODY. Executes deletion. * * @method _onTbodyKeyup * @param e {HTMLEvent} The key event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ /*YAHOO.widget.DataTable.prototype._onTbodyKeyup = function(e, oSelf) { var nKey = YAHOO.util.Event.getCharCode(e); // delete if(nKey == 46) {//TODO: if something is selected //TODO: delete row } };*/ /** * Handles click events on paginator links. * * @method _onPaginatorLinkClick * @param e {HTMLEvent} The click event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onPaginatorLinkClick = function(e, oSelf) { var elTarget = YAHOO.util.Event.getTarget(e); var elTag = elTarget.tagName.toLowerCase(); if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) { oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor}); } while(elTarget && (elTag != "table")) { switch(elTag) { case "body": return; case "a": YAHOO.util.Event.stopEvent(e); //TODO: after the showPage call, figure out which link //TODO: was clicked and reset focus to the new version of it switch(elTarget.className) { case YAHOO.widget.DataTable.CLASS_PAGE: oSelf.showPage(parseInt(elTarget.innerHTML,10)); return; case YAHOO.widget.DataTable.CLASS_FIRST: oSelf.showPage(1); return; case YAHOO.widget.DataTable.CLASS_LAST: oSelf.showPage(oSelf.get("paginator").totalPages); return; case YAHOO.widget.DataTable.CLASS_PREVIOUS: oSelf.showPage(oSelf.get("paginator").currentPage - 1); return; case YAHOO.widget.DataTable.CLASS_NEXT: oSelf.showPage(oSelf.get("paginator").currentPage + 1); return; } break; default: return; } elTarget = elTarget.parentNode; if(elTarget) { elTag = elTarget.tagName.toLowerCase(); } else { return; } } }; /** * Handles change events on paginator SELECT element. * * @method _onPaginatorDropdownChange * @param e {HTMLEvent} The change event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onPaginatorDropdownChange = function(e, oSelf) { var elTarget = YAHOO.util.Event.getTarget(e); var newValue = elTarget[elTarget.selectedIndex].value; var newRowsPerPage = YAHOO.lang.isValue(parseInt(newValue,10)) ? parseInt(newValue,10) : null; if(newRowsPerPage !== null) { var newStartRecordIndex = (oSelf.get("paginator").currentPage-1) * newRowsPerPage; oSelf.updatePaginator({rowsPerPage:newRowsPerPage, startRecordIndex:newStartRecordIndex}); oSelf.refreshView(); } else { YAHOO.log("Could not paginate with " + newValue + " rows per page", "error", oSelf.toString()); } }; /** * Handles change events on SELECT elements within DataTable. * * @method _onDropdownChange * @param e {HTMLEvent} The change event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onDropdownChange = function(e, oSelf) { var elTarget = YAHOO.util.Event.getTarget(e); //TODO: pass what args? //var value = elTarget[elTarget.selectedIndex].value; oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget}); }; ///////////////////////////////////////////////////////////////////////////// // // Public member variables // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // Public methods // ///////////////////////////////////////////////////////////////////////////// // OBJECT ACCESSORS /** * Public accessor to the unique name of the DataSource instance. * * @method toString * @return {String} Unique name of the DataSource instance. */ YAHOO.widget.DataTable.prototype.toString = function() { return "DataTable " + this._sName; }; /** * Returns the DataTable instance's DataSource instance. * * @method getDataSource * @return {YAHOO.util.DataSource} DataSource instance. */ YAHOO.widget.DataTable.prototype.getDataSource = function() { return this._oDataSource; }; /** * Returns the DataTable instance's ColumnSet instance. * * @method getColumnSet * @return {YAHOO.widget.ColumnSet} ColumnSet instance. */ YAHOO.widget.DataTable.prototype.getColumnSet = function() { return this._oColumnSet; }; /** * Returns the DataTable instance's RecordSet instance. * * @method getRecordSet * @return {YAHOO.widget.RecordSet} RecordSet instance. */ YAHOO.widget.DataTable.prototype.getRecordSet = function() { return this._oRecordSet; }; /** * Returns the DataTable instance's Cell Editor as an object literal with the * following properties: *
*
cell
*
Cell element being edited
* *
column
*
Associated Column instance
* *
container
*
Reference to editor's container DIV element
* *
isActive
*
True if cell is currently being edited
* *
record
*
Associated Record instance
* *
validator
*
Associated validator function
* *
value
*
Current input value
*
* * * * * * * @method getCellEditor * @return {Object} Cell Editor object literal values. */ YAHOO.widget.DataTable.prototype.getCellEditor = function() { return this._oCellEditor; }; // DOM ACCESSORS /** * Returns DOM reference to the DataTable's TABLE element. * * @method getTableEl * @return {HTMLElement} Reference to TABLE element. */ YAHOO.widget.DataTable.prototype.getTableEl = function() { return this._elTable; }; /** * Returns DOM reference to the DataTable's THEAD element. * * @method getTheadEl * @return {HTMLElement} Reference to THEAD element. */ YAHOO.widget.DataTable.prototype.getTheadEl = function() { return this._elThead; }; /** * Returns DOM reference to the DataTable's primary TBODY element. * * @method getTbodyEl * @return {HTMLElement} Reference to TBODY element. */ YAHOO.widget.DataTable.prototype.getTbodyEl = function() { return this._elTbody; }; // Backward compatibility YAHOO.widget.DataTable.prototype.getBody = function() { YAHOO.log("The method getBody() has been deprecated" + " in favor of getTbodyEl()", "warn", this.toString()); return this.getTbodyEl(); }; /** * Returns DOM reference to the DataTable's secondary TBODY element that is * used to display messages. * * @method getMsgTbodyEl * @return {HTMLElement} Reference to TBODY element. */ YAHOO.widget.DataTable.prototype.getMsgTbodyEl = function() { return this._elMsgTbody; }; /** * Returns DOM reference to the TD element within the secondary TBODY that is * used to display messages. * * @method getMsgTdEl * @return {HTMLElement} Reference to TD element. */ YAHOO.widget.DataTable.prototype.getMsgTdEl = function() { return this._elMsgTd; }; /** * Returns the corresponding TR reference for a given DOM element, ID string or * directly page row index. If the given identifier is a child of a TR element, * then DOM tree is traversed until a parent TR element is returned, otherwise * null. * * @method getTrEl * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to * get: by element reference, ID string, page row index, or Record. * @return {HTMLElement} Reference to TR element, or null. */ YAHOO.widget.DataTable.prototype.getTrEl = function(row) { var allRows = this._elTbody.rows; // By Record if(row instanceof YAHOO.widget.Record) { var nTrIndex = this.getTrIndex(row); return allRows[nTrIndex]; } // By page row index else if(YAHOO.lang.isNumber(row) && (row > -1) && (row < allRows.length)) { return allRows[row]; } // By ID string or element reference else { var elRow; var el = YAHOO.util.Dom.get(row); // Validate HTML element if(el && (el.ownerDocument == document)) { // Validate TR element if(el.tagName.toLowerCase() != "tr") { // Traverse up the DOM to find the corresponding TR element elRow = YAHOO.util.Dom.getAncestorByTagName(el,"tr"); } else { elRow = el; } // Make sure the TR is in this TBODY if(elRow && (elRow.parentNode == this._elTbody)) { // Now we can return the TR element return elRow; } } } YAHOO.log("Could not get TR element for row " + row, "warn", this.toString()); return null; }; // Backward compatibility YAHOO.widget.DataTable.prototype.getRow = function(index) { YAHOO.log("The method getRow() has been deprecated" + " in favor of getTrEl()", "warn", this.toString()); return this.getTrEl(index); }; /** * Returns DOM reference to the first TR element in the DataTable page, or null. * * @method getFirstTrEl * @return {HTMLElement} Reference to TR element. */ YAHOO.widget.DataTable.prototype.getFirstTrEl = function() { return this._elTbody.rows[0] || null; }; /** * Returns DOM reference to the last TR element in the DataTable page, or null. * * @method getLastTrEl * @return {HTMLElement} Reference to last TR element. */ YAHOO.widget.DataTable.prototype.getLastTrEl = function() { var allRows = this._elTbody.rows; if(allRows.length > 0) { return allRows[allRows.length-1] || null; } }; /** * Returns DOM reference to the given TD element. * * @method getTdEl * @param cell {HTMLElement | String} DOM element reference or string ID. * @return {HTMLElement} Reference to TD element. */ YAHOO.widget.DataTable.prototype.getTdEl = function(cell) { var elCell; var el = YAHOO.util.Dom.get(cell); // Validate HTML element if(el && (el.ownerDocument == document)) { // Validate TD element if(el.tagName.toLowerCase() != "td") { // Traverse up the DOM to find the corresponding TR element elCell = YAHOO.util.Dom.getAncestorByTagName(el, "td"); } else { elCell = el; } // Make sure the TD is in this TBODY if(elCell && (elCell.parentNode.parentNode == this._elTbody)) { // Now we can return the TD element return elCell; } } YAHOO.log("Could not get TD element for cell " + cell, "warn", this.toString()); return null; }; /** * Returns DOM reference to the TH element at given DataTable page coordinates, or null. * * @method getThEl * @param header {HTMLElement | String | YAHOO.widget.Column} DOM element * reference or string ID, or Column instance. * @return {HTMLElement} Reference to TH element. */ YAHOO.widget.DataTable.prototype.getThEl = function(header) { var elHeader; // Validate Column instance if(header instanceof YAHOO.widget.Column) { var oColumn = header; elHeader = YAHOO.util.Dom.get(this.id + "-col" + oColumn.getId()); if(elHeader) { return elHeader; } } // Validate HTML element else { var el = YAHOO.util.Dom.get(header); if(el && (el.ownerDocument == document)) { // Validate TH element if(el.tagName.toLowerCase() != "th") { // Traverse up the DOM to find the corresponding TR element elHeader = YAHOO.util.Dom.getAncestorByTagName(el,"th"); } else { elHeader = el; } // Make sure the TH is in this THEAD if(elHeader && (elHeader.parentNode.parentNode == this._elThead)) { // Now we can return the TD element return elHeader; } } } YAHOO.log("Could not get TH element for header " + header, "warn", this.toString()); return null; }; /** * Returns the page row index of given row. Returns null if the row is not in * view on the current DataTable page. * * @method getTrIndex * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID * string reference to an element within the DataTable page, a Record instance, * or a Record's RecordSet index. * @return {Number} Page row index, or null if row does not exist or is not in view. */ YAHOO.widget.DataTable.prototype.getTrIndex = function(row) { var nRecordIndex; // By Record if(row instanceof YAHOO.widget.Record) { nRecordIndex = this._oRecordSet.getRecordIndex(row); } // Calculate page row index from Record index else if(YAHOO.lang.isNumber(row)) { nRecordIndex = row; } if(YAHOO.lang.isNumber(nRecordIndex)) { // DataTable is paginated if(this.get("paginated")) { // Get the first and last Record on this page var startRecordIndex = this.get("paginator").startRecordIndex; var endRecordIndex = startRecordIndex + this.get("paginator").rowsPerPage - 1; // This Record is in view if((nRecordIndex >= startRecordIndex) && (nRecordIndex <= endRecordIndex)) { return nRecordIndex - startRecordIndex; } // This Record is not in view else { return null; } } // Not paginated, just return the Record index else { return nRecordIndex; } } // By element reference or ID string else { // Validate TR element elRow = this.getTrEl(row); if(elRow && (elRow.ownerDocument == document) && (elRow.parentNode == this._elTbody)) { return elRow.sectionRowIndex; } } YAHOO.log("Could not get page row index for row " + row, "warn", this.toString()); return null; }; // TABLE FUNCTIONS /** * Resets a RecordSet with the given data and populates the page view * with the new data. Any previous data and selection states are cleared. * However, sort states are not cleared, so if the given data is in a particular * sort order, implementers should take care to reset the sortedBy property. If * pagination is enabled, the currentPage is shown and Paginator UI updated, * otherwise all rows are displayed as a single page. For performance, existing * DOM elements are reused when possible. * * @method initializeTable * @param oData {Object | Object[]} An object literal of data or an array of * object literals containing data. */ YAHOO.widget.DataTable.prototype.initializeTable = function(oData) { // Clear the RecordSet this._oRecordSet.reset(); // Add data to RecordSet var records = this._oRecordSet.addRecords(oData); // Clear selections this._unselectAllTrEls(); this._unselectAllTdEls(); this._aSelections = null; this._sLastSelectedId = null; this._sSelectionAnchorId = null; // Refresh the view this.refreshView(); this.fireEvent("initEvent"); }; /** * Refreshes the view with existing Records from the RecordSet while * maintaining sort, pagination, and selection states. For performance, reuses * existing DOM elements when possible while deleting extraneous elements. * * @method refreshView */ YAHOO.widget.DataTable.prototype.refreshView = function() { var i, j, k, l, aRecords; var oPaginator = this.updatePaginator(); // Paginator is enabled, show a subset of Records and update Paginator UI if(this.get("paginated")) { var rowsPerPage = oPaginator.rowsPerPage; var startRecordIndex = (oPaginator.currentPage - 1) * rowsPerPage; aRecords = this._oRecordSet.getRecords(startRecordIndex, rowsPerPage); this.formatPaginators(); } // Show all records else { aRecords = this._oRecordSet.getRecords(); } var elTbody = this._elTbody; var elRows = elTbody.rows; // Has rows if(YAHOO.lang.isArray(aRecords) && (aRecords.length > 0)) { this.hideTableMessage(); // Keep track of selected rows var aSelectedRows = this.getSelectedRows(); // Keep track of selected cells var aSelectedCells = this.getSelectedCells(); // Anything to reinstate? var bReselect = (aSelectedRows.length>0) || (aSelectedCells.length > 0); // Remove extra rows from the bottom so as to preserve ID order while(elTbody.hasChildNodes() && (elRows.length > aRecords.length)) { elTbody.deleteRow(-1); } // Unselect all TR and TD elements in the UI if(bReselect) { this._unselectAllTrEls(); this._unselectAllTdEls(); } // From the top, update in-place existing rows for(i=0; i-1; i--) { this.addRow(aData[i], index); } } else { for(i=0; i 0) ? nRecordIndex + count -1 : nRecordIndex; var endIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1; for(var i=startIndex; i>endIndex-1; i--) { this.deleteRow(i); } } else { this.deleteRow(nRecordIndex); } } else { YAHOO.log("Could not delete row " + row, "info", this.toString()); } }; // CELL FUNCTIONS /** * Outputs markup into the given TD based on given Record. * * @method formatCell * @param elCell {HTMLElement} TD Element. * @param oRecord {YAHOO.widget.Record} (Optional) Record instance. * @param oColumn {YAHOO.widget.Column} (Optional) Column instance. * @return {HTML} Markup. */ YAHOO.widget.DataTable.prototype.formatCell = function(elCell, oRecord, oColumn) { if(!(oRecord instanceof YAHOO.widget.Record)) { oRecord = this.getRecord(elCell); } if(!(oColumn instanceof YAHOO.widget.Column)) { oColumn = this._oColumnSet.getColumn(elCell.yuiColumnId); } if(oRecord && oColumn) { var oData = oRecord.getData(oColumn.key); var fnFormatter; if(YAHOO.lang.isString(oColumn.formatter)) { switch(oColumn.formatter) { case "button": fnFormatter = YAHOO.widget.DataTable.formatButton; break; case "checkbox": fnFormatter = YAHOO.widget.DataTable.formatCheckbox; break; case "currency": fnFormatter = YAHOO.widget.DataTable.formatCurrency; break; case "date": fnFormatter = YAHOO.widget.DataTable.formatDate; break; case "dropdown": fnFormatter = YAHOO.widget.DataTable.formatDropdown; break; case "email": fnFormatter = YAHOO.widget.DataTable.formatEmail; break; case "link": fnFormatter = YAHOO.widget.DataTable.formatLink; break; case "number": fnFormatter = YAHOO.widget.DataTable.formatNumber; break; case "radio": fnFormatter = YAHOO.widget.DataTable.formatRadio; break; case "text": fnFormatter = YAHOO.widget.DataTable.formatText; break; case "textarea": fnFormatter = YAHOO.widget.DataTable.formatTextarea; break; case "textbox": fnFormatter = YAHOO.widget.DataTable.formatTextbox; break; case "html": // This is the default break; default: YAHOO.log("Could not find formatter function \"" + oColumn.formatter + "\"", "warn", this.toString()); fnFormatter = null; } } else if(YAHOO.lang.isFunction(oColumn.formatter)) { fnFormatter = oColumn.formatter; } // Apply special formatter if(fnFormatter) { fnFormatter.call(this, elCell, oRecord, oColumn, oData); } else { elCell.innerHTML = (YAHOO.lang.isValue(oData)) ? oData.toString() : ""; } // Add custom classNames var aCustomClasses = null; if(YAHOO.lang.isString(oColumn.className)) { aCustomClasses = [oColumn.className]; } else if(YAHOO.lang.isArray(oColumn.className)) { aCustomClasses = oColumn.className; } if(aCustomClasses) { for(var i=0; i" + sValue + ""; //} }; /** * Formats a CHECKBOX element. * * @method DataTable.formatCheckbox * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object | Boolean} Data value for the cell. Can be a simple * Boolean to indicate whether checkbox is checked or not. Can be object literal * {checked:bBoolean, label:sLabel}. Other forms of oData require a custom * formatter. * @static */ YAHOO.widget.DataTable.formatCheckbox = function(el, oRecord, oColumn, oData) { var bChecked = oData; bChecked = (bChecked) ? " checked" : ""; el.innerHTML = ""; }; /** * Formats currency. Default unit is USD. * * @method DataTable.formatCurrency * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Number} Data value for the cell. * @static */ YAHOO.widget.DataTable.formatCurrency = function(el, oRecord, oColumn, oData) { if(YAHOO.lang.isNumber(oData)) { var nAmount = oData; var markup; // Round to the penny nAmount = Math.round(nAmount*100)/100; // Default currency is USD markup = "$"+nAmount; // Normalize digits var dotIndex = markup.indexOf("."); if(dotIndex < 0) { markup += ".00"; } else { while(dotIndex > markup.length-3) { markup += "0"; } } el.innerHTML = markup; } else { el.innerHTML = YAHOO.lang.isValue(oData) ? oData : ""; } }; /** * Formats JavaScript Dates. * * @method DataTable.formatDate * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} Data value for the cell, or null. * @static */ YAHOO.widget.DataTable.formatDate = function(el, oRecord, oColumn, oData) { var oDate = oData; if(oDate instanceof Date) { el.innerHTML = (oDate.getMonth()+1) + "/" + oDate.getDate() + "/" + oDate.getFullYear(); } else { el.innerHTML = YAHOO.lang.isValue(oData) ? oData : ""; } }; /** * Formats SELECT elements. * * @method DataTable.formatDropdown * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} Data value for the cell, or null. * @static */ YAHOO.widget.DataTable.formatDropdown = function(el, oRecord, oColumn, oData) { var selectedValue = (YAHOO.lang.isValue(oData)) ? oData : oRecord.getData(oColumn.key); var options = (YAHOO.lang.isArray(oColumn.dropdownOptions)) ? oColumn.dropdownOptions : null; var selectEl; var collection = el.getElementsByTagName("select"); // Create the form element only once, so we can attach the onChange listener if(collection.length === 0) { // Create SELECT element selectEl = document.createElement("select"); YAHOO.util.Dom.addClass(selectEl, YAHOO.widget.DataTable.CLASS_DROPDOWN); selectEl = el.appendChild(selectEl); // Add event listener //TODO: static method doesn't have access to the datatable instance... YAHOO.util.Event.addListener(selectEl,"change",oDataTable._onDropdownChange,oDataTable); } selectEl = collection[0]; // Update the form element if(selectEl) { // Clear out previous options selectEl.innerHTML = ""; // We have options to populate if(options) { // Create OPTION elements for(var i=0; i" + selectedValue + ""; } } else { el.innerHTML = YAHOO.lang.isValue(oData) ? oData : ""; } }; /** * Formats emails. * * @method DataTable.formatEmail * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} Data value for the cell, or null. * @static */ YAHOO.widget.DataTable.formatEmail = function(el, oRecord, oColumn, oData) { if(YAHOO.lang.isString(oData)) { el.innerHTML = "" + oData + ""; } else { el.innerHTML = YAHOO.lang.isValue(oData) ? oData : ""; } }; /** * Formats links. * * @method DataTable.formatLink * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} Data value for the cell, or null. * @static */ YAHOO.widget.DataTable.formatLink = function(el, oRecord, oColumn, oData) { if(YAHOO.lang.isString(oData)) { el.innerHTML = "" + oData + ""; } else { el.innerHTML = YAHOO.lang.isValue(oData) ? oData : ""; } }; /** * Formats numbers. * * @method DataTable.formatNumber * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} Data value for the cell, or null. * @static */ YAHOO.widget.DataTable.formatNumber = function(el, oRecord, oColumn, oData) { if(YAHOO.lang.isNumber(oData)) { el.innerHTML = oData; } else { el.innerHTML = YAHOO.lang.isValue(oData) ? oData : ""; } }; /** * Formats INPUT TYPE=RADIO elements. * * @method DataTable.formatRadio * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} (Optional) Data value for the cell. * @static */ YAHOO.widget.DataTable.formatRadio = function(el, oRecord, oColumn, oData) { var bChecked = oData; bChecked = (bChecked) ? " checked" : ""; el.innerHTML = ""; }; /** * Formats text strings. * * @method DataTable.formatText * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} (Optional) Data value for the cell. * @static */ YAHOO.widget.DataTable.formatText = function(el, oRecord, oColumn, oData) { var value = (YAHOO.lang.isValue(oRecord.getData(oColumn.key))) ? oRecord.getData(oColumn.key) : ""; //TODO: move to util function el.innerHTML = value.toString().replace(/&/g, "&").replace(//g, ">"); }; /** * Formats TEXTAREA elements. * * @method DataTable.formatTextarea * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} (Optional) Data value for the cell. * @static */ YAHOO.widget.DataTable.formatTextarea = function(el, oRecord, oColumn, oData) { var value = (YAHOO.lang.isValue(oRecord.getData(oColumn.key))) ? oRecord.getData(oColumn.key) : ""; var markup = ""; el.innerHTML = markup; }; /** * Formats INPUT TYPE=TEXT elements. * * @method DataTable.formatTextbox * @param el {HTMLElement} The element to format with markup. * @param oRecord {YAHOO.widget.Record} Record instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param oData {Object} (Optional) Data value for the cell. * @static */ YAHOO.widget.DataTable.formatTextbox = function(el, oRecord, oColumn, oData) { var value = (YAHOO.lang.isValue(oRecord.getData(oColumn.key))) ? oRecord.getData(oColumn.key) : ""; var markup = ""; el.innerHTML = markup; }; // PAGINATION /** * Updates Paginator values in response to RecordSet changes and/or DOM events. * Pass in all, a subset, or no values. * * @method updatePaginator * @param oNewValues {Object} (Optional) Object literal of Paginator values, or * a subset of Paginator values. * @param {Object} Object literal of all Paginator values. */ YAHOO.widget.DataTable.prototype.updatePaginator = function(oNewValues) { // Complete the set var oValidPaginator = this.get("paginator"); for(var param in oNewValues) { if(oValidPaginator.hasOwnProperty(param)) { oValidPaginator[param] = oNewValues[param]; } } oValidPaginator.totalRecords = this._oRecordSet.getLength(); oValidPaginator.rowsThisPage = Math.min(oValidPaginator.rowsPerPage, oValidPaginator.totalRecords); oValidPaginator.totalPages = Math.ceil(oValidPaginator.totalRecords / oValidPaginator.rowsThisPage); if(isNaN(oValidPaginator.totalPages)) { oValidPaginator.totalPages = 0; } this.set("paginator", oValidPaginator); return this.get("paginator"); }; /** * Displays given page of a paginated DataTable. * * @method showPage * @param nPage {Number} Which page. */ YAHOO.widget.DataTable.prototype.showPage = function(nPage) { // Validate input if(!YAHOO.lang.isNumber(nPage) || (nPage < 1) || (nPage > this.get("paginator").totalPages)) { nPage = 1; } this.updatePaginator({currentPage:nPage}); this.refreshView(); }; /** * Updates Paginator containers with markup. Override this method to customize pagination UI. * * @method formatPaginators */ YAHOO.widget.DataTable.prototype.formatPaginators = function() { var pag = this.get("paginator"); // For Opera workaround var dropdownEnabled = false; // Links are enabled if(pag.pageLinks > -1) { for(var i=0; i-1; i--) { if((this.get("paginator").rowsPerPage + "") === options[i].value) { options[i].selected = true; } } } // Show the dropdown elDropdown.style.display = ""; return; } YAHOO.log("Could not update Paginator dropdown " + elDropdown, "error", this.toString()); }; /** * Updates Paginator links container with markup. * * @method formatPaginatorLinks * @param elContainer {HTMLElement} The link container element. * @param nCurrentPage {Number} Current page. * @param nPageLinksStart {Number} First page link to display. * @param nPageLinksLength {Number} How many page links to display. * @param nTotalPages {Number} Total number of pages. */ YAHOO.widget.DataTable.prototype.formatPaginatorLinks = function(elContainer, nCurrentPage, nPageLinksStart, nPageLinksLength, nTotalPages) { if(elContainer && (elContainer.ownerDocument == document) && YAHOO.lang.isNumber(nCurrentPage) && YAHOO.lang.isNumber(nPageLinksStart) && YAHOO.lang.isNumber(nTotalPages)) { // Set up markup for first/last/previous/next var bIsFirstPage = (nCurrentPage == 1) ? true : false; var bIsLastPage = (nCurrentPage == nTotalPages) ? true : false; var sFirstLinkMarkup = (bIsFirstPage) ? " << " : " << "; var sPrevLinkMarkup = (bIsFirstPage) ? " < " : " < " ; var sNextLinkMarkup = (bIsLastPage) ? " > " : " > " ; var sLastLinkMarkup = (bIsLastPage) ? " >> " : " >> "; // Start with first and previous var sMarkup = sFirstLinkMarkup + sPrevLinkMarkup; // Ok to show all links var nMaxLinks = nTotalPages; var nFirstLink = 1; var nLastLink = nTotalPages; if(nPageLinksLength > 0) { // Calculate how many links to show nMaxLinks = (nPageLinksStart+nPageLinksLength < nTotalPages) ? nPageLinksStart+nPageLinksLength-1 : nTotalPages; // Try to keep the current page in the middle nFirstLink = (nCurrentPage - Math.floor(nMaxLinks/2) > 0) ? nCurrentPage - Math.floor(nMaxLinks/2) : 1; nLastLink = (nCurrentPage + Math.floor(nMaxLinks/2) <= nTotalPages) ? nCurrentPage + Math.floor(nMaxLinks/2) : nTotalPages; // Keep the last link in range if(nFirstLink === 1) { nLastLink = nMaxLinks; } // Keep the first link in range else if(nLastLink === nTotalPages) { nFirstLink = nTotalPages - nMaxLinks + 1; } // An even number of links can get funky if(nLastLink - nFirstLink === nMaxLinks) { nLastLink--; } } // Generate markup for each page for(var i=nFirstLink; i<=nLastLink; i++) { if(i != nCurrentPage) { sMarkup += " " + i + " "; } else { sMarkup += " " + i + ""; } } sMarkup += sNextLinkMarkup + sLastLinkMarkup; elContainer.innerHTML = sMarkup; return; } YAHOO.log("Could not format Paginator links", "error", this.toString()); }; // SELECTION/HIGHLIGHTING /** * ID string of last highlighted cell element * * @property _sLastHighlightedCellId * @type String * @private */ YAHOO.widget.DataTable.prototype._sLastHighlightedCellId = null; /** * ID string of last highlighted row element * * @property _sLastHighlightedRowId * @type String * @private */ YAHOO.widget.DataTable.prototype._sLastHighlightedRowId = null; /** * Array of selections: {recordId:nRecordId, cellIndex:nCellIndex} * * @property _aSelections * @type Object[] * @private */ YAHOO.widget.DataTable.prototype._aSelections = null; /** * ID string of last selected element * * @property _sLastSelectedId * @type String * @private */ YAHOO.widget.DataTable.prototype._sLastSelectedId = null; /** * ID string of the selection anchor element. * * @property _sSelectionAnchorId * @type String * @private */ YAHOO.widget.DataTable.prototype._sSelectionAnchorId = null; /** * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED * from all TR elements on the page. * * @method _unselectAllTrEls * @private */ YAHOO.widget.DataTable.prototype._unselectAllTrEls = function() { var selectedRows = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_SELECTED,"tr",this._elTbody); YAHOO.util.Dom.removeClass(selectedRows, YAHOO.widget.DataTable.CLASS_SELECTED); }; /** * Returns array of selected TR elements on the page. * * @method getSelectedTrEls * @return {HTMLElement[]} Array of selected TR elements. */ YAHOO.widget.DataTable.prototype.getSelectedTrEls = function() { return YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_SELECTED,"tr",this._elTbody); }; /** * Sets given row to the selected state. * * @method selectRow * @param row {HTMLElement | String} HTML element reference or ID. */ YAHOO.widget.DataTable.prototype.selectRow = function(row) { // Validate the row var elRow = this.getTrEl(row); if(elRow) { var oRecord = this.getRecord(elRow); if(oRecord) { // Get Record ID var tracker = this._aSelections || []; var nRecordId = oRecord.getId(); // Remove if already there // Use Array.indexOf if available... if(tracker.indexOf && (tracker.indexOf(nRecordId) > -1)) { tracker.splice(tracker.indexOf(nRecordId),1); } // ...or do it the old-fashioned way else { for(var j=0; j -1)) { tracker.splice(tracker.indexOf(nRecordId),1); bFound = true; } // ...or do it the old-fashioned way else { for(var j=0; j"; // Then create the labels in an IE-friendly way elLabel = elContainer.appendChild(document.createElement("label")); elLabel.htmlFor = checkboxId; elLabel.innerHTML = checkboxValue; } var aCheckboxEls = []; var checkboxEl; // Loop through checkboxes to check them for(j=0; j"; // Then create the labels in an IE-friendly way elLabel = elContainer.appendChild(document.createElement("label")); elLabel.htmlFor = radioId; elLabel.innerHTML = radioValue; } // Then check one, and assign click handlers for(j=0; j=nTargetTrIndex; i--) { if(!this.isSelected(allRows[i])) { this.selectRow(allRows[i]); } } } } else { // Unselect all rows between anchor row and target row if(nAnchorTrIndex < nTargetTrIndex) { for(i=nAnchorTrIndex+1; i<=nTargetTrIndex-1; i++) { if(this.isSelected(allRows[i])) { this.unselectRow(allRows[i]); } } } // Unselect all rows between target row and anchor row else { for(i=nTargetTrIndex+1; i<=nAnchorTrIndex-1; i++) { if(this.isSelected(allRows[i])) { this.unselectRow(allRows[i]); } } } // Select the target row this.selectRow(elTargetRow); } } // Invalid anchor else { // Set anchor this._sSelectionAnchorId = elTargetRow.id; // Toggle selection of target if(this.isSelected(elTargetRow)) { this.unselectRow(elTargetRow); } else { this.selectRow(elTargetRow); } } } // Only SHIFT else if((sMode != "single") && bSHIFT) { this.unselectAllRows(); // Validate anchor if(elAnchorRow && YAHOO.lang.isNumber(elAnchorRow.sectionRowIndex)) { nAnchorTrIndex = elAnchorRow.sectionRowIndex; // Select all rows between anchor row and target row, // including the anchor row and target row if(nAnchorTrIndex < nTargetTrIndex) { for(i=nAnchorTrIndex; i<=nTargetTrIndex; i++) { this.selectRow(allRows[i]); } } // Select all rows between target row and anchor row, // including the target row and anchor row else { for(i=nAnchorTrIndex; i>=nTargetTrIndex; i--) { this.selectRow(allRows[i]); } } } // Invalid anchor else { // Set anchor this._sSelectionAnchorId = elTargetRow.id; // Select target row only this.selectRow(elTargetRow); } } // Only CTRL else if((sMode != "single") && bCTRL) { // Set anchor this._sSelectionAnchorId = elTargetRow.id; // Toggle selection of target if(this.isSelected(elTargetRow)) { this.unselectRow(elTargetRow); } else { this.selectRow(elTargetRow); } } // Neither SHIFT nor CTRL else if(sMode == "single") { this.unselectAllRows(); this.selectRow(elTargetRow); } // Neither SHIFT nor CTRL else { // Set anchor this._sSelectionAnchorId = elTargetRow.id; // Select only target this.unselectAllRows(); this.selectRow(elTargetRow); } YAHOO.util.Event.stopEvent(evt); // Clear any selections that are a byproduct of the click or dblclick var sel; if(window.getSelection) { sel = window.getSelection(); } else if(document.getSelection) { sel = document.getSelection(); } else if(document.selection) { sel = document.selection; } if(sel) { if(sel.empty) { sel.empty(); } else if (sel.removeAllRanges) { sel.removeAllRanges(); } else if(sel.collapse) { sel.collapse(); } } } else { YAHOO.log("Could not select row " + elTarget, "warn", this.toString()); } }; /** * Overridable custom event handler to select cell. * * @method onEventSelectCell * @param oArgs.event {HTMLEvent} Event object. * @param oArgs.target {HTMLElement} Target element. */ YAHOO.widget.DataTable.prototype.onEventSelectCell = function(oArgs) { var sMode = this.get("selectionMode"); if ((sMode == "standard") || (sMode == "single")) { return; } var evt = oArgs.event; var elTarget = oArgs.target; var bSHIFT = evt.shiftKey; var bCTRL = evt.ctrlKey; var i, j, nAnchorTrIndex, nAnchorTdIndex, currentRow, startIndex, endIndex; var elTargetCell = this.getTdEl(elTarget); if(elTargetCell) { var elTargetRow = this.getTrEl(elTargetCell); var allRows = this._elTbody.rows; var nTargetTrIndex = elTargetRow.sectionRowIndex; var nTargetTdIndex = elTarget.yuiCellIndex; var elAnchorCell = YAHOO.util.Dom.get(this._sSelectionAnchorId); // Both SHIFT and CTRL if((sMode != "singlecell") && bSHIFT && bCTRL) { // Validate anchor if(elAnchorCell && YAHOO.lang.isNumber(elAnchorCell.yuiCellIndex)) { nAnchorTrIndex = elAnchorCell.parentNode.sectionRowIndex; nAnchorTdIndex = elAnchorCell.yuiCellIndex; // Anchor is selected if(this.isSelected(elAnchorCell)) { // All cells are on the same row if(nAnchorTrIndex == nTargetTrIndex) { // Select all cells between anchor cell and target cell, including target cell if(nAnchorTdIndex < nTargetTdIndex) { for(i=nAnchorTdIndex+1; i<=nTargetTdIndex; i++) { this.selectCell(allRows[nTargetTrIndex].cells[i]); } } // Select all cells between target cell and anchor cell, including target cell else if(nTargetTdIndex < nAnchorTdIndex) { for(i=nTargetTdIndex; i=nTargetTrIndex; i--) { for(j=endIndex; j>=startIndex; j--) { this.selectCell(allRows[i].cells[j]); } } } } } // Anchor cell is unselected else { // All cells are on the same row if(nAnchorTrIndex == nTargetTrIndex) { // Unselect all cells between anchor cell and target cell if(nAnchorTdIndex < nTargetTdIndex) { for(i=nAnchorTdIndex+1; inAnchorTdIndex) { this.unselectCell(currentRow.cells[j]); } } // This is the target row, only unelect cells before the target cell else if(currentRow.sectionRowIndex == nTargetTrIndex) { if(jnTargetTdIndex) { this.unselectCell(currentRow.cells[j]); } } // This is the anchor row, only unselect cells before the anchor cell else if(currentRow.sectionRowIndex == nAnchorTrIndex) { if(j=nAnchorTdIndex) { this.selectCell(currentRow.cells[j]); } } // This is the target row, only select the target cell and before else if(currentRow.sectionRowIndex == nTargetTrIndex) { if(j<=nTargetTdIndex) { this.selectCell(currentRow.cells[j]); } } // Select all cells on this row else { this.selectCell(currentRow.cells[j]); } } } } else if(sMode == "cellblock") { // Select the cellblock from anchor cell to target cell // including the anchor cell and the target cell startIndex = Math.min(nAnchorTdIndex, nTargetTdIndex); endIndex = Math.max(nAnchorTdIndex, nTargetTdIndex); for(i=nAnchorTrIndex; i<=nTargetTrIndex; i++) { for(j=startIndex; j<=endIndex; j++) { this.selectCell(allRows[i].cells[j]); } } this._sLastSelectedId = allRows[nTargetTrIndex].cells[nTargetTdIndex].id; } } // Anchor row is below target row else { if(sMode == "cellrange") { // Select all cells from target cell to anchor cell, // including the target cell and anchor cell for(i=nTargetTrIndex; i<=nAnchorTrIndex; i++) { currentRow = allRows[i]; for(j=0; j=nTargetTdIndex) { this.selectCell(currentRow.cells[j]); } } // This is the anchor row, only select the anchor cell and before else if(currentRow.sectionRowIndex == nAnchorTrIndex) { if(j<=nAnchorTdIndex) { this.selectCell(currentRow.cells[j]); } } // Select all cells on this row else { this.selectCell(currentRow.cells[j]); } } } } else if(sMode == "cellblock") { // Select the cellblock from target cell to anchor cell // including the target cell and the anchor cell startIndex = Math.min(nAnchorTdIndex, nTargetTdIndex); endIndex = Math.max(nAnchorTdIndex, nTargetTdIndex); for(i=nTargetTrIndex; i<=nAnchorTrIndex; i++) { for(j=startIndex; j<=endIndex; j++) { this.selectCell(allRows[i].cells[j]); } } this._sLastSelectedId = allRows[nTargetTrIndex].cells[nTargetTdIndex].id; } } } // Invalid anchor else { // Set anchor this._sSelectionAnchorId = elTargetCell.id; // Select target only this.selectCell(elTargetCell); } } // Only CTRL else if((sMode != "singlecell") && bCTRL) { // Set anchor this._sSelectionAnchorId = elTargetCell.id; // Toggle selection of target if(this.isSelected(elTargetCell)) { this.unselectCell(elTargetCell); } else { this.selectCell(elTargetCell); } } // Neither SHIFT nor CTRL, or multi-selection has been disabled else { // Set anchor this._sSelectionAnchorId = elTargetCell.id; // Select only target this.unselectAllCells(); this.selectCell(elTargetCell); } YAHOO.util.Event.stopEvent(evt); // Clear any selections that are a byproduct of the click or dblclick var sel; if(window.getSelection) { sel = window.getSelection(); } else if(document.getSelection) { sel = document.getSelection(); } else if(document.selection) { sel = document.selection; } if(sel) { if(sel.empty) { sel.empty(); } else if (sel.removeAllRanges) { sel.removeAllRanges(); } else if(sel.collapse) { sel.collapse(); } } } else { YAHOO.log("Could not select cell " + elTarget, "warn", this.toString()); } }; /** * Overridable custom event handler to highlight row. * * @method onEventHighlightRow * @param oArgs.event {HTMLEvent} Event object. * @param oArgs.target {HTMLElement} Target element. */ YAHOO.widget.DataTable.prototype.onEventHighlightRow = function(oArgs) { var evt = oArgs.event; var elTarget = oArgs.target; this.highlightRow(elTarget); }; /** * Overridable custom event handler to unhighlight row. * * @method onEventUnhighlightRow * @param oArgs.event {HTMLEvent} Event object. * @param oArgs.target {HTMLElement} Target element. */ YAHOO.widget.DataTable.prototype.onEventUnhighlightRow = function(oArgs) { var evt = oArgs.event; var elTarget = oArgs.target; this.unhighlightRow(elTarget); }; /** * Overridable custom event handler to highlight cell. * * @method onEventHighlightCell * @param oArgs.event {HTMLEvent} Event object. * @param oArgs.target {HTMLElement} Target element. */ YAHOO.widget.DataTable.prototype.onEventHighlightCell = function(oArgs) { var evt = oArgs.event; var elTarget = oArgs.target; this.highlightCell(elTarget); }; /** * Overridable custom event handler to unhighlight cell. * * @method onEventUnhighlightCell * @param oArgs.event {HTMLEvent} Event object. * @param oArgs.target {HTMLElement} Target element. */ YAHOO.widget.DataTable.prototype.onEventUnhighlightCell = function(oArgs) { var evt = oArgs.event; var elTarget = oArgs.target; this.unhighlightCell(elTarget); }; /** * Overridable custom event handler to format cell. * * @method onEventFormatCell * @param oArgs.event {HTMLEvent} Event object. * @param oArgs.target {HTMLElement} Target element. */ YAHOO.widget.DataTable.prototype.onEventFormatCell = function(oArgs) { var evt = oArgs.event; var target = oArgs.target; var elTag = target.tagName.toLowerCase(); var elCell = this.getTdEl(target); if(elCell && YAHOO.lang.isNumber(elCell.yuiColumnId)) { var oColumn = this._oColumnSet.getColumn(elCell.yuiColumnId); this.formatCell(elCell, this.getRecord(elCell), oColumn); } else { YAHOO.log("Could not format cell " + target, "warn", this.toString()); } }; /** * Overridable custom event handler to edit cell. * * @method onEventShowCellEditor * @param oArgs.event {HTMLEvent} Event object. * @param oArgs.target {HTMLElement} Target element. */ YAHOO.widget.DataTable.prototype.onEventShowCellEditor = function(oArgs) { var evt = oArgs.event; var target = oArgs.target; var elTag = target.tagName.toLowerCase(); var elCell = this.getTdEl(target); if(elCell) { this.showCellEditor(elCell); } else { YAHOO.log("Could not edit cell " + target, "warn", this.toString()); } }; // Backward compatibility YAHOO.widget.DataTable.prototype.onEventEditCell = function(oArgs) { YAHOO.log("The method onEventEditCell() has been deprecated" + " in favor of onEventShowCellEditor()", "warn", this.toString()); this.onEventShowCellEditor(oArgs); }; /** * Overridable custom event handler to save Cell Editor input. * * @method onEventSaveCellEditor * @param oArgs.editor {Object} Cell Editor object literal. */ YAHOO.widget.DataTable.prototype.onEventSaveCellEditor = function(oArgs) { this.saveCellEditor(); }; /** * Callback function for creating a progressively enhanced DataTable first * receives data from DataSource and populates the RecordSet, then initializes * DOM elements. * * @method _onDataReturnEnhanceTable * @param sRequest {String} Original request. * @param oResponse {Object} Response object. * @param bError {Boolean} (optional) True if there was a data error. * @private */ YAHOO.widget.DataTable.prototype._onDataReturnEnhanceTable = function(sRequest, oResponse) { // Pass data through abstract method for any transformations var ok = this.doBeforeLoadData(sRequest, oResponse); // Data ok to populate if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) { // Update RecordSet this._oRecordSet.addRecords(oResponse.results); // Initialize DOM elements this._initTableEl(); if(!this._elTable || !this._elThead || !this._elTbody) { YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString()); return; } // Call Element's constructor after DOM elements are created // but *before* UI is updated with data YAHOO.widget.DataTable.superclass.constructor.call(this, this._elContainer, this._oConfigs); //HACK: Set the Paginator values if(this._oConfigs.paginator) { this.updatePaginator(this._oConfigs.paginator); } // Update the UI this.refreshView(); } // Error else if(ok && oResponse.error) { this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR); } // Empty else if(ok){ this.showTableMessage(YAHOO.widget.DataTable.MSG_EMPTY, YAHOO.widget.DataTable.CLASS_EMPTY); } }; /** * Callback function receives data from DataSource and populates an entire * DataTable with Records and TR elements, clearing previous Records, if any. * * @method onDataReturnInitializeTable * @param sRequest {String} Original request. * @param oResponse {Object} Response object. * @param bError {Boolean} (optional) True if there was a data error. */ YAHOO.widget.DataTable.prototype.onDataReturnInitializeTable = function(sRequest, oResponse) { this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse}); // Pass data through abstract method for any transformations var ok = this.doBeforeLoadData(sRequest, oResponse); // Data ok to populate if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) { this.initializeTable(oResponse.results); } // Error else if(ok && oResponse.error) { this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR); } // Empty else if(ok){ this.showTableMessage(YAHOO.widget.DataTable.MSG_EMPTY, YAHOO.widget.DataTable.CLASS_EMPTY); } }; // Backward compatibility YAHOO.widget.DataTable.prototype.onDataReturnReplaceRows = function(sRequest, oResponse) { YAHOO.log("The method onDataReturnReplaceRows() has been deprecated" + " in favor of onDataReturnInitializeTable()", "warn", this.toString()); this.onDataReturnInitializeTable(sRequest, oResponse); }; /** * Callback function receives data from DataSource and appends to an existing * DataTable new Records and, if applicable, creates or updates * corresponding TR elements. * * @method onDataReturnAppendRows * @param sRequest {String} Original request. * @param oResponse {Object} Response object. * @param bError {Boolean} (optional) True if there was a data error. */ YAHOO.widget.DataTable.prototype.onDataReturnAppendRows = function(sRequest, oResponse) { this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse}); // Pass data through abstract method for any transformations var ok = this.doBeforeLoadData(sRequest, oResponse); // Data ok to append if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) { this.addRows(oResponse.results); } // Error else if(ok && oResponse.error) { this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR); } }; /** * Callback function receives data from DataSource and inserts into top of an * existing DataTable new Records and, if applicable, creates or updates * corresponding TR elements. * * @method onDataReturnInsertRows * @param sRequest {String} Original request. * @param oResponse {Object} Response object. * @param bError {Boolean} (optional) True if there was a data error. */ YAHOO.widget.DataTable.prototype.onDataReturnInsertRows = function(sRequest, oResponse) { this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse}); // Pass data through abstract method for any transformations var ok = this.doBeforeLoadData(sRequest, oResponse); // Data ok to append if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) { this.addRows(oResponse.results, 0); } // Error else if(ok && oResponse.error) { this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR); } }; ///////////////////////////////////////////////////////////////////////////// // // Custom Events // ///////////////////////////////////////////////////////////////////////////// /** * Fired when the DataTable instance's initialization is complete. * * @event initEvent */ /** * Fired when the DataTable's view is refreshed. * * @event refreshEvent */ /** * Fired when data is returned from DataSource. * * @event dataReturnEvent * @param oArgs.request {String} Original request. * @param oArgs.response {Object} Response object. */ /** * Fired when the DataTable has a focus. * * @event tableFocusEvent */ /** * Fired when the DataTable has a blur. * * @event tableBlurEvent */ /** * Fired when the DataTable has a mouseover. * * @event tableMouseoverEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The DataTable's TABLE element. * */ /** * Fired when the DataTable has a mouseout. * * @event tableMouseoutEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The DataTable's TABLE element. * */ /** * Fired when the DataTable has a mousedown. * * @event tableMousedownEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The DataTable's TABLE element. * */ /** * Fired when the DataTable has a click. * * @event tableClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The DataTable's TABLE element. * */ /** * Fired when the DataTable has a dblclick. * * @event tableDblclickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The DataTable's TABLE element. * */ /** * Fired when a fixed scrolling DataTable has a scroll. * * @event tableScrollEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE) * or the DataTable's TBODY element (everyone else). * */ /** * Fired when a message is shown in the DataTable's message element. * * @event tableMsgShowEvent * @param oArgs.html {String} The HTML displayed. * @param oArgs.className {String} The className assigned. * */ /** * Fired when the DataTable's message element is hidden. * * @event tableMsgHideEvent */ /** * Fired when a header row has a mouseover. * * @event headerRowMouseoverEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a header row has a mouseout. * * @event headerRowMouseoutEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a header row has a mousedown. * * @event headerRowMousedownEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a header row has a click. * * @event headerRowClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a header row has a dblclick. * * @event headerRowDblclickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a header cell has a mouseover. * * @event headerCellMouseoverEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TH element. * */ /** * Fired when a header cell has a mouseout. * * @event headerCellMouseoutEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TH element. * */ /** * Fired when a header cell has a mousedown. * * @event headerCellMousedownEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TH element. */ /** * Fired when a header cell has a click. * * @event headerCellClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TH element. */ /** * Fired when a header cell has a dblclick. * * @event headerCellDblclickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TH element. */ /** * Fired when a header label has a mouseover. * * @event headerLabelMouseoverEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The SPAN element. * */ /** * Fired when a header label has a mouseout. * * @event headerLabelMouseoutEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The SPAN element. * */ /** * Fired when a header label has a mousedown. * * @event headerLabelMousedownEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The SPAN element. */ /** * Fired when a header label has a click. * * @event headerLabelClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The SPAN element. */ /** * Fired when a header label has a dblclick. * * @event headerLabelDblclickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The SPAN element. */ /** * Fired when a column is sorted. * * @event columnSortEvent * @param oArgs.column {YAHOO.widget.Column} The Column instance. * @param oArgs.dir {String} Sort direction "asc" or "desc". */ /** * Fired when a column is resized. * * @event columnResizeEvent * @param oArgs.column {YAHOO.widget.Column} The Column instance. * @param oArgs.target {HTMLElement} The TH element. */ /** * Fired when a row has a mouseover. * * @event rowMouseoverEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a row has a mouseout. * * @event rowMouseoutEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a row has a mousedown. * * @event rowMousedownEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a row has a click. * * @event rowClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a row has a dblclick. * * @event rowDblclickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TR element. */ /** * Fired when a row is added. * * @event rowAddEvent * @param oArgs.record {YAHOO.widget.Record} The added Record. */ /** * Fired when a row is updated. * * @event rowUpdateEvent * @param oArgs.record {YAHOO.widget.Record} The updated Record. * @param oArgs.oldData {Object} Object literal of the old data. */ /** * Fired when a row is deleted. * * @event rowDeleteEvent * @param oArgs.oldData {Object} Object literal of the deleted data. * @param oArgs.recordIndex {Number} Index of the deleted Record. * @param oArgs.trElIndex {Number} Index of the deleted TR element, if in view. */ /** * Fired when a row is selected. * * @event rowSelectEvent * @param oArgs.el {HTMLElement} The selected TR element, if applicable. * @param oArgs.record {YAHOO.widget.Record} The selected Record. */ /** * Fired when a row is unselected. * * @event rowUnselectEvent * @param oArgs.el {HTMLElement} The unselected TR element, if applicable. * @param oArgs.record {YAHOO.widget.Record} The unselected Record. */ /*TODO: delete and use rowUnselectEvent? * Fired when all row selections are cleared. * * @event unselectAllRowsEvent */ /* * Fired when a row is highlighted. * * @event rowHighlightEvent * @param oArgs.el {HTMLElement} The highlighted TR element. * @param oArgs.record {YAHOO.widget.Record} The highlighted Record. */ /* * Fired when a row is unhighlighted. * * @event rowUnhighlightEvent * @param oArgs.el {HTMLElement} The highlighted TR element. * @param oArgs.record {YAHOO.widget.Record} The highlighted Record. */ /** * Fired when a cell has a mouseover. * * @event cellMouseoverEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TD element. */ /** * Fired when a cell has a mouseout. * * @event cellMouseoutEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TD element. */ /** * Fired when a cell has a mousedown. * * @event cellMousedownEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TD element. */ /** * Fired when a cell has a click. * * @event cellClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TD element. */ /** * Fired when a cell has a dblclick. * * @event cellDblclickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The TD element. */ /** * Fired when a cell is formatted. * * @event cellFormatEvent * @param oArgs.el {HTMLElement} The formatted TD element. * @param oArgs.record {YAHOO.widget.Record} The formatted Record. * @param oArgs.key {String} The key of the formatted cell. */ /** * Fired when a cell is selected. * * @event cellSelectEvent * @param oArgs.el {HTMLElement} The selected TD element. * @param oArgs.record {YAHOO.widget.Record} The selected Record. * @param oArgs.key {String} The key of the selected cell. */ /** * Fired when a cell is unselected. * * @event cellUnselectEvent * @param oArgs.el {HTMLElement} The unselected TD element. * @param oArgs.record {YAHOO.widget.Record} The unselected Record. * @param oArgs.key {String} The key of the unselected cell. */ /** * Fired when a cell is highlighted. * * @event cellHighlightEvent * @param oArgs.el {HTMLElement} The highlighted TD element. * @param oArgs.record {YAHOO.widget.Record} The highlighted Record. * @param oArgs.key {String} The key of the highlighted cell. */ /** * Fired when a cell is unhighlighted. * * @event cellUnhighlightEvent * @param oArgs.el {HTMLElement} The unhighlighted TD element. * @param oArgs.record {YAHOO.widget.Record} The unhighlighted Record. * @param oArgs.key {String} The key of the unhighlighted cell. */ /*TODO: hide from doc and use cellUnselectEvent * Fired when all cell selections are cleared. * * @event unselectAllCellsEvent */ /*TODO: implement * Fired when DataTable paginator is updated. * * @event paginatorUpdateEvent * @param paginator {Object} Object literal of Paginator values. */ /** * Fired when an Editor is activated. * * @event editorShowEvent * @param oArgs.editor {Object} The Editor object literal. */ /** * Fired when an active Editor has a keydown. * * @event editorKeydownEvent * @param oArgs.editor {Object} The Editor object literal. * @param oArgs.event {HTMLEvent} The event object. */ /** * Fired when Editor input is reverted. * * @event editorRevertEvent * @param oArgs.editor {Object} The Editor object literal. * @param oArgs.newData {Object} New data value. * @param oArgs.oldData {Object} Old data value. */ /** * Fired when Editor input is saved. * * @event editorSaveEvent * @param oArgs.editor {Object} The Editor object literal. * @param oArgs.newData {Object} New data value. * @param oArgs.oldData {Object} Old data value. */ /** * Fired when Editor input is canceled. * * @event editorCancelEvent * @param oArgs.editor {Object} The Editor object literal. */ /** * Fired when an active Editor has a blur. * * @event editorBlurEvent * @param oArgs.editor {Object} The Editor object literal. */ /** * Fired when a link is clicked. * * @event linkClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The A element. */ /** * Fired when a BUTTON element is clicked. * * @event buttonClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The BUTTON element. */ /** * Fired when a CHECKBOX element is clicked. * * @event checkboxClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The CHECKBOX element. */ /*TODO * Fired when a SELECT element is changed. * * @event dropdownChangeEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The SELECT element. */ /** * Fired when a RADIO element is clicked. * * @event radioClickEvent * @param oArgs.event {HTMLEvent} The event object. * @param oArgs.target {HTMLElement} The RADIO element. */ /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /** * The ColumnSet class defines and manages a DataTable's Columns, * including nested hierarchies and access to individual Column instances. * * @namespace YAHOO.widget * @class ColumnSet * @uses YAHOO.util.EventProvider * @constructor * @param aHeaders {Object[]} Array of object literals that define header cells. */ YAHOO.widget.ColumnSet = function(aHeaders) { this._sName = "instance" + YAHOO.widget.ColumnSet._nCount; // DOM tree representation of all Columns var tree = []; // Flat representation of all Columns var flat = []; // Flat representation of only Columns that are meant to display data var keys = []; // Array of HEADERS attribute values for all keys in the "keys" array var headers = []; // Tracks current node list depth being tracked var nodeDepth = -1; // Internal recursive function to defined Column instances var parseColumns = function(nodeList, parent) { // One level down nodeDepth++; // Create corresponding tree node if not already there for this depth if(!tree[nodeDepth]) { tree[nodeDepth] = []; } // Parse each node at this depth for attributes and any children for(var j=0; j maxRowDepth) { maxRowDepth = tmpRowDepth; } } } }; // Count max row depth for each row for(var m=0; m b) { return (desc) ? -1 : 1; } else { return 0; } } }; /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /** * ColumnResizer subclasses DragDrop to support resizeable Columns. * * @namespace YAHOO.util * @class ColumnResizer * @extends YAHOO.util.DragDrop * @constructor * @param oDataTable {YAHOO.widget.DataTable} DataTable instance. * @param oColumn {YAHOO.widget.Column} Column instance. * @param elThead {HTMLElement} TH element reference. * @param sHandleElId {String} DOM ID of the handle element that causes the resize. * @param sGroup {String} Group name of related DragDrop items. * @param oConfig {Object} (Optional) Object literal of config values. */ YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elThead, sHandleId, sGroup, oConfig) { if(oDataTable && oColumn && elThead && sHandleId) { this.datatable = oDataTable; this.column = oColumn; this.cell = elThead; this.init(sHandleId, sGroup, oConfig); //this.initFrame(); this.setYConstraint(0,0); } else { YAHOO.log("Column resizer could not be created due to invalid colElId","warn"); } }; if(YAHOO.util.DD) { YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DD); } ///////////////////////////////////////////////////////////////////////////// // // Public DOM event handlers // ///////////////////////////////////////////////////////////////////////////// /** * Handles mousedown events on the Column resizer. * * @method onMouseDown * @param e {string} The mousedown event */ YAHOO.util.ColumnResizer.prototype.onMouseDown = function(e) { this.startWidth = this.cell.offsetWidth; this.startPos = YAHOO.util.Dom.getX(this.getDragEl()); if(this.datatable.fixedWidth) { var cellLabel = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_LABEL,"span",this.cell)[0]; this.minWidth = cellLabel.offsetWidth + 6; var sib = this.cell.nextSibling; var sibCellLabel = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_LABEL,"span",sib)[0]; this.sibMinWidth = sibCellLabel.offsetWidth + 6; //!! var left = ((this.startWidth - this.minWidth) < 0) ? 0 : (this.startWidth - this.minWidth); var right = ((sib.offsetWidth - this.sibMinWidth) < 0) ? 0 : (sib.offsetWidth - this.sibMinWidth); this.setXConstraint(left, right); YAHOO.log("cellstartwidth:" + this.startWidth,"time"); YAHOO.log("cellminwidth:" + this.minWidth,"time"); YAHOO.log("sibstartwidth:" + sib.offsetWidth,"time"); YAHOO.log("sibminwidth:" + this.sibMinWidth,"time"); YAHOO.log("l:" + left + " AND r:" + right,"time"); } }; /** * Handles mouseup events on the Column resizer. * * @method onMouseUp * @param e {string} The mouseup event */ YAHOO.util.ColumnResizer.prototype.onMouseUp = function(e) { //TODO: replace the resizer where it belongs: var resizeStyle = YAHOO.util.Dom.get(this.handleElId).style; resizeStyle.left = "auto"; resizeStyle.right = 0; resizeStyle.marginRight = "-6px"; resizeStyle.width = "6px"; //.yui-dt-headresizer {position:absolute;margin-right:-6px;right:0;bottom:0;width:6px;height:100%;cursor:w-resize;cursor:col-resize;} //var cells = this.datatable._elTable.tHead.rows[this.datatable._elTable.tHead.rows.length-1].cells; //for(var i=0; i -1)) { this._records.splice(index,0,oRecord); } else { index = this.getLength(); this._records.push(oRecord); } this._length++; return oRecord; }; /** * Deletes Records from the RecordSet at the given index. If range is null, * then only one Record is deleted. * * @method _deleteRecord * @param index {Number} Position index. * @param range {Number} (optional) How many Records to delete * @private */ YAHOO.widget.RecordSet.prototype._deleteRecord = function(index, range) { if(!YAHOO.lang.isNumber(range) || (range < 0)) { range = 1; } this._records.splice(index, range); this._length = this._length - range; }; ///////////////////////////////////////////////////////////////////////////// // // Public methods // ///////////////////////////////////////////////////////////////////////////// /** * Public accessor to the unique name of the RecordSet instance. * * @method toString * @return {String} Unique name of the RecordSet instance. */ YAHOO.widget.RecordSet.prototype.toString = function() { return this._sName; }; /** * Returns the number of Records held in the RecordSet. * * @method getLength * @return {Number} Number of records in the RecordSet. */ YAHOO.widget.RecordSet.prototype.getLength = function() { return this._length; }; /** * Returns Record at given position index. * * @method getRecord * @param index {Number} Record's Recordset position index. * @return {YAHOO.widget.Record} Record object. */ YAHOO.widget.RecordSet.prototype.getRecord = function(index) { if(YAHOO.lang.isNumber(index)) { return this._records[index]; } /*else if(YAHOO.lang.isString(identifier)) { for(var i=0; i-1; i--) { if(oRecord.getId() === this._records[i].getId()) { return i; } } return null; }; /** * Adds one Record to the RecordSet at the given index. If index is null, * then adds the Record to the end of the RecordSet. * * @method addRecord * @param oData {Object} An object literal of data. * @param index {Number} (optional) Position index. * @return {YAHOO.widget.Record} A Record instance. */ YAHOO.widget.RecordSet.prototype.addRecord = function(oData, index) { if(oData && (oData.constructor == Object)) { var oRecord = this._addRecord(oData, index); this.fireEvent("recordAddEvent",{record:oRecord,data:oData}); YAHOO.log("Added Record at index " + index + " with data " + YAHOO.lang.dump(oData), "info", this.toString()); return oRecord; } else { YAHOO.log("Could not add Record with data" + YAHOO.lang.dump(oData), "info", this.toString()); return null; } }; /** * Adds multiple Records at once to the RecordSet at the given index with the * given data. If index is null, then the new Records are added to the end of * the RecordSet. * * @method addRecords * @param aData {Object[]} An array of object literal data. * @param index {Number} (optional) Position index. * @return {YAHOO.widget.Record[]} An array of Record instances. */ YAHOO.widget.RecordSet.prototype.addRecords = function(aData, index) { if(YAHOO.lang.isArray(aData)) { var newRecords = []; // Can't go backwards bc we need to preserve order for(var i=0; i -1) && (index < this.getLength())) { // Copy data from the Record for the event that gets fired later var oRecordData = this.getRecord(index).getData(); var oData = {}; for(var key in oRecordData) { oData[key] = oRecordData[key]; } this._deleteRecord(index); this.fireEvent("recordDeleteEvent",{data:oData,index:index}); YAHOO.log("Record deleted at index " + index + " and containing data " + YAHOO.lang.dump(oData), "info", this.toString()); return oData; } else { YAHOO.log("Could not delete Record at index " + index, "error", this.toString()); return null; } }; /** * Removes the Record at the given position index from the RecordSet. If a range * is also provided, removes that many Records, starting from the index. Length * of RecordSet is correspondingly shortened. * * @method deleteRecords * @param index {Number} Record's RecordSet position index. * @param range {Number} (optional) How many Records to delete. */ YAHOO.widget.RecordSet.prototype.deleteRecords = function(index, range) { if(!YAHOO.lang.isNumber(range)) { range = 1; } if(YAHOO.lang.isNumber(index) && (index > -1) && (index < this.getLength())) { var recordsToDelete = this.getRecords(index, range); // Copy data from each Record for the event that gets fired later var deletedData = []; for(var i=0; i