/*
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