/* Copyright (c) 2007, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.2.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. * * @class DataTable * @uses YAHOO.util.EventProvider * @constructor * @param elContainer {HTMLElement} Container element for the TABLE. * @param oColumnSet {YAHOO.widget.ColumnSet} ColumnSet instance. * @param oDataSource {YAHOO.util.DataSource} DataSource instance. * @param oConfigs {object} (optional) Object literal of configuration values. */ YAHOO.widget.DataTable = function(elContainer,oColumnSet,oDataSource,oConfigs) { // Internal vars var i; this._nIndex = YAHOO.widget.DataTable._nCount; this._sName = "instance" + this._nIndex; this.id = "yui-dt"+this._nIndex; // Validate configs if(typeof oConfigs == "object") { for(var sConfig in oConfigs) { this[sConfig] = oConfigs[sConfig]; } } // Validate DataSource if(oDataSource) { if(oDataSource instanceof YAHOO.util.DataSource) { this.dataSource = oDataSource; } else { } } // Validate ColumnSet if(oColumnSet && (oColumnSet instanceof YAHOO.widget.ColumnSet)) { this._oColumnSet = oColumnSet; } else { return; } // Create RecordSet this._oRecordSet = new YAHOO.widget.RecordSet(); // Validate HTML Element elContainer = YAHOO.util.Dom.get(elContainer); if(elContainer && elContainer.tagName && (elContainer.tagName.toLowerCase() == "div")) { this._elContainer = elContainer; // Peek in container child nodes to see if TABLE already exists var elTable = null; if(elContainer.hasChildNodes()) { var children = elContainer.childNodes; for(i=0; i" + contentText + ""; //elHeadContent.innerHTML = contentText; } else { elHeadContent.innerHTML = contentText; } }; /** * Add a new row to table body at position i if given, or to the bottom * otherwise. Does not fire any events. * * @method _addRow * @param oRecord {YAHOO.widget.Record} Record instance. * @param index {Number} Position at which to add row. * @return {String} ID of the added TR element. * @private */ YAHOO.widget.DataTable.prototype._addRow = function(oRecord, index) { this.hideTableMessages(); // Is this an insert or an append? var insert = (isNaN(index)) ? false : true; if(!insert) { index = this._elBody.rows.length; } var oColumnSet = this._oColumnSet; var oRecordSet = this._oRecordSet; var elRow = (insert && this._elBody.rows[index]) ? this._elBody.insertBefore(document.createElement("tr"),this._elBody.rows[index]) : this._elBody.appendChild(document.createElement("tr")); var recId = oRecord.id; elRow.id = this.id+"-bdrow"+index; elRow.recordId = recId; // Create TBODY cells for(var j=0; j 0)) { //TODO: hideMessages() //this._initRows() //this.isEmpty = false; } // Striping if(!insert) { if(index%2) { YAHOO.util.Dom.addClass(elRow, YAHOO.widget.DataTable.CLASS_ODD); } else { YAHOO.util.Dom.addClass(elRow, YAHOO.widget.DataTable.CLASS_EVEN); } } else { //TODO: pass in a subset for better performance this._restripeRows(); } return elRow.id; }; /** * Restripes rows by applying class YAHOO.widget.DataTable.CLASS_EVEN or * YAHOO.widget.DataTable.CLASS_ODD. * * @method _restripeRows * @param range {Number} (optional) Range defines a subset of rows to restripe. * @private */ YAHOO.widget.DataTable.prototype._restripeRows = function(range) { if(!range) { var rows = this._elBody.rows; for(var i=0; i 0)) { if(!e.shiftKey) { oSelf.unselectAllRows(); } newSelected = oSelf._elBody.rows[oldSelected.sectionRowIndex-1]; oSelf.select(newSelected); } } // cell mode else if(oldSelected.tagName.toLowerCase() == "td") { // We have room to move up if((oldSelected.sectionRowIndex > 0)) { if(!e.shiftKey) { oSelf.unselectAllRows(); } newSelected = oSelf._elBody.rows[oldSelected.sectionRowIndex-1]; oSelf.select(newSelected); } } // Arrows can cause widget to lose focus oSelf._bFocused = false; oSelf.focusTable(); } } }; /** * Handles keyup events on the TABLE. Executes deletion * * @method _onKeyup * @param e {HTMLEvent} The key event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onKeyup = function(e, oSelf) { var key = YAHOO.util.Event.getCharCode(e); // delete if(key == 46) {//TODO: && this.isFocused //TODO: delete row } }; /** * Handles keyup events on the DOCUMENT. Executes interaction with editor. * * @method _onDocumentKeyup * @param e {HTMLEvent} The key event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onDocumentKeyup = function(e, oSelf) { // esc Clears active editor if((e.keyCode == 27) && (oSelf.activeEditor)) { oSelf.activeEditor.hide(); oSelf.activeEditor = null; // Editor causes widget to lose focus oSelf._bFocused = false; oSelf.focusTable(); } // enter Saves active editor data if((e.keyCode == 13) && (oSelf.activeEditor)) { var elCell = oSelf.activeEditor.cell; var oColumn = oSelf.activeEditor.column; var oRecord = oSelf.activeEditor.record; var oldValue = oRecord[oColumn.key]; var newValue = oSelf.activeEditor.getValue(); //Update Record //TODO: Column.key may be null! oSelf._oRecordSet.updateRecord(oRecord,oColumn.key,newValue); //Update cell oSelf.formatCell(elCell); // Hide editor oSelf.activeEditor.hide(); oSelf.activeEditor = null; // Editor causes widget to lose focus oSelf._bFocused = false; oSelf.focusTable(); oSelf.fireEvent("cellEditEvent",{target:elCell,oldData:oldValue,newData:newValue}); } }; /** * Handles click events on paginator links. * * @method _onPagerClick * @param e {HTMLEvent} The click event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onPagerClick = function(e, oSelf) { var elTarget = YAHOO.util.Event.getTarget(e); var elTag = elTarget.tagName.toLowerCase(); var knownTag = false; // True if event should stop propagating if (elTag != "table") { while(!knownTag) { switch(elTag) { case "body": knownTag = true; break; case "a": YAHOO.util.Event.stopEvent(e); switch(elTarget.className) { case YAHOO.widget.DataTable.CLASS_PAGELINK: oSelf.showPage(parseInt(elTarget.innerHTML,10)); break; case YAHOO.widget.DataTable.CLASS_FIRSTLINK: oSelf.showPage(1); break; case YAHOO.widget.DataTable.CLASS_LASTLINK: oSelf.showPage(oSelf._totalPages); break; case YAHOO.widget.DataTable.CLASS_PREVLINK: oSelf.showPage(oSelf.pageCurrent-1); break; case YAHOO.widget.DataTable.CLASS_NEXTLINK: oSelf.showPage(oSelf.pageCurrent+1); break; } knownTag = true; break; default: break; } elTarget = elTarget.parentNode; if(elTarget) { elTag = elTarget.tagName.toLowerCase(); } else { break; } } } }; /** * Handles change events on paginator SELECT. * * @method _onPagerSelect * @param e {HTMLEvent} The change event. * @param oSelf {YAHOO.widget.DataTable} DataTable instance. * @private */ YAHOO.widget.DataTable.prototype._onPagerSelect = function(e, oSelf) { var elTarget = YAHOO.util.Event.getTarget(e); var elTag = elTarget.tagName.toLowerCase(); // How many rows per page var oldRowsPerPage = oSelf.rowsPerPage; var rowsPerPage = parseInt(elTarget[elTarget.selectedIndex].text,10); if(rowsPerPage && (rowsPerPage != oSelf.rowsPerPage)) { if(rowsPerPage > oldRowsPerPage) { oSelf.pageCurrent = 1; } oSelf.rowsPerPage = rowsPerPage; oSelf.paginateRows(); } }; ///////////////////////////////////////////////////////////////////////////// // // Private Custom Event Handlers // ///////////////////////////////////////////////////////////////////////////// /** * Handles row delete events. * * @method _onRowDelete * @param oArgs.rowIndexes {Number[]} The indexes of the deleted rows. * @private */ YAHOO.widget.DataTable.prototype._onRowDelete = function(oArgs) { this._restripeRows(); }; /** * Passes along recordSetUpdate Event when recordUpdateEvent is caught from RecordSet. * * @event _onRecordUpdate * @param oArgs.record {YAHOO.widget.Record} The Record instance. * @param oArgs.key {String} The Record key. * @param oArgs.newData {Object} New data. * @param oArgs.oldData {Object} New data. * @private */ YAHOO.widget.DataTable.prototype._onRecordUpdate = function(oArgs) { this.fireEvent("recordSetUpdateEvent",oArgs); }; ///////////////////////////////////////////////////////////////////////////// // // Public member variables // ///////////////////////////////////////////////////////////////////////////// /** * DataSource instance. * * @property dataSource * @type YAHOO.util.DataSource */ YAHOO.widget.DataTable.prototype.dataSource = null; /** * Initial request to send to DataSource. * * @property initialRequest * @type String * @default "" */ YAHOO.widget.DataTable.prototype.initialRequest = ""; /** * Defines value of CAPTION attribute. * * @property caption * @type String */ YAHOO.widget.DataTable.prototype.caption = null; /** * Defines value of SUMMARY attribute. * * @property summary * @type String */ YAHOO.widget.DataTable.prototype.summary = null; /** * True if DataTable's width is a fixed size. * * @property fixedWidth * @type Boolean * @default false */ YAHOO.widget.DataTable.prototype.fixedWidth = false; /** * True if TBODY should scroll while THEAD remains fixed. * * @property scrollable * @type Boolean * @default false */ YAHOO.widget.DataTable.prototype.scrollable = false; /** * True if only one row may be selected at a time. * * @property rowSingleSelect * @type Boolean * @default false */ YAHOO.widget.DataTable.prototype.rowSingleSelect = false; /** * ContextMenu instance. * * @property contextMenu * @type YAHOO.widget.ContextMenu */ YAHOO.widget.DataTable.prototype.contextMenu = null; /** * Current page number. * * @property pageCurrent * @type Number * @default 1 */ YAHOO.widget.DataTable.prototype.pageCurrent = 1; /** * Rows per page. * * @property rowsPerPage * @type Number * @default 500 */ YAHOO.widget.DataTable.prototype.rowsPerPage = 500; /** * Record index of first row of current page. * * @property startRecordIndex * @type Number * @default 1 */ YAHOO.widget.DataTable.prototype.startRecordIndex = 1; /** * Maximum number of pagination page links to show. Any page links beyond this number are * available through the "<" and ">" links. A negative value will display all page links. * * @property pageLinksLength * @type Number * @default -1 */ YAHOO.widget.DataTable.prototype.pageLinksLength = -1; /** * Options to show in the rows-per-page pagination dropdown, should be an array * of numbers. Null or an empty array causes no dropdown to be displayed. * * @property rowsPerPageDropdown * @type Number[] */ YAHOO.widget.DataTable.prototype.rowsPerPageDropdown = null; /** * First pagination page link. * * @property pageLinksStart * @type Number * @default 1 */ YAHOO.widget.DataTable.prototype.pageLinksStart = 1; /** * An array of DIV elements into which pagination elements can go. * * @property pagers * @type HTMLElement[] */ YAHOO.widget.DataTable.prototype.pagers = null; /** * True if the DataTable is empty of data. False if DataTable is populated with * data from RecordSet. * * @property isEmpty * @type Boolean */ YAHOO.widget.DataTable.prototype.isEmpty = false; /** * Object literal holds sort metadata: * sortedBy.colKey * sortedBy.dir * * * @property sortedBy * @type Object */ YAHOO.widget.DataTable.prototype.sortedBy = null; ///////////////////////////////////////////////////////////////////////////// // // Public methods // ///////////////////////////////////////////////////////////////////////////// /** * 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 element reference to TABLE. * * @method getTable * @return {HTMLElement} Reference to TABLE element. */ YAHOO.widget.DataTable.prototype.getTable = function() { return(this._elTable); }; /** * Returns element reference to THEAD. * * @method getHead * @return {HTMLElement} Reference to THEAD element. */ YAHOO.widget.DataTable.prototype.getHead = function() { return(this._elHead); }; /** * Returns element reference to TBODY. * * @method getBody * @return {HTMLElement} Reference to TBODY element. */ YAHOO.widget.DataTable.prototype.getBody = function() { return(this._elBody); }; /** * Returns element reference to given TR cell. * * @method getRow * @param index {Number} Row number. * @return {HTMLElement} Reference to TR element. */ YAHOO.widget.DataTable.prototype.getRow = function(index) { return(this._elBody.rows[index]); }; /** * Returns element reference to given TD cell. * * @method getCell * @param row {Number} Row number. * @param col {Number} Column number. * @return {HTMLElement} Reference to TD element. */ YAHOO.widget.DataTable.prototype.getCell = function(row, col) { return(this._elBody.rows[row].cells[col]); }; /** * Placeholder row to indicate table data is empty. * * @method showEmptyMessage */ YAHOO.widget.DataTable.prototype.showEmptyMessage = function() { if(this.isEmpty) { return; } if(this.isLoading) { this.hideTableMessages(); } this._elMsgBody.style.display = ""; var elCell = this._elMsgCell; elCell.className = YAHOO.widget.DataTable.CLASS_EMPTY; elCell.innerHTML = YAHOO.widget.DataTable.MSG_EMPTY; this.isEmpty = true; }; /** * Placeholder row to indicate table data is loading. * * @method showLoadingMessage */ YAHOO.widget.DataTable.prototype.showLoadingMessage = function() { if(this.isLoading) { return; } if(this.isEmpty) { this.hideTableMessages(); } this._elMsgBody.style.display = ""; var elCell = this._elMsgCell; elCell.className = YAHOO.widget.DataTable.CLASS_LOADING; elCell.innerHTML = YAHOO.widget.DataTable.MSG_LOADING; this.isLoading = true; }; /** * Hide any placeholder message row. * * @method hideTableMessages */ YAHOO.widget.DataTable.prototype.hideTableMessages = function() { if(!this.isEmpty && !this.isLoading) { return; } this._elMsgBody.style.display = "none"; this.isEmpty = false; this.isLoading = false; }; /** * Sets focus on the TABLE element. * * @method focusTable */ YAHOO.widget.DataTable.prototype.focusTable = function() { var elTable = this._elTable; if(!this._bFocused) { // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing // strange unexpected things as the user clicks on buttons and other controls. setTimeout(function() { elTable.focus(); },0); this._bFocused = true; this.fireEvent("tableFocusEvent"); } }; /** * Overridable method gives implementers a hook to access data before * it gets added to RecordSet and rendered to the TBODY. * * @method doBeforeLoadData * @param sRequest {String} Original request. * @param oResponse {Object} Response object. * @return {Boolean} Return true to continue loading data into RecordSet and * updating DataTable with new Records, false to cancel. */ YAHOO.widget.DataTable.prototype.doBeforeLoadData = function(sRequest, oResponse) { return true; }; /** * Add rows to bottom of table body. * * @method appendRow * @param aRecords {YAHOO.widget.Record[]} Array of Records. */ YAHOO.widget.DataTable.prototype.appendRows = function(aRecords) { if(aRecords && aRecords.length > 0) { this.hideTableMessages(); var rowIds = []; for(var i=0; i 0) { this.hideTableMessages(); var rowIds = []; for(var i=0; i 0) { this.hideTableMessages(); var elBody = this._elBody; var elRows = this._elBody.rows; // Remove extra rows while(elBody.hasChildNodes() && (elRows.length > aRecords.length)) { elBody.deleteRow(0); } // Unselect rows in the UI but keep tracking selected rows var selectedRecords = this.getSelectedRecordIds(); if(selectedRecords.length > 0) { this._unselectAllRows(); } var rowIds = []; // Format in-place existing rows for(i=0; i -1)) { tracker.splice(tracker.indexOf(id),1); } // ...or do it the old-fashioned way else { for(var j=0; j -1)) { tracker.splice(tracker.indexOf(id),1); } // ...or do it the old-fashioned way else { for(var j=0; j this._totalPages)) { nPage = 1; } this.pageCurrent = nPage; this.paginateRows(); }; /** * If pagination is enabled, paginates all data in RecordSet and renders * paginator UI, others renders normal TBODY without any paginator UI. * * @method paginateRows */ YAHOO.widget.DataTable.prototype.paginateRows = function() { var i; // How many total Records var recordsLength = this._oRecordSet.getLength(); // How many rows this page var maxRows = (this.rowsPerPage < recordsLength) ? this.rowsPerPage : recordsLength; // How many total pages this._totalPages = Math.ceil(recordsLength / maxRows); // First row of this page this.startRecordIndex = (this.pageCurrent-1) * this.rowsPerPage; // How many page links to display var pageLinksLength = ((this.pageLinksLength > 0) && (this.pageLinksLength < this._totalPages)) ? this.pageLinksLength : this._totalPages; // First link of this page this.pageLinksStart = (Math.ceil(this.pageCurrent/pageLinksLength-1) * pageLinksLength) + 1; // Show Records for this page var pageRecords = this._oRecordSet.getRecords(this.startRecordIndex, this.rowsPerPage); this.replaceRows(pageRecords); if(this.rowsPerPage < recordsLength) { // Markup for page links var isFirstPage = (this.pageCurrent == 1) ? true : false; var isLastPage = (this.pageCurrent == this._totalPages) ? true : false; var firstPageLink = (isFirstPage) ? " << " : " << "; var prevPageLink = (isFirstPage) ? " < " : " < " ; var nextPageLink = (isLastPage) ? " > " : " > " ; var lastPageLink = (isLastPage) ? " >> " : " >> "; var markup = firstPageLink + prevPageLink; var maxLinks = (this.pageLinksStart+pageLinksLength < this._totalPages) ? this.pageLinksStart+pageLinksLength-1 : this._totalPages; for(i=this.pageLinksStart; i<=maxLinks; i++) { if(i != this.pageCurrent) { markup += " " + i + " "; } else { markup += " " + i + ""; } } markup += nextPageLink + lastPageLink; // Markup for rows-per-page dropdowns var dropdown = this.rowsPerPageDropdown; var select1, select2; if(dropdown && (dropdown.constructor == Array) && (dropdown.length > 0)) { select1 = document.createElement("select"); select1.className = YAHOO.widget.DataTable.CLASS_PAGESELECT; select2 = document.createElement("select"); select2.className = YAHOO.widget.DataTable.CLASS_PAGESELECT; for(i=0; i nodeLevelMaxChildren) { nodeLevelMaxChildren = tmpMax; } } }; recurseChildren(nodeList); // Parse each node for attributes and any children for(var j=0; j 0) { // Children of siblings increase the rowspan of the Column oColumn._rowspan += nodeLevelMaxChildren; //if(oColumn.key) { oColumn._index = keys.length; keys.push(oColumn); //} } // This entire node level does not have any children else { //if(oColumn.key) { oColumn._index = keys.length; keys.push(oColumn); //} } // Add the Column to the top-down tree tree[nodelevel].push(oColumn); } nodelevel--; }; // Do the parsing if(aHeaders.length > 0) { parseColumns(aHeaders); } // Store header nesting in an array var recurseAncestors = function(i, oColumn) { headers[i].push(oColumn._id); if(oColumn._parent) { recurseAncestors(i, oColumn._parent); } }; for(var i=0; i"+oData.toString()+"" : ""; classname = YAHOO.widget.DataTable.CLASS_STRING; break; } YAHOO.util.Dom.addClass(elCell, classname); if(this.className) { YAHOO.util.Dom.addClass(elCell, this.className); } } if(this.editor) { YAHOO.util.Dom.addClass(elCell,YAHOO.widget.DataTable.CLASS_EDITABLE); } }; /** * Formats cells in Columns of type "checkbox". * * @method formatCheckbox * @param elCell {HTMLElement} Table cell element. * @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.Column.formatCheckbox = function(elCell, oRecord, oColumn, oData) { var bChecked = oData; bChecked = (bChecked) ? " checked" : ""; elCell.innerHTML = ""; }; /** * Formats cells in Columns of type "currency". Can be overridden for custom formatting. * * @method formatCurrency * @param elCell {HTMLElement} Table cell element. * @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.Column.formatCurrency = function(elCell, oRecord, oColumn, oData) { // Make it dollars var nAmount = oData; var markup; if(nAmount) { markup = "$"+nAmount; // Normalize to the penny var dotIndex = markup.indexOf("."); if(dotIndex < 0) { markup += ".00"; } else { while(dotIndex != markup.length-3) { markup += "0"; } } } else { markup = ""; } elCell.innerHTML = markup; }; /** * Formats cells in Columns of type "date". * * @method formatDate * @param elCell {HTMLElement} Table cell element. * @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.Column.formatDate = function(elCell, oRecord, oColumn, oData) { var oDate = oData; if(oDate) { elCell.innerHTML = oDate.getMonth() + "/" + oDate.getDate() + "/" + oDate.getFullYear(); } else { elCell.innerHTML = ""; } }; /** * Formats cells in Columns of type "email". * * @method formatEmail * @param elCell {HTMLElement} Table cell element. * @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.Column.formatEmail = function(elCell, oRecord, oColumn, oData) { var sEmail = oData; if(sEmail) { elCell.innerHTML = "" + sEmail + ""; } else { elCell.innerHTML = ""; } }; /** * Formats cells in Columns of type "link". * * @method formatLink * @param elCell {HTMLElement} Table cell element. * @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.Column.formatLink = function(elCell, oRecord, oColumn, oData) { var sLink = oData; if(sLink) { elCell.innerHTML = "" + sLink + ""; } else { elCell.innerHTML = ""; } }; /** * Formats cells in Columns of type "number". * * @method formatNumber * @param elCell {HTMLElement} Table cell element. * @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.Column.formatNumber = function(elCell, oRecord, oColumn, oData) { var nNumber = oData; if(nNumber) { elCell.innerHTML = nNumber.toString(); } else { elCell.innerHTML = ""; } }; /** * Formats cells in Columns of type "select". * * @method formatSelect * @param elCell {HTMLElement} Table cell element. * @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.Column.formatSelect = function(elCell, oRecord, oColumn, oData) { var selectedValue = oData; var options = oColumn.selectOptions; var markup = ""; elCell.innerHTML = markup; }; /** * Takes innerHTML from TD and parses out data for storage in RecordSet. * * @method parse * @param sMarkup {String} The TD's innerHTML value. * @return {Object} Data. */ YAHOO.widget.Column.prototype.parse = function(sMarkup) { if(this.parser) { return this.parser(sMarkup); } else { var data = null; switch(this.type) { case "checkbox": data = YAHOO.widget.Column.parseCheckbox(sMarkup); break; case "currency": data = YAHOO.widget.Column.parseCurrency(sMarkup); break; case "date": data = YAHOO.widget.Column.parseDate(sMarkup); break; case "number": data = YAHOO.widget.Column.parseNumber(sMarkup); break; case "select": data = YAHOO.widget.Column.parseSelect(sMarkup); break; default: if(sMarkup) { data = sMarkup; } break; } return data; } }; /** * Default parse function for Columns of type "checkbox" takes markup and * extracts data. Can be overridden for custom parsing. * * @method parseCheckbox * @param sMarkup * @return {bChecked} True if checkbox is checked. */ YAHOO.widget.Column.parseCheckbox = function(sMarkup) { return (sMarkup.indexOf("checked") < 0) ? false : true; }; /** * Default parse function for Columns of type "currency" takes markup and * extracts data. Can be overridden for custom parsing. * * @method parseCurrency * @param sMarkup * @return {nAmount} Floating point amount. */ YAHOO.widget.Column.parseCurrency = function(sMarkup) { return parseFloat(sMarkup.substring(1)); }; /** * Default parse function for Columns of type "date" takes markup and extracts * data. Can be overridden for custom parsing. * * @method parseDate * @param sMarkup * @return {oDate} Date instance. */ YAHOO.widget.Column.parseDate = function(sMarkup) { var mm = sMarkup.substring(0,sMarkup.indexOf("/")); sMarkup = sMarkup.substring(sMarkup.indexOf("/")+1); var dd = sMarkup.substring(0,sMarkup.indexOf("/")); var yy = sMarkup.substring(sMarkup.indexOf("/")+1); return new Date(yy, mm, dd); }; /** * Default parse function for Columns of type "number" takes markup and extracts * data. Can be overridden for custom parsing. * * @method parseNumber * @param sMarkup * @return {nNumber} Number. */ YAHOO.widget.Column.parseNumber = function(sMarkup) { return parseFloat(sMarkup); }; /** * Default parse function for Columns of type "select" takes markup and extracts * data. Can be overridden for custom parsing. * * @method parseSelect * @param sMarkup * @return {sValue} Value of selected option. */ YAHOO.widget.Column.parseSelect = function(sMarkup) { //return (sMarkup.indexOf("checked") < 0) ? false : true; }; /** * Outputs editor markup into the given TD based on given Record. * * @method showEditor * @param elCell {HTMLElement} The cell to edit. * @param oRecord {YAHOO.widget.Record} The DataTable Record of the cell. * @return YAHOO.widget.ColumnEditor */ YAHOO.widget.Column.prototype.getEditor = function(elCell, oRecord) { //Sync up the arg signature for ColumnEditor constructor and show() var oEditor = this.editor; if(oEditor.constructor == String) { oEditor = new YAHOO.widget.ColumnEditor(this.editor); oEditor.show(elCell, oRecord, this); this.editor = oEditor; } else if(oEditor instanceof YAHOO.widget.ColumnEditor) { oEditor.show(elCell, oRecord, this); } return oEditor; }; /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /** * The ColumnEditor defines and manages inline editing functionality for a * DataTable Column. * * @class ColumnEditor * @constructor * @param elCell {HTMLElement} The cell to edit. * @param oRecord {YAHOO.widget.Record} The DataTable Record of the cell. * @param oColumn {YAHOO.widget.Column} The DataTable Column of the cell. * @parem sType {String} Type identifier */ YAHOO.widget.ColumnEditor = function(sType) { this.type = sType; //TODO: make sure ColumnEditors get destroyed if widget gets destroyed // Works better to attach ColumnEditor to document.body // rather than the DataTable container // elTable comes in as a cell. Traverse up DOM to find the table. // TODO: safety net in case table is never found. //while(elCell.nodeName.toLowerCase() != "table") { // elCell = elCell.parentNode; //} //this.tableContainer = elCell.parentNode; var container = document.body.appendChild(document.createElement("div"));//this.tableContainer.appendChild(document.createElement("div")); container.style.position = "absolute"; container.style.zIndex = 9000; container.id = "yui-dt-coled" + YAHOO.widget.ColumnEditor._nCount; this.container = container; switch(this.type) { case "textbox": this.createTextboxEditor(); break; case "textarea": this.createTextareaEditor(); break; default: break; } YAHOO.widget.ColumnEditor._nCount++; }; ///////////////////////////////////////////////////////////////////////////// // // Private member variables // ///////////////////////////////////////////////////////////////////////////// /** * Internal instance counter. * * @property _nCount * @type Number * @static * @default 0 */ YAHOO.widget.ColumnEditor._nCount =0; ///////////////////////////////////////////////////////////////////////////// // // Public member variables // ///////////////////////////////////////////////////////////////////////////// /** * Reference to the container DOM element for the ColumnEditor. * * @property container * @type HTMLElement */ YAHOO.widget.ColumnEditor.prototype.container = null; /** * Reference to the ColumnEditor's Column instance. * * @property column * @type YAHOO.widget.Column */ YAHOO.widget.ColumnEditor.prototype.column = null; /** * Type of editor: "textbox", etc. * * @property type * @type String */ YAHOO.widget.ColumnEditor.prototype.type = null; /** * Reference to form element(s) of the ColumnEditor. * * @property input * @type HTMLElement || HTMLElement[] */ YAHOO.widget.ColumnEditor.prototype.input = null; ///////////////////////////////////////////////////////////////////////////// // // Public methods // ///////////////////////////////////////////////////////////////////////////// /** * Shows ColumnEditor. * * @method show * @param elCell {HTMLElement} The cell to edit. * @param oRecord {YAHOO.widget.Record} The DataTable Record of the cell. * @param oColumn {YAHOO.widget.Column} The DataTable Column of the cell. */ YAHOO.widget.ColumnEditor.prototype.show = function(elCell, oRecord, oColumn) { this.cell = elCell; this.record = oRecord; this.column = oColumn; switch(this.type) { case "textbox": this.showTextboxEditor(elCell, oRecord, oColumn); break; case "textarea": this.showTextareaEditor(elCell, oRecord, oColumn); break; default: break; } }; /** * Returns ColumnEditor data value. * * @method getValue * @return Object */ YAHOO.widget.ColumnEditor.prototype.getValue = function() { var value; switch(this.type) { case "textbox": value = this.getTextboxEditorValue(); break; case "textarea": value = this.getTextareaEditorValue(); break; default: break; } return value; }; /** * Creates a textbox editor in the DOM. * * @method createTextboxEditor * @return {HTML} ??? */ YAHOO.widget.ColumnEditor.prototype.createTextboxEditor = function() { var elTextbox = this.container.appendChild(document.createElement("input")); // For FF bug 236791 elTextbox.setAttribute("autocomplete","off"); this.input = elTextbox; }; /** * Creates a textarea editor in the DOM. * * @method createTextareaEditor * @return {HTML} ??? */ YAHOO.widget.ColumnEditor.prototype.createTextareaEditor = function() { var elTextarea = this.container.appendChild(document.createElement("textarea")); this.input = elTextarea; }; /** * Shows ColumnEditor * * @method showTextboxEditor * @param elCell {HTMLElement} The cell to edit. * @param oRecord {YAHOO.widget.Record} The DataTable Record of the cell. * @param oColumn {YAHOO.widget.Column} The DataTable Column of the cell. */ YAHOO.widget.ColumnEditor.prototype.showTextboxEditor = function(elCell, oRecord, oColumn) { // Size and value this.input.style.width = (parseInt(elCell.offsetWidth,10)-7) + "px"; this.input.style.height = (parseInt(elCell.offsetHeight,10)-7) + "px"; this.input.value = elCell.innerHTML; // Position and show var x,y; // Don't use getXY for Opera if(navigator.userAgent.toLowerCase().indexOf("opera") != -1) { x = elCell.offsetLeft; y = elCell.offsetTop; while(elCell.offsetParent) { x += elCell.offsetParent.offsetLeft; y += elCell.offsetParent.offsetTop; elCell = elCell.offsetParent; } } else { var xy = YAHOO.util.Dom.getXY(elCell); x = parseInt(YAHOO.util.Dom.getX(elCell),10);//xy[0] + 1; y = parseInt(YAHOO.util.Dom.getY(elCell),10);//xy[1] + 1; } this.container.style.left = x + "px"; this.container.style.top = y + "px"; this.container.style.display = "block"; this.input.tabIndex = 0; this.input.focus(); this.input.select(); }; /** * Shows ColumnEditor * * @method showTextareaEditor * @param elCell {HTMLElement} The cell to edit. * @param oRecord {YAHOO.widget.Record} The DataTable Record of the cell. * @param oColumn {YAHOO.widget.Column} The DataTable Column of the cell. */ YAHOO.widget.ColumnEditor.prototype.showTextareaEditor = function(elCell, oRecord, oColumn) { // Size and value this.input.style.width = (parseInt(elCell.offsetWidth,10)-7) + "px"; this.input.style.height = 4*(parseInt(elCell.offsetHeight,10)-7) + "px"; this.input.value = elCell.innerHTML; // Position and show var x,y; // Don't use getXY for Opera if(navigator.userAgent.toLowerCase().indexOf("opera") != -1) { x = elCell.offsetLeft; y = elCell.offsetTop; while(elCell.offsetParent) { x += elCell.offsetParent.offsetLeft; y += elCell.offsetParent.offsetTop; elCell = elCell.offsetParent; } } else { var xy = YAHOO.util.Dom.getXY(elCell); x = parseInt(YAHOO.util.Dom.getX(elCell),10);//xy[0] + 1; y = parseInt(YAHOO.util.Dom.getY(elCell),10);//xy[1] + 1; } this.container.style.left = x + "px"; this.container.style.top = y + "px"; this.container.style.display = "block"; this.input.tabIndex = 0; this.input.focus(); this.input.select(); }; /** * Hides ColumnEditor * * @method hide */ YAHOO.widget.ColumnEditor.prototype.hide = function() { this.input.tabIndex = -1; this.container.style.display = "none"; }; /** * Returns ColumnEditor value * * @method getTextboxEditorValue * @return String */ YAHOO.widget.ColumnEditor.prototype.getTextboxEditorValue = function() { return this.input.value; }; /** * Returns ColumnEditor value * * @method getTextareaEditorValue * @return String */ YAHOO.widget.ColumnEditor.prototype.getTextareaEditorValue = function() { return this.input.value; }; /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /** * Sort static utility to support Column sorting. * * @class Sort * @static */ YAHOO.util.Sort = { ///////////////////////////////////////////////////////////////////////////// // // Public methods // ///////////////////////////////////////////////////////////////////////////// /** * Comparator function for sort in ascending order. String sorting is case insensitive. * * @method compareAsc * @param a {object} First sort argument. * @param b {object} Second sort argument. */ compareAsc: function(a, b) { //TODO: is typeof better or is constructor property better? if(a.constructor == String) { a = a.toLowerCase(); } if(b.constructor == String) { b = b.toLowerCase(); } if(a < b) { return -1; } else if (a > b) { return 1; } else { return 0; } }, /** * Comparator function for sort in descending order. String sorting is case insensitive. * * @method compareDesc * @param a {object} First sort argument. * @param b {object} Second sort argument. */ compareDesc: function(a, b) { //TODO: is typeof better or is constructor property better? if(a.constructor == String) { a = a.toLowerCase(); } if(b.constructor == String) { b = b.toLowerCase(); } if(a < b) { return 1; } else if (a > b) { return -1; } else { return 0; } } }; /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /** * WidthResizer subclasses DragDrop to support resizeable Columns. * * @class WidthResizer * @extends YAHOO.util.DragDrop * @constructor * @param colElId {string} ID of the Column's TH element being resized * @param handleElId {string} ID of the handle element that causes the resize * @param sGroup {string} Group name of related DragDrop items */ YAHOO.util.WidthResizer = function(oDataTable, colId, handleId, sGroup, config) { if (colId) { this.cell = YAHOO.util.Dom.get(colId); this.init(handleId, sGroup, config); //this.initFrame(); this.datatable = oDataTable; this.setYConstraint(0,0); } else { } }; if(YAHOO.util.DD) { YAHOO.extend(YAHOO.util.WidthResizer, YAHOO.util.DD); } ///////////////////////////////////////////////////////////////////////////// // // Public DOM event handlers // ///////////////////////////////////////////////////////////////////////////// /** * Handles mousedown events on the Column resizer. * * @method onMouseDown * @param e {string} The mousedown event */ YAHOO.util.WidthResizer.prototype.onMouseDown = function(e) { this.startWidth = this.cell.offsetWidth; this.startPos = YAHOO.util.Dom.getX(this.getDragEl()); if(this.datatable.fixedwidth) { var cellText = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_COLUMNTEXT,"span",this.cell)[0]; this.minWidth = cellText.offsetWidth + 6; var sib = this.cell.nextSibling; var sibCellText = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_COLUMNTEXT,"span",sib)[0]; this.sibMinWidth = sibCellText.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); } }; /** * Handles mouseup events on the Column resizer. * * @method onMouseUp * @param e {string} The mouseup event */ YAHOO.util.WidthResizer.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; i0; i--) { record = this._records[i]; if(record && (record.extid == extId)) { return record; } } return null; };*/ /** * Updates given Record at given key with given data. * * @method updateRecord * @param oRecord {YAHOO.widget.Record} A Record instance. * @param sKey {String} Key. * @param oData {Object) New data. */ YAHOO.widget.RecordSet.prototype.updateRecord = function(oRecord, sKey, oData) { var oldData = oRecord[sKey]; oRecord[sKey] = oData; this.fireEvent("recordUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData}); }; /** * 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 oObjectLiteral {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(oObjectLiteral, index) { if(oObjectLiteral) { var oRecord = new YAHOO.widget.Record(oObjectLiteral); if(!isNaN(index) && (index > -1)) { this._records.splice(index,0,oRecord); } else { this._records.push(oRecord); } this._length++; return oRecord; } else { return null; } }; /** * Adds multiple Records to the RecordSet at the given index. If index is null, * then adds the Records to the end of the RecordSet. * * @method addRecords * @param data {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(data, index) { if(data) { if(data.constructor == Array) { var newRecords = []; // Can't go backwards bc we need to preserve order for(var i=0; i-1; i--) { var record = this.addRecord(data[i], 0); newRecords.push(record); } return newRecords; } else if(data.constructor == Object) { return this.addRecord(data, 0); } } else { return null; } }; /** * Replaces all Records in RecordSet with new data. * * @method replace * @param data {Object || Object[]} An object literal or array or data. * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record or array of Records. */ YAHOO.widget.RecordSet.prototype.replace = function(data) { if(data) { this.reset(); return this.append(data); } else { return null; } }; /** * Sorts RecordSet by given function. * * @method sort * @param fnSort {Function} Reference to a sort function. * @return {Array} Sorted array of Records */ YAHOO.widget.RecordSet.prototype.sort = function(fnSort) { return this._records.sort(fnSort); }; /** * Removes the record at the given index from the RecordSet. If a range is * given, starts at the given index and removes all records in the range. * * @method deleteRecord * @param i {Number} Record index * @param range {Number} (optional) Range of records to remove, or null. */ YAHOO.widget.RecordSet.prototype.deleteRecord = function(i, range) { if(!range || isNaN(range)) { range = 1; } if(i && !isNaN(i)) { this._records.splice(i, range); this._length = this._length - range; } }; /** * Removes all Records from the RecordSet. * * @method reset */ YAHOO.widget.RecordSet.prototype.reset = function() { this._records = []; this._length = 0; }; /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /** * The Record class defines a DataTable record. * * @class Record * @constructor * @param oConfigs {Object} (optional) Object literal of key/value pairs. */ YAHOO.widget.Record = function(oLiteral) { if(typeof oLiteral == "object") { for(var sKey in oLiteral) { if(sKey) { this[sKey] = oLiteral[sKey]; } } } this.id = "yui-dtrec"+YAHOO.widget.Record._nCount; YAHOO.widget.Record._nCount++; }; ///////////////////////////////////////////////////////////////////////////// // // Private member variables // ///////////////////////////////////////////////////////////////////////////// /** * Internal class variable to index multiple data table instances. * * @property _nCount * @type number * @private * @static */ YAHOO.widget.Record._nCount = 0; ///////////////////////////////////////////////////////////////////////////// // // Public member variables // ///////////////////////////////////////////////////////////////////////////// /** * Unique name assigned at instantation, indicates original order. * * @property id * @type string */ YAHOO.widget.Record.prototype.id = null; YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.2.0", build: "127"});