2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * The DataTable widget provides a progressively enhanced DHTML control for
9 * displaying tabular data across A-grade browsers.
12 * @requires yahoo, dom, event, datasource
14 * @title DataTable Widget
18 /****************************************************************************/
19 /****************************************************************************/
20 /****************************************************************************/
23 * DataTable class for the YUI DataTable widget.
25 * @namespace YAHOO.widget
27 * @uses YAHOO.util.EventProvider
29 * @param elContainer {HTMLElement} Container element for the TABLE.
30 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
31 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
32 * @param oConfigs {object} (optional) Object literal of configuration values.
34 YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
36 this._nIndex = YAHOO.widget.DataTable._nCount;
37 this._sName = "instance" + this._nIndex;
38 this.id = "yui-dt"+this._nIndex;
40 // Initialize container element
41 this._initContainerEl(elContainer);
42 if(!this._elContainer) {
47 this._initConfigs(oConfigs);
49 // Initialize ColumnSet
50 this._initColumnSet(aColumnDefs);
51 if(!this._oColumnSet) {
55 // Initialize RecordSet
56 this._initRecordSet();
57 if(!this._oRecordSet) {
61 // Initialize DataSource
62 this._initDataSource(oDataSource);
63 if(!this._oDataSource) {
67 // Progressive enhancement special case
68 if(this._oDataSource.dataType == YAHOO.util.DataSource.TYPE_HTMLTABLE) {
69 this._oDataSource.sendRequest(this.get("initialRequest"), this._onDataReturnEnhanceTable, this);
72 // Initialize DOM elements
74 if(!this._elTable || !this._elThead || !this._elTbody) {
78 // Call Element's constructor after DOM elements are created
79 // but *before* table is populated with data
80 YAHOO.widget.DataTable.superclass.constructor.call(this, this._elContainer, this._oConfigs);
82 //HACK: Set the Paginator values here via updatePaginator
83 if(this._oConfigs && this._oConfigs.paginator) {
84 this.updatePaginator(this._oConfigs.paginator);
87 // Send out for data in an asynchronous request
88 this._oDataSource.sendRequest(this.get("initialRequest"), this.onDataReturnInitializeTable, this);
91 // Initialize inline Cell editing
92 this._initCellEditorEl();
94 // Initialize Column sort
95 this._initColumnSort();
97 // Initialize DOM event listeners
98 this._initDomEvents();
100 YAHOO.widget.DataTable._nCount++;
103 if(YAHOO.util.Element) {
104 YAHOO.lang.extend(YAHOO.widget.DataTable, YAHOO.util.Element);
109 /////////////////////////////////////////////////////////////////////////////
111 // Superclass methods
113 /////////////////////////////////////////////////////////////////////////////
116 * Implementation of Element's abstract method. Sets up config values.
118 * @method initAttributes
119 * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
123 YAHOO.widget.DataTable.prototype.initAttributes = function(oConfigs) {
124 oConfigs = oConfigs || {};
125 YAHOO.widget.DataTable.superclass.initAttributes.call(this, oConfigs);
129 * @description Value for the SUMMARY attribute.
132 this.setAttributeConfig("summary", {
134 validator: YAHOO.lang.isString,
135 method: function(sSummary) {
136 this._elTable.summary = sSummary;
141 * @config selectionMode
142 * @description Specifies row or cell selection mode. Accepts the following strings:
144 * <dt>"standard"</dt>
145 * <dd>Standard row selection with support for modifier keys to enable
146 * multiple selections.</dd>
149 * <dd>Row selection with modifier keys disabled to not allow
150 * multiple selections.</dd>
152 * <dt>"singlecell"</dt>
153 * <dd>Cell selection with modifier keys disabled to not allow
154 * multiple selections.</dd>
156 * <dt>"cellblock"</dt>
157 * <dd>Cell selection with support for modifier keys to enable multiple
158 * selections in a block-fashion, like a spreadsheet.</dd>
160 * <dt>"cellrange"</dt>
161 * <dd>Cell selection with support for modifier keys to enable multiple
162 * selections in a range-fashion, like a calendar.</dd>
165 * @default "standard"
168 this.setAttributeConfig("selectionMode", {
170 validator: YAHOO.lang.isString
174 * @config initialRequest
175 * @description Defines the initial request that gets sent to the DataSource.
178 this.setAttributeConfig("initialRequest", {
180 validator: YAHOO.lang.isString
185 * @description Object literal provides metadata for initial sort values if
186 * data will arrive pre-sorted:
188 * <dt>sortedBy.key</dt>
189 * <dd>Key of sorted Column</dd>
190 * <dt>sortedBy.dir</dt>
191 * <dd>Initial sort direction, either "asc" or "desc"</dd>
195 this.setAttributeConfig("sortedBy", {
197 // TODO: accepted array for nested sorts
198 validator: function(oNewSortedBy) {
199 return (oNewSortedBy && (oNewSortedBy.constructor == Object) && oNewSortedBy.key);
201 method: function(oNewSortedBy) {
202 // Remove ASC/DESC from TH
203 var oOldSortedBy = this.get("sortedBy");
204 if(oOldSortedBy && (oOldSortedBy.constructor == Object) && oOldSortedBy.key) {
205 var oldColumn = this._oColumnSet.getColumn(oOldSortedBy.key);
206 var oldThEl = this.getThEl(oldColumn);
207 YAHOO.util.Dom.removeClass(oldThEl, YAHOO.widget.DataTable.CLASS_ASC);
208 YAHOO.util.Dom.removeClass(oldThEl, YAHOO.widget.DataTable.CLASS_DESC);
211 // Set ASC/DESC on TH
212 var column = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key);
214 var newClass = (oNewSortedBy.dir && (oNewSortedBy.dir != "asc")) ?
215 YAHOO.widget.DataTable.CLASS_DESC :
216 YAHOO.widget.DataTable.CLASS_ASC;
217 YAHOO.util.Dom.addClass(this.id + "-col" + column.getId(), newClass);
224 * @description Object literal of pagination values.
226 * { containers:[], // UI container elements <br>
227 * rowsPerPage:500, // 500 rows <br>
228 * currentPage:1, // page one <br>
229 * pageLinks:0, // show all links <br>
230 * pageLinksStart:1, // first link is page 1 <br>
231 * dropdownOptions:null, // no dropdown <br>
232 * links: [], // links elements <br>
233 * dropdowns: [] } //dropdown elements
237 this.setAttributeConfig("paginator", {
239 rowsPerPage:500, // 500 rows per page
240 currentPage:1, // show page one
241 startRecordIndex:0, // start with first Record
242 totalRecords:0, // how many Records total
243 totalPages:0, // how many pages total
244 rowsThisPage:0, // how many rows this page
245 pageLinks:0, // show all links
246 pageLinksStart:1, // first link is page 1
247 dropdownOptions: null, //no dropdown
248 containers:[], // Paginator container element references
249 dropdowns: [], //dropdown element references,
250 links: [] // links elements
252 validator: function(oNewPaginator) {
253 if(oNewPaginator && (oNewPaginator.constructor == Object)) {
254 // Check for incomplete set of values
255 if((oNewPaginator.rowsPerPage !== undefined) &&
256 (oNewPaginator.currentPage !== undefined) &&
257 (oNewPaginator.startRecordIndex !== undefined) &&
258 (oNewPaginator.totalRecords !== undefined) &&
259 (oNewPaginator.totalPages !== undefined) &&
260 (oNewPaginator.rowsThisPage !== undefined) &&
261 (oNewPaginator.pageLinks !== undefined) &&
262 (oNewPaginator.pageLinksStart !== undefined) &&
263 (oNewPaginator.dropdownOptions !== undefined) &&
264 (oNewPaginator.containers !== undefined) &&
265 (oNewPaginator.dropdowns !== undefined) &&
266 (oNewPaginator.links !== undefined)) {
268 // Validate each value
269 if(YAHOO.lang.isNumber(oNewPaginator.rowsPerPage) &&
270 YAHOO.lang.isNumber(oNewPaginator.currentPage) &&
271 YAHOO.lang.isNumber(oNewPaginator.startRecordIndex) &&
272 YAHOO.lang.isNumber(oNewPaginator.totalRecords) &&
273 YAHOO.lang.isNumber(oNewPaginator.totalPages) &&
274 YAHOO.lang.isNumber(oNewPaginator.rowsThisPage) &&
275 YAHOO.lang.isNumber(oNewPaginator.pageLinks) &&
276 YAHOO.lang.isNumber(oNewPaginator.pageLinksStart) &&
277 YAHOO.lang.isArray(oNewPaginator.dropdownOptions) &&
278 YAHOO.lang.isArray(oNewPaginator.containers) &&
279 YAHOO.lang.isArray(oNewPaginator.dropdowns) &&
280 YAHOO.lang.isArray(oNewPaginator.links)) {
291 * @description True if built-in client-side pagination is enabled
295 this.setAttributeConfig("paginated", {
297 validator: YAHOO.lang.isBoolean,
298 method: function(oParam) {
299 var oPaginator = this.get("paginator");
300 var aContainerEls = oPaginator.containers;
302 // Paginator is enabled
304 // No containers found, create two from scratch
305 if(aContainerEls.length === 0) {
307 var pag0 = document.createElement("span");
308 pag0.id = this.id + "-paginator0";
309 YAHOO.util.Dom.addClass(pag0, YAHOO.widget.DataTable.CLASS_PAGINATOR);
310 pag0 = this._elContainer.insertBefore(pag0, this._elTable);
311 aContainerEls.push(pag0);
314 var pag1 = document.createElement("span");
315 pag1.id = this.id + "-paginator1";
316 YAHOO.util.Dom.addClass(pag1, YAHOO.widget.DataTable.CLASS_PAGINATOR);
317 pag1 = this._elContainer.insertBefore(pag1, this._elTable.nextSibling);
318 aContainerEls.push(pag1);
320 // Add containers directly to tracker
321 this._configs.paginator.value.containers = [pag0, pag1];
325 // Show each container
326 for(var i=0; i<aContainerEls.length; i++) {
327 aContainerEls[i].style.display = "";
332 if(oPaginator.pageLinks > -1) {
333 var aLinkEls = oPaginator.links;
334 // No links containers found, create from scratch
335 if(aLinkEls.length === 0) {
336 for(i=0; i<aContainerEls.length; i++) {
337 // Create one links container per Paginator container
338 var linkEl = document.createElement("span");
339 linkEl.id = "yui-dt-pagselect"+i;
340 linkEl = aContainerEls[i].appendChild(linkEl);
342 // Add event listener
344 YAHOO.util.Event.addListener(linkEl,"click",this._onPaginatorLinkClick,this);
346 // Add directly to tracker
347 this._configs.paginator.value.links.push(linkEl);
352 // Show these options in the dropdown
353 var dropdownOptions = oPaginator.dropdownOptions || [];
355 for(i=0; i<aContainerEls.length; i++) {
356 // Create one SELECT element per Paginator container
357 var selectEl = document.createElement("select");
358 YAHOO.util.Dom.addClass(selectEl, YAHOO.widget.DataTable.CLASS_DROPDOWN);
359 selectEl = aContainerEls[i].appendChild(selectEl);
360 selectEl.id = "yui-dt-pagselect"+i;
362 // Add event listener
364 YAHOO.util.Event.addListener(selectEl,"change",this._onPaginatorDropdownChange,this);
366 // Add DOM reference directly to tracker
367 this._configs.paginator.value.dropdowns.push(selectEl);
370 if(!oPaginator.dropdownOptions) {
371 selectEl.style.display = "none";
375 //TODO: fire paginatorDisabledEvent & add to api doc
377 // Pagination is disabled
380 if(aContainerEls.length > 0) {
381 // Destroy or just hide?
383 // Hide each container
384 for(i=0; i<aContainerEls.length; i++) {
385 aContainerEls[i].style.display = "none";
389 // Destroy each container
390 for(i=0; i<aContainerEls.length; i++) {
391 YAHOO.util.Event.purgeElement(aContainerEls[i], true);
392 aContainerEls.innerHTML = null;
393 //TODO: remove container?
394 // aContainerEls[i].parentNode.removeChild(aContainerEls[i]);
398 //TODO: fire paginatorDisabledEvent & add to api doc
405 * @description Value for the CAPTION element.
408 this.setAttributeConfig("caption", {
410 validator: YAHOO.lang.isString,
411 method: function(sCaption) {
412 // Create CAPTION element
413 if(!this._elCaption) {
414 if(!this._elTable.firstChild) {
415 this._elCaption = this._elTable.appendChild(document.createElement("caption"));
418 this._elCaption = this._elTable.insertBefore(document.createElement("caption"), this._elTable.firstChild);
422 this._elCaption.innerHTML = sCaption;
428 * @description True if primary TBODY should scroll while THEAD remains fixed.
429 * When enabling this feature, captions cannot be used, and the following
430 * features are not recommended: inline editing, resizeable columns.
434 this.setAttributeConfig("scrollable", {
436 validator: function(oParam) {
437 //TODO: validate agnst resizeable
438 return (YAHOO.lang.isBoolean(oParam) &&
439 // Not compatible with caption
440 !YAHOO.lang.isString(this.get("caption")));
442 method: function(oParam) {
445 YAHOO.util.Dom.addClass(this._elContainer,YAHOO.widget.DataTable.CLASS_SCROLLABLE);
446 YAHOO.util.Dom.addClass(this._elTbody,YAHOO.widget.DataTable.CLASS_SCROLLBODY);
449 YAHOO.util.Dom.removeClass(this._elContainer,YAHOO.widget.DataTable.CLASS_SCROLLABLE);
450 YAHOO.util.Dom.removeClass(this._elTbody,YAHOO.widget.DataTable.CLASS_SCROLLBODY);
457 /////////////////////////////////////////////////////////////////////////////
461 /////////////////////////////////////////////////////////////////////////////
464 * Class name assigned to TABLE element.
466 * @property DataTable.CLASS_TABLE
470 * @default "yui-dt-table"
472 YAHOO.widget.DataTable.CLASS_TABLE = "yui-dt-table";
475 * Class name assigned to header container elements within each TH element.
477 * @property DataTable.CLASS_HEADER
481 * @default "yui-dt-header"
483 YAHOO.widget.DataTable.CLASS_HEADER = "yui-dt-header";
486 * Class name assigned to the primary TBODY element.
488 * @property DataTable.CLASS_BODY
492 * @default "yui-dt-body"
494 YAHOO.widget.DataTable.CLASS_BODY = "yui-dt-body";
497 * Class name assigned to the scrolling TBODY element of a fixed scrolling DataTable.
499 * @property DataTable.CLASS_SCROLLBODY
503 * @default "yui-dt-scrollbody"
505 YAHOO.widget.DataTable.CLASS_SCROLLBODY = "yui-dt-scrollbody";
508 * Class name assigned to display label elements.
510 * @property DataTable.CLASS_LABEL
514 * @default "yui-dt-label"
516 YAHOO.widget.DataTable.CLASS_LABEL = "yui-dt-label";
519 * Class name assigned to resizer handle elements.
521 * @property DataTable.CLASS_RESIZER
525 * @default "yui-dt-resizer"
527 YAHOO.widget.DataTable.CLASS_RESIZER = "yui-dt-resizer";
530 * Class name assigned to Editor container elements.
532 * @property DataTable.CLASS_EDITOR
536 * @default "yui-dt-editor"
538 YAHOO.widget.DataTable.CLASS_EDITOR = "yui-dt-editor";
541 * Class name assigned to paginator container elements.
543 * @property DataTable.CLASS_PAGINATOR
547 * @default "yui-dt-paginator"
549 YAHOO.widget.DataTable.CLASS_PAGINATOR = "yui-dt-paginator";
552 * Class name assigned to page number indicators.
554 * @property DataTable.CLASS_PAGE
558 * @default "yui-dt-page"
560 YAHOO.widget.DataTable.CLASS_PAGE = "yui-dt-page";
563 * Class name assigned to default indicators.
565 * @property DataTable.CLASS_DEFAULT
569 * @default "yui-dt-default"
571 YAHOO.widget.DataTable.CLASS_DEFAULT = "yui-dt-default";
574 * Class name assigned to previous indicators.
576 * @property DataTable.CLASS_PREVIOUS
580 * @default "yui-dt-previous"
582 YAHOO.widget.DataTable.CLASS_PREVIOUS = "yui-dt-previous";
585 * Class name assigned next indicators.
587 * @property DataTable.CLASS_NEXT
591 * @default "yui-dt-next"
593 YAHOO.widget.DataTable.CLASS_NEXT = "yui-dt-next";
596 * Class name assigned to first elements.
598 * @property DataTable.CLASS_FIRST
602 * @default "yui-dt-first"
604 YAHOO.widget.DataTable.CLASS_FIRST = "yui-dt-first";
607 * Class name assigned to last elements.
609 * @property DataTable.CLASS_LAST
613 * @default "yui-dt-last"
615 YAHOO.widget.DataTable.CLASS_LAST = "yui-dt-last";
618 * Class name assigned to even elements.
620 * @property DataTable.CLASS_EVEN
624 * @default "yui-dt-even"
626 YAHOO.widget.DataTable.CLASS_EVEN = "yui-dt-even";
629 * Class name assigned to odd elements.
631 * @property DataTable.CLASS_ODD
635 * @default "yui-dt-odd"
637 YAHOO.widget.DataTable.CLASS_ODD = "yui-dt-odd";
640 * Class name assigned to selected elements.
642 * @property DataTable.CLASS_SELECTED
646 * @default "yui-dt-selected"
648 YAHOO.widget.DataTable.CLASS_SELECTED = "yui-dt-selected";
651 * Class name assigned to highlighted elements.
653 * @property DataTable.CLASS_HIGHLIGHTED
657 * @default "yui-dt-highlighted"
659 YAHOO.widget.DataTable.CLASS_HIGHLIGHTED = "yui-dt-highlighted";
662 * Class name assigned to disabled elements.
664 * @property DataTable.CLASS_DISABLED
668 * @default "yui-dt-disabled"
670 YAHOO.widget.DataTable.CLASS_DISABLED = "yui-dt-disabled";
673 * Class name assigned to empty indicators.
675 * @property DataTable.CLASS_EMPTY
679 * @default "yui-dt-empty"
681 YAHOO.widget.DataTable.CLASS_EMPTY = "yui-dt-empty";
684 * Class name assigned to loading indicatorx.
686 * @property DataTable.CLASS_LOADING
690 * @default "yui-dt-loading"
692 YAHOO.widget.DataTable.CLASS_LOADING = "yui-dt-loading";
695 * Class name assigned to error indicators.
697 * @property DataTable.CLASS_ERROR
701 * @default "yui-dt-error"
703 YAHOO.widget.DataTable.CLASS_ERROR = "yui-dt-error";
706 * Class name assigned to editable elements.
708 * @property DataTable.CLASS_EDITABLE
712 * @default "yui-dt-editable"
714 YAHOO.widget.DataTable.CLASS_EDITABLE = "yui-dt-editable";
717 * Class name assigned to scrollable elements.
719 * @property DataTable.CLASS_SCROLLABLE
723 * @default "yui-dt-scrollable"
725 YAHOO.widget.DataTable.CLASS_SCROLLABLE = "yui-dt-scrollable";
728 * Class name assigned to sortable elements.
730 * @property DataTable.CLASS_SORTABLE
734 * @default "yui-dt-sortable"
736 YAHOO.widget.DataTable.CLASS_SORTABLE = "yui-dt-sortable";
739 * Class name assigned to ascending elements.
741 * @property DataTable.CLASS_ASC
745 * @default "yui-dt-asc"
747 YAHOO.widget.DataTable.CLASS_ASC = "yui-dt-asc";
750 * Class name assigned to descending elements.
752 * @property DataTable.CLASS_DESC
756 * @default "yui-dt-desc"
758 YAHOO.widget.DataTable.CLASS_DESC = "yui-dt-desc";
761 * Class name assigned to BUTTON container elements.
763 * @property DataTable.CLASS_BUTTON
767 * @default "yui-dt-button"
769 YAHOO.widget.DataTable.CLASS_BUTTON = "yui-dt-button";
772 * Class name assigned to SELECT container elements.
774 * @property DataTable.CLASS_DROPDOWN
778 * @default "yui-dt-dropdown"
780 YAHOO.widget.DataTable.CLASS_DROPDOWN = "yui-dt-dropdown";
783 * Class name assigned to INPUT TYPE=CHECKBOX container elements.
785 * @property DataTable.CLASS_CHECKBOX
789 * @default "yui-dt-checkbox"
791 YAHOO.widget.DataTable.CLASS_CHECKBOX = "yui-dt-checkbox";
794 * Message to display if DataTable has no data.
796 * @property DataTable.MSG_EMPTY
800 * @default "No records found."
802 YAHOO.widget.DataTable.MSG_EMPTY = "No records found.";
805 * Message to display while DataTable is loading data.
807 * @property DataTable.MSG_LOADING
811 * @default "Loading data..."
813 YAHOO.widget.DataTable.MSG_LOADING = "Loading data...";
816 * Message to display while DataTable has data error.
818 * @property DataTable.MSG_ERROR
822 * @default "Data error."
824 YAHOO.widget.DataTable.MSG_ERROR = "Data error.";
826 /////////////////////////////////////////////////////////////////////////////
828 // Private member variables
830 /////////////////////////////////////////////////////////////////////////////
833 * Internal class variable for indexing multiple DataTable instances.
835 * @property DataTable._nCount
840 YAHOO.widget.DataTable._nCount = 0;
843 * Index assigned to instance.
849 YAHOO.widget.DataTable.prototype._nIndex = null;
852 * Counter for IDs assigned to TR elements.
854 * @property _nTrCount
858 YAHOO.widget.DataTable.prototype._nTrCount = 0;
861 * Unique name assigned to instance.
867 YAHOO.widget.DataTable.prototype._sName = null;
870 * DOM reference to the container element for the DataTable instance into which
871 * the TABLE element gets created.
873 * @property _elContainer
877 YAHOO.widget.DataTable.prototype._elContainer = null;
880 * DOM reference to the CAPTION element for the DataTable instance.
882 * @property _elCaption
886 YAHOO.widget.DataTable.prototype._elCaption = null;
889 * DOM reference to the TABLE element for the DataTable instance.
895 YAHOO.widget.DataTable.prototype._elTable = null;
898 * DOM reference to the THEAD element for the DataTable instance.
904 YAHOO.widget.DataTable.prototype._elThead = null;
907 * DOM reference to the primary TBODY element for the DataTable instance.
913 YAHOO.widget.DataTable.prototype._elTbody = null;
916 * DOM reference to the secondary TBODY element used to display DataTable messages.
918 * @property _elMsgTbody
922 YAHOO.widget.DataTable.prototype._elMsgTbody = null;
925 * DOM reference to the secondary TBODY element's single TR element used to display DataTable messages.
927 * @property _elMsgTbodyRow
931 YAHOO.widget.DataTable.prototype._elMsgTbodyRow = null;
934 * DOM reference to the secondary TBODY element's single TD element used to display DataTable messages.
936 * @property _elMsgTbodyCell
940 YAHOO.widget.DataTable.prototype._elMsgTbodyCell = null;
943 * DataSource instance for the DataTable instance.
945 * @property _oDataSource
946 * @type YAHOO.util.DataSource
949 YAHOO.widget.DataTable.prototype._oDataSource = null;
952 * ColumnSet instance for the DataTable instance.
954 * @property _oColumnSet
955 * @type YAHOO.widget.ColumnSet
958 YAHOO.widget.DataTable.prototype._oColumnSet = null;
961 * RecordSet instance for the DataTable instance.
963 * @property _oRecordSet
964 * @type YAHOO.widget.RecordSet
967 YAHOO.widget.DataTable.prototype._oRecordSet = null;
970 * ID string of first label link element of the current DataTable page, if any.
971 * Used for focusing sortable Columns with TAB.
973 * @property _sFirstLabelLinkId
977 YAHOO.widget.DataTable.prototype._sFirstLabelLinkId = null;
980 * ID string of first TR element of the current DataTable page.
982 * @property _sFirstTrId
986 YAHOO.widget.DataTable.prototype._sFirstTrId = null;
989 * ID string of the last TR element of the current DataTable page.
991 * @property _sLastTrId
995 YAHOO.widget.DataTable.prototype._sLastTrId = null;
1028 /////////////////////////////////////////////////////////////////////////////
1032 /////////////////////////////////////////////////////////////////////////////
1035 * Sets focus on the given element.
1038 * @param el {HTMLElement} Element.
1041 YAHOO.widget.DataTable.prototype._focusEl = function(el) {
1042 el = el || this._elTable;
1043 // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
1044 // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
1045 // strange unexpected things as the user clicks on buttons and other controls.
1046 setTimeout(function() { el.focus(); },0);
1056 * Initializes container element.
1058 * @method _initContainerEl
1059 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
1062 YAHOO.widget.DataTable.prototype._initContainerEl = function(elContainer) {
1063 this._elContainer = null;
1064 elContainer = YAHOO.util.Dom.get(elContainer);
1065 if(elContainer && elContainer.tagName && (elContainer.tagName.toLowerCase() == "div")) {
1066 this._elContainer = elContainer;
1071 * Initializes object literal of config values.
1073 * @method _initConfigs
1074 * @param oConfig {Object} Object literal of config values.
1077 YAHOO.widget.DataTable.prototype._initConfigs = function(oConfigs) {
1079 if(oConfigs.constructor != Object) {
1082 // Backward compatibility
1083 else if(YAHOO.lang.isBoolean(oConfigs.paginator)) {
1085 this._oConfigs = oConfigs;
1090 * Initializes ColumnSet.
1092 * @method _initColumnSet
1093 * @param aColumnDefs {Object[]} Array of object literal Column definitions.
1096 YAHOO.widget.DataTable.prototype._initColumnSet = function(aColumnDefs) {
1097 this._oColumnSet = null;
1098 if(YAHOO.lang.isArray(aColumnDefs)) {
1099 this._oColumnSet = new YAHOO.widget.ColumnSet(aColumnDefs);
1101 // Backward compatibility
1102 else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) {
1103 this._oColumnSet = aColumnDefs;
1108 * Initializes DataSource.
1110 * @method _initDataSource
1111 * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
1114 YAHOO.widget.DataTable.prototype._initDataSource = function(oDataSource) {
1115 this._oDataSource = null;
1116 if(oDataSource && (oDataSource instanceof YAHOO.util.DataSource)) {
1117 this._oDataSource = oDataSource;
1119 // Backward compatibility
1121 var tmpTable = null;
1122 var tmpContainer = this._elContainer;
1123 // Peek in container child nodes to see if TABLE already exists
1124 if(tmpContainer.hasChildNodes()) {
1125 var tmpChildren = tmpContainer.childNodes;
1126 for(i=0; i<tmpChildren.length; i++) {
1127 if(tmpChildren[i].tagName && tmpChildren[i].tagName.toLowerCase() == "table") {
1128 tmpTable = tmpChildren[i];
1133 var tmpFieldsArray = [];
1134 for(i=0; i<this._oColumnSet.keys.length; i++) {
1135 tmpFieldsArray.push({key:this._oColumnSet.keys[i].key});
1138 this._oDataSource = new YAHOO.util.DataSource(tmpTable);
1139 this._oDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
1140 this._oDataSource.responseSchema = {fields: tmpFieldsArray};
1147 * Initializes RecordSet.
1149 * @method _initRecordSet
1152 YAHOO.widget.DataTable.prototype._initRecordSet = function() {
1153 if(this._oRecordSet) {
1154 this._oRecordSet.reset();
1157 this._oRecordSet = new YAHOO.widget.RecordSet();
1162 * Creates HTML markup for TABLE, THEAD and TBODY elements.
1164 * @method _initTableEl
1167 YAHOO.widget.DataTable.prototype._initTableEl = function() {
1168 // Clear the container
1169 YAHOO.util.Event.purgeElement(this._elContainer, true);
1170 this._elContainer.innerHTML = "";
1173 this._elTable = this._elContainer.appendChild(document.createElement("table"));
1174 var elTable = this._elTable;
1175 elTable.tabIndex = 0;
1176 elTable.id = this.id + "-table";
1177 YAHOO.util.Dom.addClass(elTable, YAHOO.widget.DataTable.CLASS_TABLE);
1180 this._initTheadEl(elTable, this._oColumnSet);
1183 // Create TBODY for messages
1184 var elMsgTbody = document.createElement("tbody");
1185 var elMsgRow = elMsgTbody.appendChild(document.createElement("tr"));
1186 YAHOO.util.Dom.addClass(elMsgRow,YAHOO.widget.DataTable.CLASS_FIRST);
1187 YAHOO.util.Dom.addClass(elMsgRow,YAHOO.widget.DataTable.CLASS_LAST);
1188 this._elMsgRow = elMsgRow;
1189 var elMsgCell = elMsgRow.appendChild(document.createElement("td"));
1190 elMsgCell.colSpan = this._oColumnSet.keys.length;
1191 YAHOO.util.Dom.addClass(elMsgCell,YAHOO.widget.DataTable.CLASS_FIRST);
1192 YAHOO.util.Dom.addClass(elMsgCell,YAHOO.widget.DataTable.CLASS_LAST);
1193 this._elMsgTd = elMsgCell;
1194 this._elMsgTbody = elTable.appendChild(elMsgTbody);
1195 this.showTableMessage(YAHOO.widget.DataTable.MSG_LOADING, YAHOO.widget.DataTable.CLASS_LOADING);
1197 // Create TBODY for data
1198 this._elTbody = elTable.appendChild(document.createElement("tbody"));
1199 YAHOO.util.Dom.addClass(this._elTbody,YAHOO.widget.DataTable.CLASS_BODY);
1203 * Populates THEAD element with TH cells as defined by ColumnSet.
1205 * @method _initTheadEl
1208 YAHOO.widget.DataTable.prototype._initTheadEl = function() {
1209 var i,oColumn, colId;
1210 var oColumnSet = this._oColumnSet;
1211 this._sFirstLabelLinkId = null;
1214 var elThead = document.createElement("thead");
1216 // Iterate through each row of Column headers...
1217 var colTree = oColumnSet.tree;
1218 for(i=0; i<colTree.length; i++) {
1219 var elTheadRow = elThead.appendChild(document.createElement("tr"));
1220 elTheadRow.id = this.id+"-hdrow"+i;
1223 // ...and create THEAD cells
1224 for(var j=0; j<colTree[i].length; j++) {
1225 oColumn = colTree[i][j];
1226 colId = oColumn.getId();
1227 elTheadCell = elTheadRow.appendChild(document.createElement("th"));
1228 elTheadCell.id = this.id + "-col" + colId;
1229 this._initThEl(elTheadCell,oColumn,i,j);
1232 // Set FIRST/LAST on THEAD rows
1234 YAHOO.util.Dom.addClass(elTheadRow, YAHOO.widget.DataTable.CLASS_FIRST);
1236 if(i === (colTree.length-1)) {
1237 YAHOO.util.Dom.addClass(elTheadRow, YAHOO.widget.DataTable.CLASS_LAST);
1241 this._elThead = this._elTable.appendChild(elThead);
1243 // Set FIRST/LAST on THEAD cells using the values in ColumnSet headers array
1244 var aFirstHeaders = oColumnSet.headers[0].split(" ");
1245 var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1].split(" ");
1246 for(i=0; i<aFirstHeaders.length; i++) {
1247 YAHOO.util.Dom.addClass(YAHOO.util.Dom.get(this.id+"-col"+aFirstHeaders[i]), YAHOO.widget.DataTable.CLASS_FIRST);
1249 for(i=0; i<aLastHeaders.length; i++) {
1250 YAHOO.util.Dom.addClass(YAHOO.util.Dom.get(this.id+"-col"+aLastHeaders[i]), YAHOO.widget.DataTable.CLASS_LAST);
1253 // Add Resizer only after DOM has been updated
1254 var foundDD = (YAHOO.util.DD) ? true : false;
1256 for(i=0; i<this._oColumnSet.keys.length; i++) {
1257 oColumn = this._oColumnSet.keys[i];
1258 colId = oColumn.getId();
1259 var elTheadCellId = YAHOO.util.Dom.get(this.id + "-col" + colId);
1260 if(oColumn.resizeable) {
1262 //TODO: fix fixed width tables
1263 // Skip the last column for fixed-width tables
1264 if(!this.fixedWidth || (this.fixedWidth &&
1265 (oColumn.getKeyIndex() != this._oColumnSet.keys.length-1))) {
1266 // TODO: better way to get elTheadContainer
1267 var elThContainer = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_HEADER,"div",elTheadCellId)[0];
1268 var elThResizer = elThContainer.appendChild(document.createElement("span"));
1269 elThResizer.id = this.id + "-resizer" + colId;
1270 YAHOO.util.Dom.addClass(elThResizer,YAHOO.widget.DataTable.CLASS_RESIZER);
1271 oColumn.ddResizer = new YAHOO.util.ColumnResizer(
1272 this, oColumn, elTheadCellId, elThResizer.id, elThResizer.id);
1273 var cancelClick = function(e) {
1274 YAHOO.util.Event.stopPropagation(e);
1276 YAHOO.util.Event.addListener(elThResizer,"click",cancelClick);
1278 if(this.fixedWidth) {
1279 //TODO: fix fixedWidth
1280 //elThContainer.style.overflow = "hidden";
1281 //TODO: better way to get elTheadText
1282 var elThLabel = (YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_LABEL,"span",elTheadCellId))[0];
1283 elThLabel.style.overflow = "hidden";
1297 * Populates TH cell as defined by Column.
1300 * @param elTheadCell {HTMLElement} TH cell element reference.
1301 * @param oColumn {YAHOO.widget.Column} Column object.
1302 * @param row {number} Row index.
1303 * @param col {number} Column index.
1306 YAHOO.widget.DataTable.prototype._initThEl = function(elTheadCell,oColumn,row,col) {
1307 // Clear out the cell of prior content
1308 // TODO: purgeListeners and other validation-related things
1309 var index = this._nIndex;
1310 var colId = oColumn.getId();
1311 elTheadCell.yuiColumnId = colId;
1313 elTheadCell.abbr = oColumn.abbr;
1316 elTheadCell.style.width = oColumn.width;
1320 if(YAHOO.lang.isString(oColumn.className)) {
1321 aCustomClasses = [oColumn.className];
1323 else if(YAHOO.lang.isArray(oColumn.className)) {
1324 aCustomClasses = oColumn.className;
1326 if(aCustomClasses) {
1327 for(var i=0; i<aCustomClasses.length; i++) {
1328 YAHOO.util.Dom.addClass(elTheadCell,aCustomClasses[i]);
1332 YAHOO.util.Dom.addClass(elTheadCell, "yui-dt-col-"+oColumn.key);
1334 elTheadCell.innerHTML = "";
1335 elTheadCell.rowSpan = oColumn.getRowspan();
1336 elTheadCell.colSpan = oColumn.getColspan();
1338 var elTheadContainer = elTheadCell.appendChild(document.createElement("div"));
1339 elTheadContainer.id = this.id + "-container" + colId;
1340 YAHOO.util.Dom.addClass(elTheadContainer,YAHOO.widget.DataTable.CLASS_HEADER);
1341 var elTheadLabel = elTheadContainer.appendChild(document.createElement("span"));
1342 elTheadLabel.id = this.id + "-label" + colId;
1343 YAHOO.util.Dom.addClass(elTheadLabel,YAHOO.widget.DataTable.CLASS_LABEL);
1345 var sLabel = YAHOO.lang.isValue(oColumn.label) ? oColumn.label : oColumn.key;
1346 if(oColumn.sortable) {
1347 YAHOO.util.Dom.addClass(elTheadCell,YAHOO.widget.DataTable.CLASS_SORTABLE);
1348 //TODO: Make sortLink customizeable
1349 //TODO: Make title configurable
1350 //TODO: Separate label from an accessibility link that says
1351 // "Click to sort ascending" and push it offscreen
1352 var sLabelLinkId = this.id + "-labellink" + colId;
1353 var sortLink = "?key=" + oColumn.key;
1354 elTheadLabel.innerHTML = "<a id=\"" + sLabelLinkId + "\" href=\"" + sortLink + "\" title=\"Click to sort\" class=\"" + YAHOO.widget.DataTable.CLASS_SORTABLE + "\">" + sLabel + "</a>";
1355 if(!this._sFirstLabelLinkId) {
1356 this._sFirstLabelLinkId = sLabelLinkId;
1360 elTheadLabel.innerHTML = sLabel;
1365 * Creates HTML markup for Cell Editor.
1367 * @method _initCellEditorEl
1370 YAHOO.widget.DataTable.prototype._initCellEditorEl = function() {
1371 // Attach Cell Editor container element to body
1372 var elCellEditor = document.createElement("div");
1373 elCellEditor.id = this.id + "-celleditor";
1374 elCellEditor.style.display = "none";
1375 YAHOO.util.Dom.addClass(elCellEditor, YAHOO.widget.DataTable.CLASS_EDITOR);
1376 elCellEditor = document.body.appendChild(elCellEditor);
1378 // Internal tracker of Cell Editor values
1379 var oCellEditor = {};
1380 oCellEditor.container = elCellEditor;
1381 oCellEditor.value = null;
1382 oCellEditor.isActive = false;
1383 this._oCellEditor = oCellEditor;
1386 this.subscribe("editorKeydownEvent", function(oArgs) {
1387 var e = oArgs.event;
1388 var elTarget = YAHOO.util.Event.getTarget(e);
1390 // ESC hides Cell Editor
1391 if((e.keyCode == 27)) {
1392 this.cancelCellEditor();
1398 * Initializes Column sorting.
1400 * @method _initColumnSort
1403 YAHOO.widget.DataTable.prototype._initColumnSort = function() {
1404 this.subscribe("headerCellClickEvent", this.onEventSortColumn);
1408 * Initializes DOM event listeners.
1410 * @method _initDomEvents
1413 YAHOO.widget.DataTable.prototype._initDomEvents = function() {
1414 var elTable = this._elTable;
1415 var elThead = this._elThead;
1416 var elTbody = this._elTbody;
1417 var elContainer = this._elContainer;
1419 YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this);
1420 YAHOO.util.Event.addListener(document, "keydown", this._onDocumentKeydown, this);
1422 YAHOO.util.Event.addListener(elTable, "focus", this._onTableFocus, this);
1423 YAHOO.util.Event.addListener(elTable, "mouseover", this._onTableMouseover, this);
1424 YAHOO.util.Event.addListener(elTable, "mouseout", this._onTableMouseout, this);
1425 YAHOO.util.Event.addListener(elTable, "mousedown", this._onTableMousedown, this);
1426 YAHOO.util.Event.addListener(elTable, "keydown", this._onTableKeydown, this);
1427 YAHOO.util.Event.addListener(elTable, "keypress", this._onTableKeypress, this);
1429 // Since we can't listen for click and dblclick on the same element...
1430 YAHOO.util.Event.addListener(elTable, "dblclick", this._onTableDblclick, this);
1431 YAHOO.util.Event.addListener(elThead, "click", this._onTheadClick, this);
1432 YAHOO.util.Event.addListener(elTbody, "click", this._onTbodyClick, this);
1434 YAHOO.util.Event.addListener(elContainer, "scroll", this._onScroll, this); // for IE
1435 YAHOO.util.Event.addListener(elTbody, "scroll", this._onScroll, this); // for everyone else
1474 // DOM MUTATION FUNCTIONS
1480 * Adds a TR element to the primary TBODY at the page row index if given, otherwise
1481 * at the end of the page. Formats TD elements within the TR element using data
1482 * from the given Record.
1485 * @param oRecord {YAHOO.widget.Record} Record instance.
1486 * @param index {Number} (optional) The page row index at which to add the TR
1488 * @return {String} ID of the added TR element, or null.
1491 YAHOO.widget.DataTable.prototype._addTrEl = function(oRecord, index) {
1492 this.hideTableMessage();
1494 // It's an append if no index provided, or index is negative or too big
1495 var append = (!YAHOO.lang.isNumber(index) || (index < 0) ||
1496 (index >= (this._elTbody.rows.length))) ? true : false;
1498 var oColumnSet = this._oColumnSet;
1499 var oRecordSet = this._oRecordSet;
1500 var isSortedBy = this.get("sortedBy");
1501 var sortedColKeyIndex = null;
1502 var sortedDir, newClass;
1504 sortedColKeyIndex = (isSortedBy.column) ?
1505 isSortedBy.column.getKeyIndex() :
1506 this._oColumnSet.getColumn(isSortedBy.key).getKeyIndex();
1507 sortedDir = isSortedBy.dir;
1508 newClass = (sortedDir === "desc") ? YAHOO.widget.DataTable.CLASS_DESC :
1509 YAHOO.widget.DataTable.CLASS_ASC;
1514 var elRow = (append) ? this._elTbody.appendChild(document.createElement("tr")) :
1515 this._elTbody.insertBefore(document.createElement("tr"),this._elTbody.rows[index]);
1517 elRow.id = this.id+"-bdrow"+this._nTrCount;
1519 elRow.yuiRecordId = oRecord.getId();
1522 for(var j=0; j<oColumnSet.keys.length; j++) {
1523 var oColumn = oColumnSet.keys[j];
1524 var elCell = elRow.appendChild(document.createElement("td"));
1525 elCell.id = elRow.id+"-cell"+j;
1526 elCell.yuiColumnId = oColumn.getId();
1527 elCell.headers = oColumnSet.headers[j];
1528 // For SF2 cellIndex bug: http://www.webreference.com/programming/javascript/ppk2/3.html
1529 elCell.yuiCellIndex = j;
1532 this.formatCell(elCell, oRecord, oColumn);
1534 // Set FIRST/LAST on TD
1536 YAHOO.util.Dom.addClass(elCell, YAHOO.widget.DataTable.CLASS_FIRST);
1538 else if (j === this._oColumnSet.keys.length-1) {
1539 YAHOO.util.Dom.addClass(elCell, YAHOO.widget.DataTable.CLASS_LAST);
1543 YAHOO.util.Dom.removeClass(elCell, YAHOO.widget.DataTable.CLASS_ASC);
1544 YAHOO.util.Dom.removeClass(elCell, YAHOO.widget.DataTable.CLASS_DESC);
1546 // Set ASC/DESC on TD
1547 if(j === sortedColKeyIndex) {
1548 newClass = (sortedDir === "desc") ?
1549 YAHOO.widget.DataTable.CLASS_DESC :
1550 YAHOO.widget.DataTable.CLASS_ASC;
1551 YAHOO.util.Dom.addClass(elCell, newClass);
1555 /*p.abx {word-wrap:break-word;}
1556 ought to solve the problem for Safari (the long words will wrap in your
1557 tds, instead of overflowing to the next td.
1558 (this is supported by IE win as well, so hide it if needed).
1560 One thing, though: it doesn't work in combination with
1561 'white-space:nowrap'.*/
1563 // need a div wrapper for safari?
1564 //TODO: fix fixedWidth
1565 if(this.fixedWidth) {
1566 elCell.style.overflow = "hidden";
1567 //elCell.style.width = "20px";
1575 * Formats all TD elements of given TR element with data from the given Record.
1577 * @method _updateTrEl
1578 * @param elRow {HTMLElement} The TR element to update.
1579 * @param oRecord {YAHOO.widget.Record} The associated Record instance.
1580 * @return {String} ID of the updated TR element, or null.
1583 YAHOO.widget.DataTable.prototype._updateTrEl = function(elRow, oRecord) {
1584 this.hideTableMessage();
1586 var isSortedBy = this.get("sortedBy");
1587 var sortedColKeyIndex = null;
1588 var sortedDir, newClass;
1590 sortedColKeyIndex = (isSortedBy.column) ?
1591 isSortedBy.column.getKeyIndex() :
1592 this._oColumnSet.getColumn(isSortedBy.key).getKeyIndex();
1593 sortedDir = isSortedBy.dir;
1594 newClass = (sortedDir === "desc") ? YAHOO.widget.DataTable.CLASS_DESC :
1595 YAHOO.widget.DataTable.CLASS_ASC;
1598 // Update TD elements with new data
1599 for(var j=0; j<elRow.cells.length; j++) {
1600 var oColumn = this._oColumnSet.keys[j];
1601 var elCell = elRow.cells[j];
1602 this.formatCell(elCell, oRecord, oColumn);
1605 YAHOO.util.Dom.removeClass(elCell, YAHOO.widget.DataTable.CLASS_ASC);
1606 YAHOO.util.Dom.removeClass(elCell, YAHOO.widget.DataTable.CLASS_DESC);
1608 // Set ASC/DESC on TD
1609 if(j === sortedColKeyIndex) {
1610 YAHOO.util.Dom.addClass(elCell, newClass);
1615 elRow.yuiRecordId = oRecord.getId();
1622 * Deletes TR element by DOM reference or by DataTable page row index.
1624 * @method _deleteTrEl
1625 * @param row {HTMLElement | Number} TR element reference or Datatable page row index.
1626 * @return {Boolean} Returns true if successful, else returns false.
1629 YAHOO.widget.DataTable.prototype._deleteTrEl = function(row) {
1632 // Get page row index for the element
1633 if(!YAHOO.lang.isNumber(row)) {
1634 rowIndex = YAHOO.util.Dom.get(row).sectionRowIndex;
1639 if(YAHOO.lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
1640 this._elTbody.deleteRow(rowIndex);
1674 // CSS/STATE FUNCTIONS
1680 * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element
1681 * of the DataTable page and updates internal tracker.
1683 * @method _setFirstRow
1686 YAHOO.widget.DataTable.prototype._setFirstRow = function() {
1687 var rowEl = this.getFirstTrEl();
1690 if(this._sFirstTrId) {
1691 YAHOO.util.Dom.removeClass(this._sFirstTrId, YAHOO.widget.DataTable.CLASS_FIRST);
1694 YAHOO.util.Dom.addClass(rowEl, YAHOO.widget.DataTable.CLASS_FIRST);
1695 this._sFirstTrId = rowEl.id;
1698 this._sFirstTrId = null;
1703 * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element
1704 * of the DataTable page and updates internal tracker.
1706 * @method _setLastRow
1709 YAHOO.widget.DataTable.prototype._setLastRow = function() {
1710 var rowEl = this.getLastTrEl();
1712 // Unassign previous class
1713 if(this._sLastTrId) {
1714 YAHOO.util.Dom.removeClass(this._sLastTrId, YAHOO.widget.DataTable.CLASS_LAST);
1717 YAHOO.util.Dom.addClass(rowEl, YAHOO.widget.DataTable.CLASS_LAST);
1718 this._sLastTrId = rowEl.id;
1721 this._sLastTrId = null;
1726 * Assigns the classes YAHOO.widget.DataTable.CLASS_EVEN and
1727 * YAHOO.widget.DataTable.CLASS_ODD to alternating TR elements of the DataTable
1728 * page. For performance, a subset of rows may be specified.
1730 * @method _setRowStripes
1731 * @param row {HTMLElement | String | Number} (optional) HTML TR element reference
1732 * or string ID, or page row index of where to start striping.
1733 * @param range {Number} (optional) If given, how many rows to stripe, otherwise
1734 * stripe all the rows until the end.
1737 YAHOO.widget.DataTable.prototype._setRowStripes = function(row, range) {
1738 // Default values stripe all rows
1739 var allRows = this._elTbody.rows;
1740 var nStartIndex = 0;
1741 var nEndIndex = allRows.length;
1744 if((row !== null) && (row !== undefined)) {
1745 // Validate given start row
1746 var elStartRow = this.getTrEl(row);
1748 nStartIndex = elStartRow.sectionRowIndex;
1750 // Validate given range
1751 if(YAHOO.lang.isNumber(range) && (range > 1)) {
1752 nEndIndex = nStartIndex + range;
1757 for(var i=nStartIndex; i<nEndIndex; i++) {
1759 YAHOO.util.Dom.removeClass(allRows[i], YAHOO.widget.DataTable.CLASS_EVEN);
1760 YAHOO.util.Dom.addClass(allRows[i], YAHOO.widget.DataTable.CLASS_ODD);
1763 YAHOO.util.Dom.removeClass(allRows[i], YAHOO.widget.DataTable.CLASS_ODD);
1764 YAHOO.util.Dom.addClass(allRows[i], YAHOO.widget.DataTable.CLASS_EVEN);
1813 /////////////////////////////////////////////////////////////////////////////
1815 // Private DOM Event Handlers
1817 /////////////////////////////////////////////////////////////////////////////
1820 * Handles scroll events on the CONTAINER (for IE) and TBODY elements (for everyone else).
1823 * @param e {HTMLEvent} The scroll event.
1824 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
1827 YAHOO.widget.DataTable.prototype._onScroll = function(e, oSelf) {
1828 var elTarget = YAHOO.util.Event.getTarget(e);
1829 var elTag = elTarget.tagName.toLowerCase();
1831 if(oSelf._oCellEditor.isActive) {
1832 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
1833 oSelf.cancelCellEditor();
1836 oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget});
1840 * Handles click events on the DOCUMENT.
1842 * @method _onDocumentClick
1843 * @param e {HTMLEvent} The click event.
1844 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
1847 YAHOO.widget.DataTable.prototype._onDocumentClick = function(e, oSelf) {
1848 var elTarget = YAHOO.util.Event.getTarget(e);
1849 var elTag = elTarget.tagName.toLowerCase();
1851 if(!YAHOO.util.Dom.isAncestor(oSelf._elTable, elTarget)) {
1852 oSelf.fireEvent("tableBlurEvent");
1854 // Fires editorBlurEvent when click is not within the TABLE.
1855 // For cases when click is within the TABLE, due to timing issues,
1856 // the editorBlurEvent needs to get fired by the lower-level DOM click
1857 // handlers below rather than by the TABLE click handler directly.
1858 if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
1859 // Only if the click was not within the Cell Editor container
1860 if(!YAHOO.util.Dom.isAncestor(oSelf._oCellEditor.container, elTarget) &&
1861 (oSelf._oCellEditor.container.id !== elTarget.id)) {
1862 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
1869 * Handles keydown events on the DOCUMENT.
1871 * @method _onDocumentKeydown
1872 * @param e {HTMLEvent} The keydown event.
1873 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
1876 YAHOO.widget.DataTable.prototype._onDocumentKeydown = function(e, oSelf) {
1877 var elTarget = YAHOO.util.Event.getTarget(e);
1878 var elTag = elTarget.tagName.toLowerCase();
1880 if(oSelf._oCellEditor && oSelf._oCellEditor.isActive &&
1881 YAHOO.util.Dom.isAncestor(oSelf._oCellEditor.container, elTarget)) {
1882 oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e});
1887 * Handles focus events on the TABLE element.
1889 * @method _onTableFocus
1890 * @param e {HTMLEvent} The focus event.
1891 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
1894 YAHOO.widget.DataTable.prototype._onTableMouseover = function(e, oSelf) {
1895 oSelf.fireEvent("tableFocusEvent");
1899 * Handles mouseover events on the TABLE element.
1901 * @method _onTableMouseover
1902 * @param e {HTMLEvent} The mouseover event.
1903 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
1906 YAHOO.widget.DataTable.prototype._onTableMouseover = function(e, oSelf) {
1907 var elTarget = YAHOO.util.Event.getTarget(e);
1908 var elTag = elTarget.tagName.toLowerCase();
1910 while(elTarget && (elTag != "table")) {
1917 oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
1920 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
1921 oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
1925 oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
1928 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
1929 oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
1932 oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
1938 elTarget = elTarget.parentNode;
1940 elTag = elTarget.tagName.toLowerCase();
1943 oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elTable),event:e});
1947 * Handles mouseout events on the TABLE element.
1949 * @method _onTableMouseout
1950 * @param e {HTMLEvent} The mouseout event.
1951 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
1954 YAHOO.widget.DataTable.prototype._onTableMouseout = function(e, oSelf) {
1955 var elTarget = YAHOO.util.Event.getTarget(e);
1956 var elTag = elTarget.tagName.toLowerCase();
1958 while(elTarget && (elTag != "table")) {
1965 oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
1968 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
1969 oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
1973 oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
1976 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
1977 oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
1980 oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
1986 elTarget = elTarget.parentNode;
1988 elTag = elTarget.tagName.toLowerCase();
1991 oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elTable),event:e});
1995 * Handles mousedown events on the TABLE element.
1997 * @method _onTableMousedown
1998 * @param e {HTMLEvent} The mousedown event.
1999 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2002 YAHOO.widget.DataTable.prototype._onTableMousedown = function(e, oSelf) {
2003 var elTarget = YAHOO.util.Event.getTarget(e);
2004 var elTag = elTarget.tagName.toLowerCase();
2006 while(elTarget && (elTag != "table")) {
2013 oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
2016 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
2017 oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
2021 oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
2024 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
2025 oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
2028 oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
2034 elTarget = elTarget.parentNode;
2036 elTag = elTarget.tagName.toLowerCase();
2039 oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elTable),event:e});
2043 * Handles dblclick events on the TABLE element.
2045 * @method _onTableDblclick
2046 * @param e {HTMLEvent} The dblclick event.
2047 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2050 YAHOO.widget.DataTable.prototype._onTableDblclick = function(e, oSelf) {
2051 var elTarget = YAHOO.util.Event.getTarget(e);
2052 var elTag = elTarget.tagName.toLowerCase();
2054 while(elTarget && (elTag != "table")) {
2059 oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
2062 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
2063 oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
2067 oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
2070 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
2071 oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
2074 oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
2080 elTarget = elTarget.parentNode;
2082 elTag = elTarget.tagName.toLowerCase();
2085 oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elTable),event:e});
2089 * Handles keydown events on the TABLE element. Handles arrow selection.
2091 * @method _onTableKeydown
2092 * @param e {HTMLEvent} The key event.
2093 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2096 YAHOO.widget.DataTable.prototype._onTableKeydown = function(e, oSelf) {
2097 var bSHIFT = e.shiftKey;
2098 var elTarget = YAHOO.util.Event.getTarget(e);
2100 // Ignore actions in the THEAD
2101 if(YAHOO.util.Dom.isAncestor(oSelf._elThead, elTarget)) {
2105 var nKey = YAHOO.util.Event.getCharCode(e);
2107 // TAB to first label link if any
2108 if(nKey === 9 && !bSHIFT && (elTarget.id === oSelf._elTable.id)) {
2109 if(oSelf._sFirstLabelLinkId) {
2110 YAHOO.util.Event.stopEvent(e);
2111 oSelf._focusEl(YAHOO.util.Dom.get(oSelf._sFirstLabelLinkId));
2116 // Something is currently selected
2117 var lastSelectedId = oSelf._sLastSelectedId;
2118 var lastSelectedEl = YAHOO.util.Dom.get(lastSelectedId);
2119 if(lastSelectedEl && oSelf.isSelected(lastSelectedEl)) {
2120 //TODO: handle tab, backspace, delete
2122 // Handle arrow selection
2123 if((nKey > 36) && (nKey < 41)) {
2124 YAHOO.util.Event.stopEvent(e);
2130 var sMode = oSelf.get("selectionMode");
2131 var allRows = oSelf._elTbody.rows;
2132 var anchorId = oSelf._sSelectionAnchorId;
2133 var anchorEl = YAHOO.util.Dom.get(anchorId);
2134 var newSelectedEl, trIndex, tdIndex, startIndex, endIndex, i, anchorPos;
2136 ////////////////////////////////////////////////////////////////////////
2138 // SHIFT cell block selection
2140 ////////////////////////////////////////////////////////////////////////
2141 if(bSHIFT && (sMode == "cellblock")) {
2142 trIndex = lastSelectedEl.parentNode.sectionRowIndex;
2143 tdIndex = lastSelectedEl.yuiCellIndex;
2147 // Is the anchor cell above, below, or same row
2148 if(anchorEl.parentNode.sectionRowIndex > trIndex) {
2151 else if(anchorEl.parentNode.sectionRowIndex < trIndex) {
2158 // Is the anchor cell left or right
2159 startIndex = Math.min(anchorEl.yuiCellIndex, tdIndex);
2160 endIndex = Math.max(anchorEl.yuiCellIndex, tdIndex);
2162 // Selecting away from anchor cell
2163 if(anchorPos <= 0) {
2164 // Select the horiz block on the next row
2165 if(trIndex < allRows.length-1) {
2166 for(i=startIndex; i<=endIndex; i++) {
2167 newSelectedEl = allRows[trIndex+1].cells[i];
2168 oSelf.selectCell(newSelectedEl);
2170 oSelf._sLastSelectedId = allRows[trIndex+1].cells[tdIndex].id;
2173 // Unselecting towards anchor cell
2175 // Unselect the horiz block on this row towards the next row
2176 for(i=startIndex; i<=endIndex; i++) {
2177 oSelf.unselectCell(allRows[trIndex].cells[i]);
2179 oSelf._sLastSelectedId = allRows[trIndex+1].cells[tdIndex].id;
2183 else if(nKey == 38) {
2184 // Is the anchor cell above, below, or same row
2185 if(anchorEl.parentNode.sectionRowIndex > trIndex) {
2188 else if(anchorEl.parentNode.sectionRowIndex < trIndex) {
2195 // Is the anchor cell left or right?
2196 startIndex = Math.min(anchorEl.yuiCellIndex, tdIndex);
2197 endIndex = Math.max(anchorEl.yuiCellIndex, tdIndex);
2199 // Selecting away from anchor cell
2200 if(anchorPos >= 0) {
2201 // Select the horiz block on the previous row
2203 for(i=startIndex; i<=endIndex; i++) {
2204 newSelectedEl = allRows[trIndex-1].cells[i];
2205 oSelf.selectCell(newSelectedEl);
2207 oSelf._sLastSelectedId = allRows[trIndex-1].cells[tdIndex].id;
2210 // Unselecting towards anchor cell
2212 // Unselect the horiz block on this row towards the previous row
2213 for(i=startIndex; i<=endIndex; i++) {
2214 oSelf.unselectCell(allRows[trIndex].cells[i]);
2216 oSelf._sLastSelectedId = allRows[trIndex-1].cells[tdIndex].id;
2220 else if(nKey == 39) {
2221 // Is the anchor cell left, right, or same column
2222 if(anchorEl.yuiCellIndex > tdIndex) {
2225 else if(anchorEl.yuiCellIndex < tdIndex) {
2232 // Selecting away from anchor cell
2233 if(anchorPos <= 0) {
2234 //Select the next vert block to the right
2235 if(tdIndex < allRows[trIndex].cells.length-1) {
2236 startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex);
2237 endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex);
2238 for(i=startIndex; i<=endIndex; i++) {
2239 newSelectedEl = allRows[i].cells[tdIndex+1];
2240 oSelf.selectCell(newSelectedEl);
2242 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id;
2245 // Unselecting towards anchor cell
2247 // Unselect the vert block on this column towards the right
2248 startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex);
2249 endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex);
2250 for(i=startIndex; i<=endIndex; i++) {
2251 oSelf.unselectCell(allRows[i].cells[tdIndex]);
2253 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id;
2257 else if(nKey == 37) {
2258 // Is the anchor cell left, right, or same column
2259 if(anchorEl.yuiCellIndex > tdIndex) {
2262 else if(anchorEl.yuiCellIndex < tdIndex) {
2269 // Selecting away from anchor cell
2270 if(anchorPos >= 0) {
2271 //Select the previous vert block to the left
2273 startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex);
2274 endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex);
2275 for(i=startIndex; i<=endIndex; i++) {
2276 newSelectedEl = allRows[i].cells[tdIndex-1];
2277 oSelf.selectCell(newSelectedEl);
2279 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id;
2282 // Unselecting towards anchor cell
2284 // Unselect the vert block on this column towards the left
2285 startIndex = Math.min(anchorEl.parentNode.sectionRowIndex, trIndex);
2286 endIndex = Math.max(anchorEl.parentNode.sectionRowIndex, trIndex);
2287 for(i=startIndex; i<=endIndex; i++) {
2288 oSelf.unselectCell(allRows[i].cells[tdIndex]);
2290 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id;
2294 ////////////////////////////////////////////////////////////////////////
2296 // SHIFT cell range selection
2298 ////////////////////////////////////////////////////////////////////////
2299 else if(bSHIFT && (sMode == "cellrange")) {
2300 trIndex = lastSelectedEl.parentNode.sectionRowIndex;
2301 tdIndex = lastSelectedEl.yuiCellIndex;
2303 // Is the anchor cell above, below, or same row
2304 if(anchorEl.parentNode.sectionRowIndex > trIndex) {
2307 else if(anchorEl.parentNode.sectionRowIndex < trIndex) {
2316 // Selecting away from anchor cell
2317 if(anchorPos <= 0) {
2318 // Select all cells to the end of this row
2319 for(i=tdIndex+1; i<allRows[trIndex].cells.length; i++){
2320 newSelectedEl = allRows[trIndex].cells[i];
2321 oSelf.selectCell(newSelectedEl);
2324 // Select some of the cells on the next row down
2325 if(trIndex < allRows.length-1) {
2326 for(i=0; i<=tdIndex; i++){
2327 newSelectedEl = allRows[trIndex+1].cells[i];
2328 oSelf.selectCell(newSelectedEl);
2332 // Unselecting towards anchor cell
2334 // Unselect all cells to the end of this row
2335 for(i=tdIndex; i<allRows[trIndex].cells.length; i++){
2336 oSelf.unselectCell(allRows[trIndex].cells[i]);
2339 // Unselect some of the cells on the next row down
2340 for(i=0; i<tdIndex; i++){
2341 oSelf.unselectCell(allRows[trIndex+1].cells[i]);
2343 oSelf._sLastSelectedId = allRows[trIndex+1].cells[tdIndex].id;
2347 else if(nKey == 38) {
2348 // Selecting away from anchor cell
2349 if(anchorPos >= 0) {
2350 // Select all the cells to the beginning of this row
2351 for(i=tdIndex-1; i>-1; i--){
2352 newSelectedEl = allRows[trIndex].cells[i];
2353 oSelf.selectCell(newSelectedEl);
2356 // Select some of the cells from the end of the previous row
2358 for(i=allRows[trIndex].cells.length-1; i>=tdIndex; i--){
2359 newSelectedEl = allRows[trIndex-1].cells[i];
2360 oSelf.selectCell(newSelectedEl);
2364 // Unselecting towards anchor cell
2366 // Unselect all the cells to the beginning of this row
2367 for(i=tdIndex; i>-1; i--){
2368 oSelf.unselectCell(allRows[trIndex].cells[i]);
2371 // Unselect some of the cells from the end of the previous row
2372 for(i=allRows[trIndex].cells.length-1; i>tdIndex; i--){
2373 oSelf.unselectCell(allRows[trIndex-1].cells[i]);
2375 oSelf._sLastSelectedId = allRows[trIndex-1].cells[tdIndex].id;
2379 else if(nKey == 39) {
2380 // Selecting away from anchor cell
2382 // Select the next cell to the right
2383 if(tdIndex < allRows[trIndex].cells.length-1) {
2384 newSelectedEl = allRows[trIndex].cells[tdIndex+1];
2385 oSelf.selectCell(newSelectedEl);
2387 // Select the first cell of the next row
2388 else if(trIndex < allRows.length-1) {
2389 newSelectedEl = allRows[trIndex+1].cells[0];
2390 oSelf.selectCell(newSelectedEl);
2393 // Unselecting towards anchor cell
2394 else if(anchorPos > 0) {
2395 oSelf.unselectCell(allRows[trIndex].cells[tdIndex]);
2397 // Unselect this cell towards the right
2398 if(tdIndex < allRows[trIndex].cells.length-1) {
2399 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id;
2401 // Unselect this cells towards the first cell of the next row
2403 oSelf._sLastSelectedId = allRows[trIndex+1].cells[0].id;
2406 // Anchor is on this row
2408 // Selecting away from anchor
2409 if(anchorEl.yuiCellIndex <= tdIndex) {
2410 // Select the next cell to the right
2411 if(tdIndex < allRows[trIndex].cells.length-1) {
2412 newSelectedEl = allRows[trIndex].cells[tdIndex+1];
2413 oSelf.selectCell(newSelectedEl);
2415 // Select the first cell on the next row
2416 else if(trIndex < allRows.length-1){
2417 newSelectedEl = allRows[trIndex+1].cells[0];
2418 oSelf.selectCell(newSelectedEl);
2421 // Unselecting towards anchor
2423 // Unselect this cell towards the right
2424 oSelf.unselectCell(allRows[trIndex].cells[tdIndex]);
2425 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id;
2430 else if(nKey == 37) {
2431 // Unselecting towards the anchor
2433 oSelf.unselectCell(allRows[trIndex].cells[tdIndex]);
2435 // Unselect this cell towards the left
2437 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id;
2439 // Unselect this cell towards the last cell of the previous row
2441 oSelf._sLastSelectedId = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1].id;
2444 // Selecting towards the anchor
2445 else if(anchorPos > 0) {
2446 // Select the next cell to the left
2448 newSelectedEl = allRows[trIndex].cells[tdIndex-1];
2449 oSelf.selectCell(newSelectedEl);
2451 // Select the last cell of the previous row
2452 else if(trIndex > 0){
2453 newSelectedEl = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1];
2454 oSelf.selectCell(newSelectedEl);
2457 // Anchor is on this row
2459 // Selecting away from anchor cell
2460 if(anchorEl.yuiCellIndex >= tdIndex) {
2461 // Select the next cell to the left
2463 newSelectedEl = allRows[trIndex].cells[tdIndex-1];
2464 oSelf.selectCell(newSelectedEl);
2466 // Select the last cell of the previous row
2467 else if(trIndex > 0){
2468 newSelectedEl = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1];
2469 oSelf.selectCell(newSelectedEl);
2472 // Unselecting towards anchor cell
2474 oSelf.unselectCell(allRows[trIndex].cells[tdIndex]);
2476 // Unselect this cell towards the left
2478 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id;
2480 // Unselect this cell towards the last cell of the previous row
2482 oSelf._sLastSelectedId = allRows[trIndex-1].cells[allRows[trIndex-1].cells.length-1].id;
2488 ////////////////////////////////////////////////////////////////////////
2490 // Simple single cell selection
2492 ////////////////////////////////////////////////////////////////////////
2493 else if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
2494 trIndex = lastSelectedEl.parentNode.sectionRowIndex;
2495 tdIndex = lastSelectedEl.yuiCellIndex;
2499 oSelf.unselectAllCells();
2501 // Select the next cell down
2502 if(trIndex < allRows.length-1) {
2503 newSelectedEl = allRows[trIndex+1].cells[tdIndex];
2504 oSelf.selectCell(newSelectedEl);
2506 // Select only the bottom cell
2508 newSelectedEl = lastSelectedEl;
2509 oSelf.selectCell(newSelectedEl);
2512 oSelf._sSelectionAnchorId = newSelectedEl.id;
2515 else if(nKey == 38) {
2516 oSelf.unselectAllCells();
2518 // Select the next cell up
2520 newSelectedEl = allRows[trIndex-1].cells[tdIndex];
2521 oSelf.selectCell(newSelectedEl);
2523 // Select only the top cell
2525 newSelectedEl = lastSelectedEl;
2526 oSelf.selectCell(newSelectedEl);
2529 oSelf._sSelectionAnchorId = newSelectedEl.id;
2532 else if(nKey == 39) {
2533 oSelf.unselectAllCells();
2535 // Select the next cell to the right
2536 if(tdIndex < lastSelectedEl.parentNode.cells.length-1) {
2537 newSelectedEl = lastSelectedEl.parentNode.cells[tdIndex+1];
2538 oSelf.selectCell(newSelectedEl);
2540 // Select only the right cell
2542 newSelectedEl = lastSelectedEl;
2543 oSelf.selectCell(newSelectedEl);
2546 oSelf._sSelectionAnchorId = newSelectedEl.id;
2549 else if(nKey == 37) {
2550 oSelf.unselectAllCells();
2552 // Select the next cell to the left
2554 newSelectedEl = lastSelectedEl.parentNode.cells[tdIndex-1];
2555 oSelf.selectCell(newSelectedEl);
2557 // Select only the left cell
2559 newSelectedEl = lastSelectedEl;
2560 oSelf.selectCell(newSelectedEl);
2563 oSelf._sSelectionAnchorId = newSelectedEl.id;
2566 ////////////////////////////////////////////////////////////////////////
2568 // SHIFT row selection
2570 ////////////////////////////////////////////////////////////////////////
2571 else if(bSHIFT && (sMode != "single")) {
2572 trIndex = lastSelectedEl.sectionRowIndex;
2574 if(anchorEl.sectionRowIndex > trIndex) {
2577 else if(anchorEl.sectionRowIndex < trIndex) {
2586 // Selecting away from anchor row
2587 if(anchorPos <= 0) {
2588 // Select the next row down
2589 if(trIndex < allRows.length-1) {
2590 oSelf.selectRow(trIndex+1);
2593 // Unselecting toward anchor row
2595 // Unselect this row towards the anchor row down
2596 oSelf.unselectRow(lastSelectedEl);
2597 oSelf._sLastSelectedId = allRows[trIndex+1].id;
2602 else if(nKey == 38) {
2603 // Selecting away from anchor row
2604 if(anchorPos >= 0) {
2605 // Select the next row up
2607 oSelf.selectRow(trIndex-1);
2610 // Unselect this row towards the anchor row up
2612 oSelf.unselectRow(lastSelectedEl);
2613 oSelf._sLastSelectedId = allRows[trIndex-1].id;
2617 else if(nKey == 39) {
2621 else if(nKey == 37) {
2625 ////////////////////////////////////////////////////////////////////////
2627 // Simple single row selection
2629 ////////////////////////////////////////////////////////////////////////
2631 trIndex = lastSelectedEl.sectionRowIndex;
2635 oSelf.unselectAllRows();
2637 // Select the next row
2638 if(trIndex < allRows.length-1) {
2639 newSelectedEl = allRows[trIndex+1];
2640 oSelf.selectRow(newSelectedEl);
2642 // Select only the last row
2644 newSelectedEl = lastSelectedEl;
2645 oSelf.selectRow(lastSelectedEl);
2648 oSelf._sSelectionAnchorId = newSelectedEl.id;
2651 else if(nKey == 38) {
2652 oSelf.unselectAllRows();
2654 // Select the previous row
2656 newSelectedEl = allRows[trIndex-1];
2657 oSelf.selectRow(newSelectedEl);
2659 // Select only the first row
2661 newSelectedEl = lastSelectedEl;
2662 oSelf.selectRow(newSelectedEl);
2665 oSelf._sSelectionAnchorId = newSelectedEl.id;
2668 else if(nKey == 39) {
2672 else if(nKey == 37) {
2680 * Handles keypress events on the TABLE. Mainly to support stopEvent on Mac.
2682 * @method _onTableKeypress
2683 * @param e {HTMLEvent} The key event.
2684 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2687 YAHOO.widget.DataTable.prototype._onTableKeypress = function(e, oSelf) {
2688 var isMac = (navigator.userAgent.toLowerCase().indexOf("mac") != -1);
2690 var nKey = YAHOO.util.Event.getCharCode(e);
2693 YAHOO.util.Event.stopEvent(e);
2696 else if(nKey == 38) {
2697 YAHOO.util.Event.stopEvent(e);
2703 * Handles click events on the THEAD element.
2705 * @method _onTheadClick
2706 * @param e {HTMLEvent} The click event.
2707 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2710 YAHOO.widget.DataTable.prototype._onTheadClick = function(e, oSelf) {
2711 var elTarget = YAHOO.util.Event.getTarget(e);
2712 var elTag = elTarget.tagName.toLowerCase();
2714 if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
2715 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
2718 while(elTarget && (elTag != "thead")) {
2723 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
2724 oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
2728 oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
2731 oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
2736 elTarget = elTarget.parentNode;
2738 elTag = elTarget.tagName.toLowerCase();
2741 oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elTable),event:e});
2745 * Handles click events on the primary TBODY element.
2747 * @method _onTbodyClick
2748 * @param e {HTMLEvent} The click event.
2749 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2752 YAHOO.widget.DataTable.prototype._onTbodyClick = function(e, oSelf) {
2753 var elTarget = YAHOO.util.Event.getTarget(e);
2754 var elTag = elTarget.tagName.toLowerCase();
2756 if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
2757 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
2760 while(elTarget && (elTag != "table")) {
2765 if(elTarget.type.toLowerCase() == "checkbox") {
2766 oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e});
2768 else if(elTarget.type.toLowerCase() == "radio") {
2769 oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e});
2773 oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
2776 oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
2779 oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
2782 oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
2787 elTarget = elTarget.parentNode;
2789 elTag = elTarget.tagName.toLowerCase();
2792 oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elTable),event:e});
2796 * Handles keyup events on the TBODY. Executes deletion.
2798 * @method _onTbodyKeyup
2799 * @param e {HTMLEvent} The key event.
2800 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2803 /*YAHOO.widget.DataTable.prototype._onTbodyKeyup = function(e, oSelf) {
2804 var nKey = YAHOO.util.Event.getCharCode(e);
2806 if(nKey == 46) {//TODO: if something is selected
2812 * Handles click events on paginator links.
2814 * @method _onPaginatorLinkClick
2815 * @param e {HTMLEvent} The click event.
2816 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2819 YAHOO.widget.DataTable.prototype._onPaginatorLinkClick = function(e, oSelf) {
2820 var elTarget = YAHOO.util.Event.getTarget(e);
2821 var elTag = elTarget.tagName.toLowerCase();
2823 if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
2824 oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
2827 while(elTarget && (elTag != "table")) {
2832 YAHOO.util.Event.stopEvent(e);
2833 //TODO: after the showPage call, figure out which link
2834 //TODO: was clicked and reset focus to the new version of it
2835 switch(elTarget.className) {
2836 case YAHOO.widget.DataTable.CLASS_PAGE:
2837 oSelf.showPage(parseInt(elTarget.innerHTML,10));
2839 case YAHOO.widget.DataTable.CLASS_FIRST:
2842 case YAHOO.widget.DataTable.CLASS_LAST:
2843 oSelf.showPage(oSelf.get("paginator").totalPages);
2845 case YAHOO.widget.DataTable.CLASS_PREVIOUS:
2846 oSelf.showPage(oSelf.get("paginator").currentPage - 1);
2848 case YAHOO.widget.DataTable.CLASS_NEXT:
2849 oSelf.showPage(oSelf.get("paginator").currentPage + 1);
2856 elTarget = elTarget.parentNode;
2858 elTag = elTarget.tagName.toLowerCase();
2867 * Handles change events on paginator SELECT element.
2869 * @method _onPaginatorDropdownChange
2870 * @param e {HTMLEvent} The change event.
2871 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2874 YAHOO.widget.DataTable.prototype._onPaginatorDropdownChange = function(e, oSelf) {
2875 var elTarget = YAHOO.util.Event.getTarget(e);
2876 var newValue = elTarget[elTarget.selectedIndex].value;
2878 var newRowsPerPage = YAHOO.lang.isValue(parseInt(newValue,10)) ? parseInt(newValue,10) : null;
2879 if(newRowsPerPage !== null) {
2880 var newStartRecordIndex = (oSelf.get("paginator").currentPage-1) * newRowsPerPage;
2881 oSelf.updatePaginator({rowsPerPage:newRowsPerPage, startRecordIndex:newStartRecordIndex});
2882 oSelf.refreshView();
2889 * Handles change events on SELECT elements within DataTable.
2891 * @method _onDropdownChange
2892 * @param e {HTMLEvent} The change event.
2893 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
2896 YAHOO.widget.DataTable.prototype._onDropdownChange = function(e, oSelf) {
2897 var elTarget = YAHOO.util.Event.getTarget(e);
2898 //TODO: pass what args?
2899 //var value = elTarget[elTarget.selectedIndex].value;
2900 oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget});
2941 /////////////////////////////////////////////////////////////////////////////
2943 // Public member variables
2945 /////////////////////////////////////////////////////////////////////////////
2948 /////////////////////////////////////////////////////////////////////////////
2952 /////////////////////////////////////////////////////////////////////////////
2957 * Public accessor to the unique name of the DataSource instance.
2960 * @return {String} Unique name of the DataSource instance.
2963 YAHOO.widget.DataTable.prototype.toString = function() {
2964 return "DataTable " + this._sName;
2968 * Returns the DataTable instance's DataSource instance.
2970 * @method getDataSource
2971 * @return {YAHOO.util.DataSource} DataSource instance.
2973 YAHOO.widget.DataTable.prototype.getDataSource = function() {
2974 return this._oDataSource;
2978 * Returns the DataTable instance's ColumnSet instance.
2980 * @method getColumnSet
2981 * @return {YAHOO.widget.ColumnSet} ColumnSet instance.
2983 YAHOO.widget.DataTable.prototype.getColumnSet = function() {
2984 return this._oColumnSet;
2988 * Returns the DataTable instance's RecordSet instance.
2990 * @method getRecordSet
2991 * @return {YAHOO.widget.RecordSet} RecordSet instance.
2993 YAHOO.widget.DataTable.prototype.getRecordSet = function() {
2994 return this._oRecordSet;
2998 * Returns the DataTable instance's Cell Editor as an object literal with the
2999 * following properties:
3002 * <dd>Cell element being edited</dd>
3005 * <dd>Associated Column instance</dd>
3007 * <dt>container</dt>
3008 * <dd>Reference to editor's container DIV element</dd>
3011 * <dd>True if cell is currently being edited</dd>
3014 * <dd>Associated Record instance</dd>
3016 * <dt>validator</dt>
3017 * <dd>Associated validator function</dd>
3020 * <dd>Current input value</dd>
3028 * @method getCellEditor
3029 * @return {Object} Cell Editor object literal values.
3031 YAHOO.widget.DataTable.prototype.getCellEditor = function() {
3032 return this._oCellEditor;
3080 * Returns DOM reference to the DataTable's TABLE element.
3082 * @method getTableEl
3083 * @return {HTMLElement} Reference to TABLE element.
3085 YAHOO.widget.DataTable.prototype.getTableEl = function() {
3086 return this._elTable;
3090 * Returns DOM reference to the DataTable's THEAD element.
3092 * @method getTheadEl
3093 * @return {HTMLElement} Reference to THEAD element.
3095 YAHOO.widget.DataTable.prototype.getTheadEl = function() {
3096 return this._elThead;
3100 * Returns DOM reference to the DataTable's primary TBODY element.
3102 * @method getTbodyEl
3103 * @return {HTMLElement} Reference to TBODY element.
3105 YAHOO.widget.DataTable.prototype.getTbodyEl = function() {
3106 return this._elTbody;
3108 // Backward compatibility
3109 YAHOO.widget.DataTable.prototype.getBody = function() {
3110 return this.getTbodyEl();
3114 * Returns DOM reference to the DataTable's secondary TBODY element that is
3115 * used to display messages.
3117 * @method getMsgTbodyEl
3118 * @return {HTMLElement} Reference to TBODY element.
3120 YAHOO.widget.DataTable.prototype.getMsgTbodyEl = function() {
3121 return this._elMsgTbody;
3125 * Returns DOM reference to the TD element within the secondary TBODY that is
3126 * used to display messages.
3128 * @method getMsgTdEl
3129 * @return {HTMLElement} Reference to TD element.
3131 YAHOO.widget.DataTable.prototype.getMsgTdEl = function() {
3132 return this._elMsgTd;
3136 * Returns the corresponding TR reference for a given DOM element, ID string or
3137 * directly page row index. If the given identifier is a child of a TR element,
3138 * then DOM tree is traversed until a parent TR element is returned, otherwise
3142 * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to
3143 * get: by element reference, ID string, page row index, or Record.
3144 * @return {HTMLElement} Reference to TR element, or null.
3146 YAHOO.widget.DataTable.prototype.getTrEl = function(row) {
3147 var allRows = this._elTbody.rows;
3150 if(row instanceof YAHOO.widget.Record) {
3151 var nTrIndex = this.getTrIndex(row);
3152 return allRows[nTrIndex];
3154 // By page row index
3155 else if(YAHOO.lang.isNumber(row) && (row > -1) && (row < allRows.length)) {
3156 return allRows[row];
3158 // By ID string or element reference
3161 var el = YAHOO.util.Dom.get(row);
3163 // Validate HTML element
3164 if(el && (el.ownerDocument == document)) {
3165 // Validate TR element
3166 if(el.tagName.toLowerCase() != "tr") {
3167 // Traverse up the DOM to find the corresponding TR element
3168 elRow = YAHOO.util.Dom.getAncestorByTagName(el,"tr");
3174 // Make sure the TR is in this TBODY
3175 if(elRow && (elRow.parentNode == this._elTbody)) {
3176 // Now we can return the TR element
3184 // Backward compatibility
3185 YAHOO.widget.DataTable.prototype.getRow = function(index) {
3186 return this.getTrEl(index);
3190 * Returns DOM reference to the first TR element in the DataTable page, or null.
3192 * @method getFirstTrEl
3193 * @return {HTMLElement} Reference to TR element.
3195 YAHOO.widget.DataTable.prototype.getFirstTrEl = function() {
3196 return this._elTbody.rows[0] || null;
3200 * Returns DOM reference to the last TR element in the DataTable page, or null.
3202 * @method getLastTrEl
3203 * @return {HTMLElement} Reference to last TR element.
3205 YAHOO.widget.DataTable.prototype.getLastTrEl = function() {
3206 var allRows = this._elTbody.rows;
3207 if(allRows.length > 0) {
3208 return allRows[allRows.length-1] || null;
3213 * Returns DOM reference to the given TD element.
3216 * @param cell {HTMLElement | String} DOM element reference or string ID.
3217 * @return {HTMLElement} Reference to TD element.
3219 YAHOO.widget.DataTable.prototype.getTdEl = function(cell) {
3221 var el = YAHOO.util.Dom.get(cell);
3223 // Validate HTML element
3224 if(el && (el.ownerDocument == document)) {
3225 // Validate TD element
3226 if(el.tagName.toLowerCase() != "td") {
3227 // Traverse up the DOM to find the corresponding TR element
3228 elCell = YAHOO.util.Dom.getAncestorByTagName(el, "td");
3234 // Make sure the TD is in this TBODY
3235 if(elCell && (elCell.parentNode.parentNode == this._elTbody)) {
3236 // Now we can return the TD element
3245 * Returns DOM reference to the TH element at given DataTable page coordinates, or null.
3248 * @param header {HTMLElement | String | YAHOO.widget.Column} DOM element
3249 * reference or string ID, or Column instance.
3250 * @return {HTMLElement} Reference to TH element.
3252 YAHOO.widget.DataTable.prototype.getThEl = function(header) {
3255 // Validate Column instance
3256 if(header instanceof YAHOO.widget.Column) {
3257 var oColumn = header;
3258 elHeader = YAHOO.util.Dom.get(this.id + "-col" + oColumn.getId());
3263 // Validate HTML element
3265 var el = YAHOO.util.Dom.get(header);
3267 if(el && (el.ownerDocument == document)) {
3268 // Validate TH element
3269 if(el.tagName.toLowerCase() != "th") {
3270 // Traverse up the DOM to find the corresponding TR element
3271 elHeader = YAHOO.util.Dom.getAncestorByTagName(el,"th");
3277 // Make sure the TH is in this THEAD
3278 if(elHeader && (elHeader.parentNode.parentNode == this._elThead)) {
3279 // Now we can return the TD element
3289 * Returns the page row index of given row. Returns null if the row is not in
3290 * view on the current DataTable page.
3292 * @method getTrIndex
3293 * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID
3294 * string reference to an element within the DataTable page, a Record instance,
3295 * or a Record's RecordSet index.
3296 * @return {Number} Page row index, or null if row does not exist or is not in view.
3298 YAHOO.widget.DataTable.prototype.getTrIndex = function(row) {
3302 if(row instanceof YAHOO.widget.Record) {
3303 nRecordIndex = this._oRecordSet.getRecordIndex(row);
3305 // Calculate page row index from Record index
3306 else if(YAHOO.lang.isNumber(row)) {
3309 if(YAHOO.lang.isNumber(nRecordIndex)) {
3310 // DataTable is paginated
3311 if(this.get("paginated")) {
3312 // Get the first and last Record on this page
3313 var startRecordIndex = this.get("paginator").startRecordIndex;
3314 var endRecordIndex = startRecordIndex + this.get("paginator").rowsPerPage - 1;
3315 // This Record is in view
3316 if((nRecordIndex >= startRecordIndex) && (nRecordIndex <= endRecordIndex)) {
3317 return nRecordIndex - startRecordIndex;
3319 // This Record is not in view
3324 // Not paginated, just return the Record index
3326 return nRecordIndex;
3330 // By element reference or ID string
3332 // Validate TR element
3333 elRow = this.getTrEl(row);
3334 if(elRow && (elRow.ownerDocument == document) &&
3335 (elRow.parentNode == this._elTbody)) {
3336 return elRow.sectionRowIndex;
3391 * Resets a RecordSet with the given data and populates the page view
3392 * with the new data. Any previous data and selection states are cleared.
3393 * However, sort states are not cleared, so if the given data is in a particular
3394 * sort order, implementers should take care to reset the sortedBy property. If
3395 * pagination is enabled, the currentPage is shown and Paginator UI updated,
3396 * otherwise all rows are displayed as a single page. For performance, existing
3397 * DOM elements are reused when possible.
3399 * @method initializeTable
3400 * @param oData {Object | Object[]} An object literal of data or an array of
3401 * object literals containing data.
3403 YAHOO.widget.DataTable.prototype.initializeTable = function(oData) {
3404 // Clear the RecordSet
3405 this._oRecordSet.reset();
3407 // Add data to RecordSet
3408 var records = this._oRecordSet.addRecords(oData);
3411 this._unselectAllTrEls();
3412 this._unselectAllTdEls();
3413 this._aSelections = null;
3414 this._sLastSelectedId = null;
3415 this._sSelectionAnchorId = null;
3419 this.fireEvent("initEvent");
3423 * Refreshes the view with existing Records from the RecordSet while
3424 * maintaining sort, pagination, and selection states. For performance, reuses
3425 * existing DOM elements when possible while deleting extraneous elements.
3427 * @method refreshView
3429 YAHOO.widget.DataTable.prototype.refreshView = function() {
3430 var i, j, k, l, aRecords;
3431 var oPaginator = this.updatePaginator();
3433 // Paginator is enabled, show a subset of Records and update Paginator UI
3434 if(this.get("paginated")) {
3435 var rowsPerPage = oPaginator.rowsPerPage;
3436 var startRecordIndex = (oPaginator.currentPage - 1) * rowsPerPage;
3437 aRecords = this._oRecordSet.getRecords(startRecordIndex, rowsPerPage);
3438 this.formatPaginators();
3442 aRecords = this._oRecordSet.getRecords();
3445 var elTbody = this._elTbody;
3446 var elRows = elTbody.rows;
3449 if(YAHOO.lang.isArray(aRecords) && (aRecords.length > 0)) {
3450 this.hideTableMessage();
3452 // Keep track of selected rows
3453 var aSelectedRows = this.getSelectedRows();
3454 // Keep track of selected cells
3455 var aSelectedCells = this.getSelectedCells();
3456 // Anything to reinstate?
3457 var bReselect = (aSelectedRows.length>0) || (aSelectedCells.length > 0);
3459 // Remove extra rows from the bottom so as to preserve ID order
3460 while(elTbody.hasChildNodes() && (elRows.length > aRecords.length)) {
3461 elTbody.deleteRow(-1);
3464 // Unselect all TR and TD elements in the UI
3466 this._unselectAllTrEls();
3467 this._unselectAllTdEls();
3470 // From the top, update in-place existing rows
3471 for(i=0; i<elRows.length; i++) {
3472 this._updateTrEl(elRows[i], aRecords[i]);
3475 // Add TR elements as necessary
3476 for(i=elRows.length; i<aRecords.length; i++) {
3477 this._addTrEl(aRecords[i]);
3480 // Reinstate selected and sorted classes
3481 var allRows = elTbody.rows;
3483 // Loop over each row
3484 for(j=0; j<allRows.length; j++) {
3485 var thisRow = allRows[j];
3486 var sMode = this.get("selectionMode");
3487 if ((sMode == "standard") || (sMode == "single")) {
3489 for(k=0; k<aSelectedRows.length; k++) {
3490 if(aSelectedRows[k] === thisRow.yuiRecordId) {
3491 YAHOO.util.Dom.addClass(thisRow, YAHOO.widget.DataTable.CLASS_SELECTED);
3492 if(j === allRows.length-1) {
3493 this._sLastSelectedId = thisRow.id;
3494 this._sSelectionAnchorId = thisRow.id;
3500 // Loop over each cell
3501 for(k=0; k<thisRow.cells.length; k++) {
3502 var thisCell = thisRow.cells[k];
3504 for(l=0; l<aSelectedCells.length; l++) {
3505 if((aSelectedCells[l].recordId === thisRow.yuiRecordId) &&
3506 (aSelectedCells[l].columnId === thisCell.yuiColumnId)) {
3507 YAHOO.util.Dom.addClass(thisCell, YAHOO.widget.DataTable.CLASS_SELECTED);
3508 if(k === thisRow.cells.length-1) {
3509 this._sLastSelectedId = thisCell.id;
3510 this._sSelectionAnchorId = thisCell.id;
3519 // Set FIRST/LAST, EVEN/ODD
3520 this._setFirstRow();
3522 this._setRowStripes();
3524 this.fireEvent("refreshEvent");
3529 while(elTbody.hasChildNodes()) {
3530 elTbody.deleteRow(-1);
3533 this.showTableMessage(YAHOO.widget.DataTable.MSG_EMPTY, YAHOO.widget.DataTable.CLASS_EMPTY);
3538 * Nulls out the entire DataTable instance and related objects, removes attached
3539 * event listeners, and clears out DOM elements inside the container. After
3540 * calling this method, the instance reference should be expliclitly nulled by
3541 * implementer, as in myDataTable = null. Use with caution!
3545 YAHOO.widget.DataTable.prototype.destroy = function() {
3546 // Destroy Cell Editor
3547 YAHOO.util.Event.purgeElement(this._oCellEditor.container, true);
3548 document.body.removeChild(this._oCellEditor.container);
3550 var instanceName = this.toString();
3551 var elContainer = this._elContainer;
3553 // Unhook custom events
3554 this._oRecordSet.unsubscribeAll();
3555 this.unsubscribeAll();
3557 // Unhook DOM events
3558 YAHOO.util.Event.purgeElement(elContainer, true);
3560 // Remove DOM elements
3561 elContainer.innerHTML = "";
3564 for(var param in this) {
3565 if(this.hasOwnProperty(param)) {
3573 * Displays message within secondary TBODY.
3575 * @method showTableMessage
3576 * @param sHTML {String} (optional) Value for innerHTML.
3577 * @param sClassName {String} (optional) Classname.
3579 YAHOO.widget.DataTable.prototype.showTableMessage = function(sHTML, sClassName) {
3580 var elCell = this._elMsgTd;
3581 if(YAHOO.lang.isString(sHTML)) {
3582 elCell.innerHTML = sHTML;
3584 if(YAHOO.lang.isString(sClassName)) {
3585 YAHOO.util.Dom.addClass(elCell, sClassName);
3587 this._elMsgTbody.style.display = "";
3588 this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
3592 * Hides secondary TBODY.
3594 * @method hideTableMessage
3596 YAHOO.widget.DataTable.prototype.hideTableMessage = function() {
3597 if(this._elMsgTbody.style.display != "none") {
3598 this._elMsgTbody.style.display = "none";
3599 this.fireEvent("tableMsgHideEvent");
3604 * Brings focus to DataTable instance.
3608 YAHOO.widget.DataTable.prototype.focus = function() {
3609 this._focusEl(this._elTable);
3677 // RECORDSET FUNCTIONS
3680 * Returns Record index for given TR element or page row index.
3682 * @method getRecordIndex
3683 * @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR
3684 * element reference or page row index.
3685 * @return {Number} Record's RecordSet index, or null.
3687 YAHOO.widget.DataTable.prototype.getRecordIndex = function(row) {
3690 if(!YAHOO.lang.isNumber(row)) {
3692 if(row instanceof YAHOO.widget.Record) {
3693 return this._oRecordSet.getRecordIndex(row);
3695 // By element reference
3697 // Find the TR element
3698 var el = this.getTrEl(row);
3700 nTrIndex = el.sectionRowIndex;
3704 // By page row index
3709 if(YAHOO.lang.isNumber(nTrIndex)) {
3710 if(this.get("paginated")) {
3711 return this.get("paginator").startRecordIndex + nTrIndex;
3722 * For the given identifier, returns the associated Record instance.
3725 * @param row {HTMLElement | String | Number} RecordSet position index, DOM
3726 * reference or ID string to an element within the DataTable page.
3727 * @return {YAHOO.widget.Record} Record instance.
3729 YAHOO.widget.DataTable.prototype.getRecord = function(row) {
3730 var nRecordIndex = row;
3732 // By element reference or ID string
3733 if(!YAHOO.lang.isNumber(nRecordIndex)) {
3734 // Validate TR element
3735 var elRow = this.getTrEl(row);
3737 nRecordIndex = this.getRecordIndex(row);
3741 if(YAHOO.lang.isNumber(nRecordIndex)) {
3742 return this._oRecordSet.getRecord(nRecordIndex);
3796 * For the given identifier, returns the associated Column instance.
3799 * @param column {HTMLElement | String | Number} ColumnSet.keys position index, DOM
3800 * reference or ID string to an element within the DataTable page.
3801 * @return {YAHOO.widget.Column} Column instance.
3803 YAHOO.widget.DataTable.prototype.getColumn = function(column) {
3804 var nColumnIndex = column;
3806 // By element reference or ID string
3807 if(!YAHOO.lang.isNumber(nColumnIndex)) {
3808 // Validate TD element
3809 var elCell = this.getTdEl(column);
3811 nColumnIndex = elCell.yuiColumnId;
3813 // Validate TH element
3815 elCell = this.getThEl(column);
3817 nColumnIndex = elCell.yuiColumnId;
3823 if(YAHOO.lang.isNumber(nColumnIndex)) {
3824 return this._oColumnSet.getColumn(nColumnIndex);
3831 * Sorts given Column.
3833 * @method sortColumn
3834 * @param oColumn {YAHOO.widget.Column} Column instance.
3836 YAHOO.widget.DataTable.prototype.sortColumn = function(oColumn) {
3840 if(!oColumn instanceof YAHOO.widget.Column) {
3841 //TODO: accept the TH or TH.key
3842 //TODO: Get the column based on TH.yuiColumnId
3845 if(oColumn.sortable) {
3846 // What is the default sort direction?
3847 var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultOrder) ? oColumn.sortOptions.defaultOrder : "asc";
3850 var oSortedBy = this.get("sortedBy");
3851 if(oSortedBy && (oSortedBy.key === oColumn.key)) {
3853 sortDir = (oSortedBy.dir == "asc") ? "desc" : "asc";
3856 sortDir = (sortDir == "asc") ? "desc" : "asc";
3860 // Is there a custom sort handler function defined?
3861 var sortFnc = (oColumn.sortOptions && YAHOO.lang.isFunction(oColumn.sortOptions.sortFunction)) ?
3862 oColumn.sortOptions.sortFunction : function(a, b, desc) {
3863 var sorted = YAHOO.util.Sort.compare(a.getData(oColumn.key),b.getData(oColumn.key), desc);
3865 return YAHOO.util.Sort.compare(a.getId(),b.getId(), desc);
3872 // Do the actual sort
3873 var desc = (sortDir == "desc") ? true : false;
3874 this._oRecordSet.sortRecords(sortFnc, desc);
3876 // Update sortedBy tracker
3877 this.set("sortedBy", {key:oColumn.key, dir:sortDir, column:oColumn});
3879 // Reset to first page
3880 //TODO: Keep selection in view
3881 this.updatePaginator({currentPage:1});
3886 this.fireEvent("columnSortEvent",{column:oColumn,dir:sortDir});
3941 * Adds one new Record of data into the RecordSet at the index if given,
3942 * otherwise at the end. If the new Record is in page view, the
3943 * corresponding DOM elements are also updated.
3946 * @param oData {Object} Object literal of data for the row.
3947 * @param index {Number} (optional) RecordSet position index at which to add data.
3949 YAHOO.widget.DataTable.prototype.addRow = function(oData, index) {
3950 if(oData && (oData.constructor == Object)) {
3951 var oRecord = this._oRecordSet.addRecord(oData, index);
3953 var nTrIndex = this.getTrIndex(oRecord);
3956 if(YAHOO.lang.isNumber(nTrIndex)) {
3957 // Paginated so just refresh the view to keep pagination state
3958 if(this.get("paginated")) {
3961 // Add the TR element
3963 var newTrId = this._addTrEl(oRecord, nTrIndex);
3965 // Is this an insert or an append?
3966 var append = (YAHOO.lang.isNumber(nTrIndex) &&
3967 (nTrIndex == this._elTbody.rows.length-1)) ? true : false;
3969 // Stripe the one new row
3971 if((this._elTbody.rows.length-1)%2) {
3972 YAHOO.util.Dom.addClass(newTrId, YAHOO.widget.DataTable.CLASS_ODD);
3975 YAHOO.util.Dom.addClass(newTrId, YAHOO.widget.DataTable.CLASS_EVEN);
3978 // Restripe all the rows after the new one
3980 this._setRowStripes(nTrIndex);
3983 // If new row is at the bottom
3987 // If new row is at the top
3988 else if(YAHOO.lang.isNumber(index) && (nTrIndex === 0)) {
3989 this._setFirstRow();
3994 // Record is not in view so just update pagination UI
3996 this.updatePaginator();
3999 // TODO: what args to pass?
4000 this.fireEvent("rowAddEvent", {record:oRecord});
4003 nTrIndex = (YAHOO.lang.isValue(nTrIndex))? nTrIndex : "n/a";
4011 * Convenience method to add multiple rows.
4014 * @param aData {Object[]} Array of object literal data for the rows.
4015 * @param index {Number} (optional) RecordSet position index at which to add data.
4017 YAHOO.widget.DataTable.prototype.addRows = function(aData, index) {
4018 if(YAHOO.lang.isArray(aData)) {
4020 if(YAHOO.lang.isNumber(index)) {
4021 for(i=aData.length-1; i>-1; i--) {
4022 this.addRow(aData[i], index);
4026 for(i=0; i<aData.length; i++) {
4027 this.addRow(aData[i]);
4036 * For the given row, updates the associated Record with the given data. If the
4037 * row is in view, the corresponding DOM elements are also updated.
4040 * @param row {YAHOO.widget.Record | Number | HTMLElement | String}
4041 * Which row to update: By Record instance, by Record's RecordSet
4042 * position index, by HTMLElement reference to the TR element, or by ID string
4043 * of the TR element.
4044 * @param oData {Object} Object literal of data for the row.
4046 YAHOO.widget.DataTable.prototype.updateRow = function(row, oData) {
4047 var oldRecord, updatedRecord, elRow;
4049 // Get the Record directly
4050 if((row instanceof YAHOO.widget.Record) || (YAHOO.lang.isNumber(row))) {
4051 // Get the Record directly
4052 oldRecord = this._oRecordSet.getRecord(row);
4054 // Is this row in view?
4055 elRow = this.getTrEl(oldRecord);
4057 // Get the Record by TR element
4059 elRow = this.getTrEl(row);
4061 oldRecord = this._oRecordSet.getRecord(this.getRecordIndex(elRow));
4065 // Update the Record
4067 // Copy data from the Record for the event that gets fired later
4068 var oRecordData = oldRecord.getData();
4070 for(var param in oRecordData) {
4071 oldData[param] = oRecordData[param];
4074 updatedRecord = this._oRecordSet.updateRecord(oldRecord, oData);
4081 // Update the TR only if row is in view
4083 this._updateTrEl(elRow, updatedRecord);
4086 this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData});
4090 * Deletes the given row's Record from the RecordSet. If the row is in view, the
4091 * corresponding DOM elements are also deleted.
4094 * @param row {HTMLElement | String | Number} DOM element reference or ID string
4095 * to DataTable page element or RecordSet index.
4097 YAHOO.widget.DataTable.prototype.deleteRow = function(row) {
4098 // Get the Record index...
4099 var nRecordIndex = null;
4100 // ...by Record index
4101 if(YAHOO.lang.isNumber(row)) {
4104 // ...by element reference
4106 var elRow = YAHOO.util.Dom.get(row);
4107 elRow = this.getTrEl(elRow);
4109 nRecordIndex = this.getRecordIndex(elRow);
4112 if(nRecordIndex !== null) {
4113 var oRecord = this._oRecordSet.getRecord(nRecordIndex);
4115 var nRecordId = oRecord.getId();
4117 // Remove from selection tracker if there
4118 var tracker = this._aSelections || [];
4119 for(var j=0; j<tracker.length; j++) {
4120 if((YAHOO.lang.isNumber(tracker[j]) && (tracker[j] === nRecordId)) ||
4121 ((tracker[j].constructor == Object) && (tracker[j].recordId === nRecordId))) {
4122 tracker.splice(j,1);
4126 // Copy data from the Record for the event that gets fired later
4127 var oRecordData = oRecord.getData();
4129 for(var param in oRecordData) {
4130 oData[param] = oRecordData[param];
4133 // Delete Record from RecordSet
4134 this._oRecordSet.deleteRecord(nRecordIndex);
4136 // If row is in view, delete the TR element
4137 var nTrIndex = this.getTrIndex(nRecordIndex);
4138 if(YAHOO.lang.isNumber(nTrIndex)) {
4139 var isLast = (nTrIndex == this.getLastTrEl().sectionRowIndex) ?
4141 this._deleteTrEl(nTrIndex);
4144 if(this._elTbody.rows.length === 0) {
4145 this.showTableMessage(YAHOO.widget.DataTable.MSG_EMPTY, YAHOO.widget.DataTable.CLASS_EMPTY);
4150 if(nTrIndex === 0) {
4151 this._setFirstRow();
4157 if(nTrIndex != this._elTbody.rows.length) {
4158 this._setRowStripes(nTrIndex);
4163 this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,
4164 oldData:oData, trElIndex:nTrIndex});
4172 * Convenience method to delete multiple rows.
4174 * @method deleteRows
4175 * @param row {HTMLElement | String | Number} DOM element reference or ID string
4176 * to DataTable page element or RecordSet index.
4177 * @param count {Number} (optional) How many rows to delete. A negative value
4178 * will delete towards the beginning.
4180 YAHOO.widget.DataTable.prototype.deleteRows = function(row, count) {
4181 // Get the Record index...
4182 var nRecordIndex = null;
4183 // ...by Record index
4184 if(YAHOO.lang.isNumber(row)) {
4187 // ...by element reference
4189 var elRow = YAHOO.util.Dom.get(row);
4190 elRow = this.getTrEl(elRow);
4192 nRecordIndex = this.getRecordIndex(elRow);
4195 if(nRecordIndex !== null) {
4196 if(count && YAHOO.lang.isNumber(count)) {
4197 // Start with highest index and work down
4198 var startIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex;
4199 var endIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1;
4200 for(var i=startIndex; i>endIndex-1; i--) {
4205 this.deleteRow(nRecordIndex);
4260 * Outputs markup into the given TD based on given Record.
4262 * @method formatCell
4263 * @param elCell {HTMLElement} TD Element.
4264 * @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
4265 * @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
4266 * @return {HTML} Markup.
4268 YAHOO.widget.DataTable.prototype.formatCell = function(elCell, oRecord, oColumn) {
4269 if(!(oRecord instanceof YAHOO.widget.Record)) {
4270 oRecord = this.getRecord(elCell);
4272 if(!(oColumn instanceof YAHOO.widget.Column)) {
4273 oColumn = this._oColumnSet.getColumn(elCell.yuiColumnId);
4276 if(oRecord && oColumn) {
4277 var oData = oRecord.getData(oColumn.key);
4280 if(YAHOO.lang.isString(oColumn.formatter)) {
4281 switch(oColumn.formatter) {
4283 fnFormatter = YAHOO.widget.DataTable.formatButton;
4286 fnFormatter = YAHOO.widget.DataTable.formatCheckbox;
4289 fnFormatter = YAHOO.widget.DataTable.formatCurrency;
4292 fnFormatter = YAHOO.widget.DataTable.formatDate;
4295 fnFormatter = YAHOO.widget.DataTable.formatDropdown;
4298 fnFormatter = YAHOO.widget.DataTable.formatEmail;
4301 fnFormatter = YAHOO.widget.DataTable.formatLink;
4304 fnFormatter = YAHOO.widget.DataTable.formatNumber;
4307 fnFormatter = YAHOO.widget.DataTable.formatRadio;
4310 fnFormatter = YAHOO.widget.DataTable.formatText;
4313 fnFormatter = YAHOO.widget.DataTable.formatTextarea;
4316 fnFormatter = YAHOO.widget.DataTable.formatTextbox;
4319 // This is the default
4325 else if(YAHOO.lang.isFunction(oColumn.formatter)) {
4326 fnFormatter = oColumn.formatter;
4329 // Apply special formatter
4331 fnFormatter.call(this, elCell, oRecord, oColumn, oData);
4334 elCell.innerHTML = (YAHOO.lang.isValue(oData)) ? oData.toString() : "";
4337 // Add custom classNames
4338 var aCustomClasses = null;
4339 if(YAHOO.lang.isString(oColumn.className)) {
4340 aCustomClasses = [oColumn.className];
4342 else if(YAHOO.lang.isArray(oColumn.className)) {
4343 aCustomClasses = oColumn.className;
4345 if(aCustomClasses) {
4346 for(var i=0; i<aCustomClasses.length; i++) {
4347 YAHOO.util.Dom.addClass(elCell, aCustomClasses[i]);
4351 YAHOO.util.Dom.addClass(elCell, "yui-dt-col-"+oColumn.key);
4354 if(oColumn.editor) {
4355 YAHOO.util.Dom.addClass(elCell,YAHOO.widget.DataTable.CLASS_EDITABLE);
4358 this.fireEvent("cellFormatEvent", {record:oRecord, key:oColumn.key, el:elCell});
4366 * Formats a BUTTON element.
4368 * @method DataTable.formatButton
4369 * @param el {HTMLElement} The element to format with markup.
4370 * @param oRecord {YAHOO.widget.Record} Record instance.
4371 * @param oColumn {YAHOO.widget.Column} Column instance.
4372 * @param oData {Object | Boolean} Data value for the cell. By default, the value
4373 * is what gets written to the BUTTON.
4376 YAHOO.widget.DataTable.formatButton= function(el, oRecord, oColumn, oData) {
4377 var sValue = YAHOO.lang.isValue(oData) ? oData : "Click";
4378 //TODO: support YAHOO.widget.Button
4379 //if(YAHOO.widget.Button) {
4383 el.innerHTML = "<button type=\"button\" class=\""+
4384 YAHOO.widget.DataTable.CLASS_BUTTON + "\">" + sValue + "</button>";
4389 * Formats a CHECKBOX element.
4391 * @method DataTable.formatCheckbox
4392 * @param el {HTMLElement} The element to format with markup.
4393 * @param oRecord {YAHOO.widget.Record} Record instance.
4394 * @param oColumn {YAHOO.widget.Column} Column instance.
4395 * @param oData {Object | Boolean} Data value for the cell. Can be a simple
4396 * Boolean to indicate whether checkbox is checked or not. Can be object literal
4397 * {checked:bBoolean, label:sLabel}. Other forms of oData require a custom
4401 YAHOO.widget.DataTable.formatCheckbox = function(el, oRecord, oColumn, oData) {
4402 var bChecked = oData;
4403 bChecked = (bChecked) ? " checked" : "";
4404 el.innerHTML = "<input type=\"checkbox\"" + bChecked +
4405 " class=\"" + YAHOO.widget.DataTable.CLASS_CHECKBOX + "\">";
4409 * Formats currency. Default unit is USD.
4411 * @method DataTable.formatCurrency
4412 * @param el {HTMLElement} The element to format with markup.
4413 * @param oRecord {YAHOO.widget.Record} Record instance.
4414 * @param oColumn {YAHOO.widget.Column} Column instance.
4415 * @param oData {Number} Data value for the cell.
4418 YAHOO.widget.DataTable.formatCurrency = function(el, oRecord, oColumn, oData) {
4419 if(YAHOO.lang.isNumber(oData)) {
4420 var nAmount = oData;
4423 // Round to the penny
4424 nAmount = Math.round(nAmount*100)/100;
4426 // Default currency is USD
4427 markup = "$"+nAmount;
4430 var dotIndex = markup.indexOf(".");
4435 while(dotIndex > markup.length-3) {
4439 el.innerHTML = markup;
4442 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4447 * Formats JavaScript Dates.
4449 * @method DataTable.formatDate
4450 * @param el {HTMLElement} The element to format with markup.
4451 * @param oRecord {YAHOO.widget.Record} Record instance.
4452 * @param oColumn {YAHOO.widget.Column} Column instance.
4453 * @param oData {Object} Data value for the cell, or null.
4456 YAHOO.widget.DataTable.formatDate = function(el, oRecord, oColumn, oData) {
4458 if(oDate instanceof Date) {
4459 el.innerHTML = (oDate.getMonth()+1) + "/" + oDate.getDate() + "/" + oDate.getFullYear();
4462 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4467 * Formats SELECT elements.
4469 * @method DataTable.formatDropdown
4470 * @param el {HTMLElement} The element to format with markup.
4471 * @param oRecord {YAHOO.widget.Record} Record instance.
4472 * @param oColumn {YAHOO.widget.Column} Column instance.
4473 * @param oData {Object} Data value for the cell, or null.
4476 YAHOO.widget.DataTable.formatDropdown = function(el, oRecord, oColumn, oData) {
4477 var selectedValue = (YAHOO.lang.isValue(oData)) ? oData : oRecord.getData(oColumn.key);
4478 var options = (YAHOO.lang.isArray(oColumn.dropdownOptions)) ?
4479 oColumn.dropdownOptions : null;
4482 var collection = el.getElementsByTagName("select");
4484 // Create the form element only once, so we can attach the onChange listener
4485 if(collection.length === 0) {
4486 // Create SELECT element
4487 selectEl = document.createElement("select");
4488 YAHOO.util.Dom.addClass(selectEl, YAHOO.widget.DataTable.CLASS_DROPDOWN);
4489 selectEl = el.appendChild(selectEl);
4491 // Add event listener
4492 //TODO: static method doesn't have access to the datatable instance...
4493 YAHOO.util.Event.addListener(selectEl,"change",oDataTable._onDropdownChange,oDataTable);
4496 selectEl = collection[0];
4498 // Update the form element
4500 // Clear out previous options
4501 selectEl.innerHTML = "";
4503 // We have options to populate
4505 // Create OPTION elements
4506 for(var i=0; i<options.length; i++) {
4507 var option = options[i];
4508 var optionEl = document.createElement("option");
4509 optionEl.value = (YAHOO.lang.isValue(option.value)) ?
4510 option.value : option;
4511 optionEl.innerHTML = (YAHOO.lang.isValue(option.text)) ?
4512 option.text : option;
4513 optionEl = selectEl.appendChild(optionEl);
4516 // Selected value is our only option
4518 selectEl.innerHTML = "<option value=\"" + selectedValue + "\">" + selectedValue + "</option>";
4522 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4529 * @method DataTable.formatEmail
4530 * @param el {HTMLElement} The element to format with markup.
4531 * @param oRecord {YAHOO.widget.Record} Record instance.
4532 * @param oColumn {YAHOO.widget.Column} Column instance.
4533 * @param oData {Object} Data value for the cell, or null.
4536 YAHOO.widget.DataTable.formatEmail = function(el, oRecord, oColumn, oData) {
4537 if(YAHOO.lang.isString(oData)) {
4538 el.innerHTML = "<a href=\"mailto:" + oData + "\">" + oData + "</a>";
4541 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4548 * @method DataTable.formatLink
4549 * @param el {HTMLElement} The element to format with markup.
4550 * @param oRecord {YAHOO.widget.Record} Record instance.
4551 * @param oColumn {YAHOO.widget.Column} Column instance.
4552 * @param oData {Object} Data value for the cell, or null.
4555 YAHOO.widget.DataTable.formatLink = function(el, oRecord, oColumn, oData) {
4556 if(YAHOO.lang.isString(oData)) {
4557 el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>";
4560 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4567 * @method DataTable.formatNumber
4568 * @param el {HTMLElement} The element to format with markup.
4569 * @param oRecord {YAHOO.widget.Record} Record instance.
4570 * @param oColumn {YAHOO.widget.Column} Column instance.
4571 * @param oData {Object} Data value for the cell, or null.
4574 YAHOO.widget.DataTable.formatNumber = function(el, oRecord, oColumn, oData) {
4575 if(YAHOO.lang.isNumber(oData)) {
4576 el.innerHTML = oData;
4579 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4584 * Formats INPUT TYPE=RADIO elements.
4586 * @method DataTable.formatRadio
4587 * @param el {HTMLElement} The element to format with markup.
4588 * @param oRecord {YAHOO.widget.Record} Record instance.
4589 * @param oColumn {YAHOO.widget.Column} Column instance.
4590 * @param oData {Object} (Optional) Data value for the cell.
4593 YAHOO.widget.DataTable.formatRadio = function(el, oRecord, oColumn, oData) {
4594 var bChecked = oData;
4595 bChecked = (bChecked) ? " checked" : "";
4596 el.innerHTML = "<input type=\"radio\"" + bChecked +
4597 " name=\"" + oColumn.getId() + "-radio\"" +
4598 " class=\"" + YAHOO.widget.DataTable.CLASS_CHECKBOX + "\">";
4602 * Formats text strings.
4604 * @method DataTable.formatText
4605 * @param el {HTMLElement} The element to format with markup.
4606 * @param oRecord {YAHOO.widget.Record} Record instance.
4607 * @param oColumn {YAHOO.widget.Column} Column instance.
4608 * @param oData {Object} (Optional) Data value for the cell.
4611 YAHOO.widget.DataTable.formatText = function(el, oRecord, oColumn, oData) {
4612 var value = (YAHOO.lang.isValue(oRecord.getData(oColumn.key))) ?
4613 oRecord.getData(oColumn.key) : "";
4614 //TODO: move to util function
4615 el.innerHTML = value.toString().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
4619 * Formats TEXTAREA elements.
4621 * @method DataTable.formatTextarea
4622 * @param el {HTMLElement} The element to format with markup.
4623 * @param oRecord {YAHOO.widget.Record} Record instance.
4624 * @param oColumn {YAHOO.widget.Column} Column instance.
4625 * @param oData {Object} (Optional) Data value for the cell.
4628 YAHOO.widget.DataTable.formatTextarea = function(el, oRecord, oColumn, oData) {
4629 var value = (YAHOO.lang.isValue(oRecord.getData(oColumn.key))) ?
4630 oRecord.getData(oColumn.key) : "";
4631 var markup = "<textarea>" + value + "</textarea>";
4632 el.innerHTML = markup;
4636 * Formats INPUT TYPE=TEXT elements.
4638 * @method DataTable.formatTextbox
4639 * @param el {HTMLElement} The element to format with markup.
4640 * @param oRecord {YAHOO.widget.Record} Record instance.
4641 * @param oColumn {YAHOO.widget.Column} Column instance.
4642 * @param oData {Object} (Optional) Data value for the cell.
4645 YAHOO.widget.DataTable.formatTextbox = function(el, oRecord, oColumn, oData) {
4646 var value = (YAHOO.lang.isValue(oRecord.getData(oColumn.key))) ?
4647 oRecord.getData(oColumn.key) : "";
4648 var markup = "<input type=\"text\" value=\"" + value + "\">";
4649 el.innerHTML = markup;
4702 * Updates Paginator values in response to RecordSet changes and/or DOM events.
4703 * Pass in all, a subset, or no values.
4705 * @method updatePaginator
4706 * @param oNewValues {Object} (Optional) Object literal of Paginator values, or
4707 * a subset of Paginator values.
4708 * @param {Object} Object literal of all Paginator values.
4711 YAHOO.widget.DataTable.prototype.updatePaginator = function(oNewValues) {
4713 var oValidPaginator = this.get("paginator");
4714 for(var param in oNewValues) {
4715 if(oValidPaginator.hasOwnProperty(param)) {
4716 oValidPaginator[param] = oNewValues[param];
4720 oValidPaginator.totalRecords = this._oRecordSet.getLength();
4721 oValidPaginator.rowsThisPage = Math.min(oValidPaginator.rowsPerPage, oValidPaginator.totalRecords);
4722 oValidPaginator.totalPages = Math.ceil(oValidPaginator.totalRecords / oValidPaginator.rowsThisPage);
4723 if(isNaN(oValidPaginator.totalPages)) {
4724 oValidPaginator.totalPages = 0;
4727 this.set("paginator", oValidPaginator);
4728 return this.get("paginator");
4732 * Displays given page of a paginated DataTable.
4735 * @param nPage {Number} Which page.
4737 YAHOO.widget.DataTable.prototype.showPage = function(nPage) {
4739 if(!YAHOO.lang.isNumber(nPage) || (nPage < 1) || (nPage > this.get("paginator").totalPages)) {
4742 this.updatePaginator({currentPage:nPage});
4747 * Updates Paginator containers with markup. Override this method to customize pagination UI.
4749 * @method formatPaginators
4751 YAHOO.widget.DataTable.prototype.formatPaginators = function() {
4752 var pag = this.get("paginator");
4754 // For Opera workaround
4755 var dropdownEnabled = false;
4757 // Links are enabled
4758 if(pag.pageLinks > -1) {
4759 for(var i=0; i<pag.links.length; i++) {
4760 this.formatPaginatorLinks(pag.links[i], pag.currentPage, pag.pageLinksStart, pag.pageLinks, pag.totalPages);
4764 // Dropdown is enabled
4765 for(i=0; i<pag.dropdowns.length; i++) {
4766 if(pag.dropdownOptions) {
4767 dropdownEnabled = true;
4768 this.formatPaginatorDropdown(pag.dropdowns[i], pag.dropdownOptions);
4771 pag.dropdowns[i].style.display = "none";
4775 // For Opera artifacting in dropdowns
4776 if(dropdownEnabled && navigator.userAgent.toLowerCase().indexOf("opera") != -1) {
4777 document.body.style += '';
4782 * Updates Paginator dropdown. If dropdown doesn't exist, the markup is created.
4783 * Sets dropdown elements's "selected" value.
4785 * @method formatPaginatorDropdown
4786 * @param elDropdown {HTMLElement} The SELECT element.
4787 * @param dropdownOptions {Object[]} OPTION values for display in the SELECT element.
4789 YAHOO.widget.DataTable.prototype.formatPaginatorDropdown = function(elDropdown, dropdownOptions) {
4790 if(elDropdown && (elDropdown.ownerDocument == document)) {
4791 // Clear OPTION elements
4792 while (elDropdown.firstChild) {
4793 elDropdown.removeChild(elDropdown.firstChild);
4796 // Create OPTION elements
4797 for(var j=0; j<dropdownOptions.length; j++) {
4798 var dropdownOption = dropdownOptions[j];
4799 var optionEl = document.createElement("option");
4800 optionEl.value = (YAHOO.lang.isValue(dropdownOption.value)) ?
4801 dropdownOption.value : dropdownOption;
4802 optionEl.innerHTML = (YAHOO.lang.isValue(dropdownOption.text)) ?
4803 dropdownOption.text : dropdownOption;
4804 optionEl = elDropdown.appendChild(optionEl);
4807 var options = elDropdown.options;
4808 // Update dropdown's "selected" value
4809 if(options.length) {
4810 for(var i=options.length-1; i>-1; i--) {
4811 if((this.get("paginator").rowsPerPage + "") === options[i].value) {
4812 options[i].selected = true;
4817 // Show the dropdown
4818 elDropdown.style.display = "";
4824 * Updates Paginator links container with markup.
4826 * @method formatPaginatorLinks
4827 * @param elContainer {HTMLElement} The link container element.
4828 * @param nCurrentPage {Number} Current page.
4829 * @param nPageLinksStart {Number} First page link to display.
4830 * @param nPageLinksLength {Number} How many page links to display.
4831 * @param nTotalPages {Number} Total number of pages.
4833 YAHOO.widget.DataTable.prototype.formatPaginatorLinks = function(elContainer, nCurrentPage, nPageLinksStart, nPageLinksLength, nTotalPages) {
4834 if(elContainer && (elContainer.ownerDocument == document) &&
4835 YAHOO.lang.isNumber(nCurrentPage) && YAHOO.lang.isNumber(nPageLinksStart) &&
4836 YAHOO.lang.isNumber(nTotalPages)) {
4837 // Set up markup for first/last/previous/next
4838 var bIsFirstPage = (nCurrentPage == 1) ? true : false;
4839 var bIsLastPage = (nCurrentPage == nTotalPages) ? true : false;
4840 var sFirstLinkMarkup = (bIsFirstPage) ?
4841 " <span class=\"" + YAHOO.widget.DataTable.CLASS_DISABLED +
4842 " " + YAHOO.widget.DataTable.CLASS_FIRST + "\"><<</span> " :
4843 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_FIRST + "\"><<</a> ";
4844 var sPrevLinkMarkup = (bIsFirstPage) ?
4845 " <span class=\"" + YAHOO.widget.DataTable.CLASS_DISABLED +
4846 " " + YAHOO.widget.DataTable.CLASS_PREVIOUS + "\"><</span> " :
4847 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_PREVIOUS + "\"><</a> " ;
4848 var sNextLinkMarkup = (bIsLastPage) ?
4849 " <span class=\"" + YAHOO.widget.DataTable.CLASS_DISABLED +
4850 " " + YAHOO.widget.DataTable.CLASS_NEXT + "\">></span> " :
4851 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_NEXT + "\">></a> " ;
4852 var sLastLinkMarkup = (bIsLastPage) ?
4853 " <span class=\"" + YAHOO.widget.DataTable.CLASS_DISABLED +
4854 " " + YAHOO.widget.DataTable.CLASS_LAST + "\">>></span> " :
4855 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_LAST + "\">>></a> ";
4857 // Start with first and previous
4858 var sMarkup = sFirstLinkMarkup + sPrevLinkMarkup;
4860 // Ok to show all links
4861 var nMaxLinks = nTotalPages;
4863 var nLastLink = nTotalPages;
4865 if(nPageLinksLength > 0) {
4866 // Calculate how many links to show
4867 nMaxLinks = (nPageLinksStart+nPageLinksLength < nTotalPages) ?
4868 nPageLinksStart+nPageLinksLength-1 : nTotalPages;
4870 // Try to keep the current page in the middle
4871 nFirstLink = (nCurrentPage - Math.floor(nMaxLinks/2) > 0) ? nCurrentPage - Math.floor(nMaxLinks/2) : 1;
4872 nLastLink = (nCurrentPage + Math.floor(nMaxLinks/2) <= nTotalPages) ? nCurrentPage + Math.floor(nMaxLinks/2) : nTotalPages;
4874 // Keep the last link in range
4875 if(nFirstLink === 1) {
4876 nLastLink = nMaxLinks;
4878 // Keep the first link in range
4879 else if(nLastLink === nTotalPages) {
4880 nFirstLink = nTotalPages - nMaxLinks + 1;
4883 // An even number of links can get funky
4884 if(nLastLink - nFirstLink === nMaxLinks) {
4889 // Generate markup for each page
4890 for(var i=nFirstLink; i<=nLastLink; i++) {
4891 if(i != nCurrentPage) {
4892 sMarkup += " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_PAGE + "\">" + i + "</a> ";
4895 sMarkup += " <span class=\"" + YAHOO.widget.DataTable.CLASS_SELECTED + "\">" + i + "</span>";
4898 sMarkup += sNextLinkMarkup + sLastLinkMarkup;
4899 elContainer.innerHTML = sMarkup;
4952 // SELECTION/HIGHLIGHTING
4955 * ID string of last highlighted cell element
4957 * @property _sLastHighlightedCellId
4961 YAHOO.widget.DataTable.prototype._sLastHighlightedCellId = null;
4964 * ID string of last highlighted row element
4966 * @property _sLastHighlightedRowId
4970 YAHOO.widget.DataTable.prototype._sLastHighlightedRowId = null;
4973 * Array of selections: {recordId:nRecordId, cellIndex:nCellIndex}
4975 * @property _aSelections
4979 YAHOO.widget.DataTable.prototype._aSelections = null;
4982 * ID string of last selected element
4984 * @property _sLastSelectedId
4988 YAHOO.widget.DataTable.prototype._sLastSelectedId = null;
4991 * ID string of the selection anchor element.
4993 * @property _sSelectionAnchorId
4997 YAHOO.widget.DataTable.prototype._sSelectionAnchorId = null;
5000 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
5001 * from all TR elements on the page.
5003 * @method _unselectAllTrEls
5006 YAHOO.widget.DataTable.prototype._unselectAllTrEls = function() {
5007 var selectedRows = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_SELECTED,"tr",this._elTbody);
5008 YAHOO.util.Dom.removeClass(selectedRows, YAHOO.widget.DataTable.CLASS_SELECTED);
5012 * Returns array of selected TR elements on the page.
5014 * @method getSelectedTrEls
5015 * @return {HTMLElement[]} Array of selected TR elements.
5017 YAHOO.widget.DataTable.prototype.getSelectedTrEls = function() {
5018 return YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_SELECTED,"tr",this._elTbody);
5022 * Sets given row to the selected state.
5025 * @param row {HTMLElement | String} HTML element reference or ID.
5027 YAHOO.widget.DataTable.prototype.selectRow = function(row) {
5029 var elRow = this.getTrEl(row);
5031 var oRecord = this.getRecord(elRow);
5034 var tracker = this._aSelections || [];
5035 var nRecordId = oRecord.getId();
5036 // Remove if already there
5038 // Use Array.indexOf if available...
5039 if(tracker.indexOf && (tracker.indexOf(nRecordId) > -1)) {
5040 tracker.splice(tracker.indexOf(nRecordId),1);
5042 // ...or do it the old-fashioned way
5044 for(var j=0; j<tracker.length; j++) {
5045 if(tracker[j] === nRecordId){
5046 tracker.splice(j,1);
5051 tracker.push(nRecordId);
5054 this._sLastSelectedId = elRow.id;
5055 if(!this._sSelectionAnchorId) {
5056 this._sSelectionAnchorId = elRow.id;
5058 this._aSelections = tracker;
5061 YAHOO.util.Dom.addClass(elRow, YAHOO.widget.DataTable.CLASS_SELECTED);
5063 this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
5069 // Backward compatibility
5070 YAHOO.widget.DataTable.prototype.select = function(els) {
5071 if(!YAHOO.lang.isArray(els)) {
5074 for(var i=0; i<els.length; i++) {
5075 this.selectRow(els[i]);
5080 * Sets given row to the unselected state.
5082 * @method unselectRow
5083 * @param row {HTMLElement | String} HTML TR element reference or ID.
5085 YAHOO.widget.DataTable.prototype.unselectRow = function(row) {
5087 var elRow = this.getTrEl(row);
5089 var oRecord = this.getRecord(elRow);
5092 var tracker = this._aSelections || [];
5093 var nRecordId = oRecord.getId();
5098 // Use Array.indexOf if available...
5099 if(tracker.indexOf && (tracker.indexOf(nRecordId) > -1)) {
5100 tracker.splice(tracker.indexOf(nRecordId),1);
5103 // ...or do it the old-fashioned way
5105 for(var j=0; j<tracker.length; j++) {
5106 if(tracker[j] === nRecordId){
5107 tracker.splice(j,1);
5115 this._aSelections = tracker;
5118 YAHOO.util.Dom.removeClass(elRow, YAHOO.widget.DataTable.CLASS_SELECTED);
5120 this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
5129 * Clears out all row selections.
5131 * @method unselectAllRows
5133 YAHOO.widget.DataTable.prototype.unselectAllRows = function() {
5134 // Remove from tracker
5135 var tracker = this._aSelections || [];
5136 for(var j=0; j<tracker.length; j++) {
5137 if(YAHOO.lang.isNumber(tracker[j])){
5138 tracker.splice(j,1);
5143 this._aSelections = tracker;
5146 this._unselectAllTrEls();
5148 //TODO: send an array of [{el:el,record:record}]
5149 //TODO: or convert this to an unselectRows method
5150 //TODO: that takes an array of rows or unselects all if none given
5151 this.fireEvent("unselectAllRowsEvent");
5155 * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
5156 * from all TD elements in the internal tracker.
5158 * @method _unselectAllTdEls
5161 YAHOO.widget.DataTable.prototype._unselectAllTdEls = function() {
5162 var selectedCells = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_SELECTED,"td",this._elTbody);
5163 YAHOO.util.Dom.removeClass(selectedCells, YAHOO.widget.DataTable.CLASS_SELECTED);
5167 * Returns array of selected TD elements on the page.
5169 * @method getSelectedTdEls
5170 * @return {HTMLElement[]} Array of selected TD elements.
5172 YAHOO.widget.DataTable.prototype.getSelectedTdEls = function() {
5173 return YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_SELECTED,"td",this._elTbody);
5177 * Sets given cell to the selected state.
5179 * @method selectCell
5180 * @param cell {HTMLElement | String} DOM element reference or ID string
5181 * to DataTable page element or RecordSet index.
5183 YAHOO.widget.DataTable.prototype.selectCell = function(cell) {
5184 var elCell = this.getTdEl(cell);
5187 var oRecord = this.getRecord(elCell);
5188 var nColumnId = elCell.yuiColumnId;
5190 if(oRecord && YAHOO.lang.isNumber(nColumnId)) {
5192 var tracker = this._aSelections || [];
5193 var nRecordId = oRecord.getId();
5196 for(var j=0; j<tracker.length; j++) {
5197 if((tracker[j].recordId === nRecordId) && (tracker[j].columnId === nColumnId)){
5198 tracker.splice(j,1);
5203 tracker.push({recordId:nRecordId, columnId:nColumnId});
5206 this._aSelections = tracker;
5207 this._sLastSelectedId = elCell.id;
5208 if(!this._sSelectionAnchorId) {
5209 this._sSelectionAnchorId = elCell.id;
5213 YAHOO.util.Dom.addClass(elCell, YAHOO.widget.DataTable.CLASS_SELECTED);
5215 this.fireEvent("cellSelectEvent", {record:oRecord,
5216 key: this._oColumnSet.getColumn(nColumnId).key, el:elCell});
5223 * Sets given cell to the unselected state.
5225 * @method unselectCell
5226 * @param cell {HTMLElement | String} DOM element reference or ID string
5227 * to DataTable page element or RecordSet index.
5229 YAHOO.widget.DataTable.prototype.unselectCell = function(cell) {
5230 var elCell = this.getTdEl(cell);
5233 var oRecord = this.getRecord(elCell);
5234 var nColumnId = elCell.yuiColumnId;
5236 if(oRecord && YAHOO.lang.isNumber(nColumnId)) {
5238 var tracker = this._aSelections || [];
5239 var id = oRecord.getId();
5242 for(var j=0; j<tracker.length; j++) {
5243 if((tracker[j].recordId === id) && (tracker[j].columnId === nColumnId)){
5244 // Remove from tracker
5245 tracker.splice(j,1);
5248 this._aSelections = tracker;
5251 YAHOO.util.Dom.removeClass(elCell, YAHOO.widget.DataTable.CLASS_SELECTED);
5253 this.fireEvent("cellUnselectEvent", {record:oRecord,
5254 key:this._oColumnSet.getColumn(nColumnId).key, el:elCell});
5264 * Clears out all cell selections.
5266 * @method unselectAllCells
5268 YAHOO.widget.DataTable.prototype.unselectAllCells= function() {
5269 // Remove from tracker
5270 var tracker = this._aSelections || [];
5271 for(var j=0; j<tracker.length; j++) {
5272 if(tracker[j].constructor == Object){
5273 tracker.splice(j,1);
5278 this._aSelections = tracker;
5281 this._unselectAllTdEls();
5284 //TODO: or fire individual cellUnselectEvent
5285 this.fireEvent("unselectAllCellsEvent");
5289 * Returns true if given TR or TD element is select, false otherwise.
5291 * @method isSelected
5292 * @param el {HTMLElement} HTML element reference or ID of a TR or TD.
5293 * @return {Boolean} True if element is selected.
5295 YAHOO.widget.DataTable.prototype.isSelected = function(el) {
5296 return YAHOO.util.Dom.hasClass(el,YAHOO.widget.DataTable.CLASS_SELECTED);
5300 * Returns selected rows as an array of Record IDs.
5302 * @method getSelectedRows
5303 * @return {HTMLElement[]} Array of selected rows by Record ID.
5305 YAHOO.widget.DataTable.prototype.getSelectedRows = function() {
5306 var aSelectedRows = [];
5307 var tracker = this._aSelections || [];
5308 for(var j=0; j<tracker.length; j++) {
5309 if(YAHOO.lang.isNumber(tracker[j])){
5310 aSelectedRows.push(tracker[j]);
5313 return aSelectedRows;
5317 * Returns selected cells as an array of object literals:
5318 * {recordId:nRecordID, columnId:nColumnId}.
5320 * @method getSelectedCells
5321 * @return {HTMLElement[]} Array of selected cells by Record and Column IDs.
5323 YAHOO.widget.DataTable.prototype.getSelectedCells = function() {
5324 var aSelectedCells = [];
5325 var tracker = this._aSelections || [];
5326 for(var j=0; j<tracker.length; j++) {
5327 if(tracker[j] && (tracker[j].constructor == Object)){
5328 aSelectedCells.push({recordId:tracker[j].recordId, columnId:tracker[j].columnId});
5331 return aSelectedCells;
5335 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row.
5337 * @method highlightRow
5338 * @param row {HTMLElement | String} DOM element reference or ID string.
5340 YAHOO.widget.DataTable.prototype.highlightRow = function(row) {
5341 var elRow = this.getTrEl(row);
5344 // Make sure previous row is unhighlighted
5345 if(this._sLastHighlightedRowId) {
5346 YAHOO.util.Dom.removeClass(this._sLastHighlightedRowId,YAHOO.widget.DataTable.CLASS_HIGHLIGHTED);
5348 var oRecord = this.getRecord(elRow);
5349 YAHOO.util.Dom.addClass(elRow,YAHOO.widget.DataTable.CLASS_HIGHLIGHTED);
5350 this._sLastHighlightedRowId = elRow.id;
5351 this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow});
5357 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row.
5359 * @method unhighlightRow
5360 * @param row {HTMLElement | String} DOM element reference or ID string.
5362 YAHOO.widget.DataTable.prototype.unhighlightRow = function(row) {
5363 var elRow = this.getTrEl(row);
5366 var oRecord = this.getRecord(elRow);
5367 YAHOO.util.Dom.removeClass(elRow,YAHOO.widget.DataTable.CLASS_HIGHLIGHTED);
5368 this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
5374 * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell.
5376 * @method highlightCell
5377 * @param cell {HTMLElement | String} DOM element reference or ID string.
5379 YAHOO.widget.DataTable.prototype.highlightCell = function(cell) {
5380 var elCell = this.getTdEl(cell);
5383 // Make sure previous cell is unhighlighted
5384 if(this._sLastHighlightedCellId) {
5385 YAHOO.util.Dom.removeClass(this._sLastHighlightedCellId,YAHOO.widget.DataTable.CLASS_HIGHLIGHTED);
5388 var oRecord = this.getRecord(elCell);
5389 YAHOO.util.Dom.addClass(elCell,YAHOO.widget.DataTable.CLASS_HIGHLIGHTED);
5390 this._sLastHighlightedCellId = elCell.id;
5391 this.fireEvent("cellHighlightEvent", {record:oRecord,
5392 key:this._oColumnSet.getColumn(elCell.yuiColumnId).key, el:elCell});
5398 * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell.
5400 * @method unhighlightCell
5401 * @param cell {HTMLElement | String} DOM element reference or ID string.
5403 YAHOO.widget.DataTable.prototype.unhighlightCell = function(cell) {
5404 var elCell = this.getTdEl(cell);
5407 var oRecord = this.getRecord(elCell);
5408 YAHOO.util.Dom.removeClass(elCell,YAHOO.widget.DataTable.CLASS_HIGHLIGHTED);
5409 this.fireEvent("cellUnhighlightEvent", {record:oRecord,
5410 key:this._oColumnSet.getColumn(elCell.yuiColumnId).key, el:elCell});
5461 /*TODO: for TAB handling
5462 * Shows Cell Editor for next cell.
5464 * @method editNextCell
5465 * @param elCell {HTMLElement} Cell element from which to edit next cell.
5467 //YAHOO.widget.DataTable.prototype.editNextCell = function(elCell) {
5471 * Shows Cell Editor for given cell.
5473 * @method showCellEditor
5474 * @param elCell {HTMLElement | String} Cell element to edit.
5475 * @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
5476 * @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
5478 YAHOO.widget.DataTable.prototype.showCellEditor = function(elCell, oRecord, oColumn) {
5479 elCell = YAHOO.util.Dom.get(elCell);
5481 if(elCell && (elCell.ownerDocument === document)) {
5482 if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) {
5483 oRecord = this.getRecord(elCell);
5485 if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) {
5486 oColumn = this.getColumn(elCell);
5488 if(oRecord && oColumn) {
5489 var oCellEditor = this._oCellEditor;
5491 // Clear previous Editor
5492 if(oCellEditor.isActive) {
5493 this.cancelCellEditor();
5496 // Editor not defined
5497 if(!oColumn.editor) {
5501 // Update Editor values
5502 oCellEditor.cell = elCell;
5503 oCellEditor.record = oRecord;
5504 oCellEditor.column = oColumn;
5505 oCellEditor.validator = (oColumn.editorOptions &&
5506 YAHOO.lang.isFunction(oColumn.editorOptions.validator)) ?
5507 oColumn.editorOptions.validator : null;
5508 oCellEditor.value = oRecord.getData(oColumn.key);
5511 var elContainer = oCellEditor.container;
5512 var x = YAHOO.util.Dom.getX(elCell);
5513 var y = YAHOO.util.Dom.getY(elCell);
5515 // SF doesn't get xy for cells in scrolling table
5516 // when tbody display is set to block
5517 if(isNaN(x) || isNaN(y)) {
5518 x = elCell.offsetLeft + // cell pos relative to table
5519 YAHOO.util.Dom.getX(this._elTable) - // plus table pos relative to document
5520 this._elTbody.scrollLeft; // minus tbody scroll
5521 y = elCell.offsetTop + // cell pos relative to table
5522 YAHOO.util.Dom.getY(this._elTable) - // plus table pos relative to document
5523 this._elTbody.scrollTop + // minus tbody scroll
5524 this._elThead.offsetHeight; // account for fixed headers
5527 elContainer.style.left = x + "px";
5528 elContainer.style.top = y + "px";
5531 elContainer.style.display = "";
5533 // Render Editor markup
5535 if(YAHOO.lang.isString(oColumn.editor)) {
5536 switch(oColumn.editor) {
5538 fnEditor = YAHOO.widget.DataTable.editCheckbox;
5541 fnEditor = YAHOO.widget.DataTable.editDate;
5544 fnEditor = YAHOO.widget.DataTable.editDropdown;
5547 fnEditor = YAHOO.widget.DataTable.editRadio;
5550 fnEditor = YAHOO.widget.DataTable.editTextarea;
5553 fnEditor = YAHOO.widget.DataTable.editTextbox;
5559 else if(YAHOO.lang.isFunction(oColumn.editor)) {
5560 fnEditor = oColumn.editor;
5564 // Create DOM input elements
5565 fnEditor(this._oCellEditor, this);
5567 // Show Save/Cancel buttons
5568 if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) {
5569 this.showCellEditorBtns(elContainer);
5572 // Hook to customize the UI
5573 this.doBeforeShowCellEditor(this._oCellEditor);
5575 oCellEditor.isActive = true;
5577 //TODO: verify which args to pass
5578 this.fireEvent("editorShowEvent", {editor:oCellEditor});
5586 * Overridable abstract method to customize Cell Editor UI.
5588 * @method doBeforeShowCellEditor
5589 * @param oCellEditor {Object} Cell Editor object literal.
5591 YAHOO.widget.DataTable.prototype.doBeforeShowCellEditor = function(oCellEditor) {
5595 * Adds Save/Cancel buttons to Cell Editor.
5597 * @method showCellEditorBtns
5598 * @param elContainer {HTMLElement} Cell Editor container.
5600 YAHOO.widget.DataTable.prototype.showCellEditorBtns = function(elContainer) {
5602 var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
5603 YAHOO.util.Dom.addClass(elBtnsDiv, YAHOO.widget.DataTable.CLASS_BUTTON);
5606 var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
5607 YAHOO.util.Dom.addClass(elSaveBtn, YAHOO.widget.DataTable.CLASS_DEFAULT);
5608 elSaveBtn.innerHTML = "OK";
5609 YAHOO.util.Event.addListener(elSaveBtn, "click", this.saveCellEditor, this, true);
5612 var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
5613 elCancelBtn.innerHTML = "Cancel";
5614 YAHOO.util.Event.addListener(elCancelBtn, "click", this.cancelCellEditor, this, true);
5618 * Clears Cell Editor of all state and UI.
5620 * @method resetCellEditor
5623 YAHOO.widget.DataTable.prototype.resetCellEditor = function() {
5624 var elContainer = this._oCellEditor.container;
5625 elContainer.style.display = "none";
5626 YAHOO.util.Event.purgeElement(elContainer, true);
5627 elContainer.innerHTML = "";
5628 this._oCellEditor.value = null;
5629 this._oCellEditor.isActive = false;
5633 * Saves Cell Editor input to Record.
5635 * @method saveCellEditor
5637 YAHOO.widget.DataTable.prototype.saveCellEditor = function() {
5638 //TODO: Copy the editor's values to pass to the event
5639 if(this._oCellEditor.isActive) {
5640 var newData = this._oCellEditor.value;
5641 var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key);
5643 // Validate input data
5644 if(this._oCellEditor.validator) {
5645 this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData);
5646 if(this._oCellEditor.value === null ) {
5647 this.resetCellEditor();
5648 this.fireEvent("editorRevertEvent",
5649 {editor:this._oCellEditor, oldData:oldData, newData:newData});
5654 // Update the Record
5655 this._oRecordSet.updateKey(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
5658 this.formatCell(this._oCellEditor.cell);
5660 // Clear out the Cell Editor
5661 this.resetCellEditor();
5663 this.fireEvent("editorSaveEvent",
5664 {editor:this._oCellEditor, oldData:oldData, newData:newData});
5671 * Cancels Cell Editor.
5673 * @method cancelCellEditor
5675 YAHOO.widget.DataTable.prototype.cancelCellEditor = function() {
5676 if(this._oCellEditor.isActive) {
5677 this.resetCellEditor();
5678 //TODO: preserve values for the event?
5679 this.fireEvent("editorCancelEvent", {editor:this._oCellEditor});
5686 * Enables CHECKBOX Editor.
5688 * @method editCheckbox
5690 //YAHOO.widget.DataTable.editCheckbox = function(elContainer, oRecord, oColumn, oEditor, oSelf) {
5691 YAHOO.widget.DataTable.editCheckbox = function(oEditor, oSelf) {
5692 var elCell = oEditor.cell;
5693 var oRecord = oEditor.record;
5694 var oColumn = oEditor.column;
5695 var elContainer = oEditor.container;
5696 var aCheckedValues = oRecord.getData(oColumn.key);
5697 if(!YAHOO.lang.isArray(aCheckedValues)) {
5698 aCheckedValues = [aCheckedValues];
5702 if(oColumn.editorOptions && YAHOO.lang.isArray(oColumn.editorOptions.checkboxOptions)) {
5703 var checkboxOptions = oColumn.editorOptions.checkboxOptions;
5704 var checkboxValue, checkboxId, elLabel, j, k;
5705 // First create the checkbox buttons in an IE-friendly way
5706 for(j=0; j<checkboxOptions.length; j++) {
5707 checkboxValue = YAHOO.lang.isValue(checkboxOptions[j].label) ?
5708 checkboxOptions[j].label : checkboxOptions[j];
5709 checkboxId = oSelf.id + "-editor-checkbox" + j;
5710 elContainer.innerHTML += "<input type=\"checkbox\"" +
5711 " name=\"" + oSelf.id + "-editor-checkbox\"" +
5712 " value=\"" + checkboxValue + "\"" +
5713 " id=\"" + checkboxId + "\">";
5714 // Then create the labels in an IE-friendly way
5715 elLabel = elContainer.appendChild(document.createElement("label"));
5716 elLabel.htmlFor = checkboxId;
5717 elLabel.innerHTML = checkboxValue;
5719 var aCheckboxEls = [];
5721 // Loop through checkboxes to check them
5722 for(j=0; j<checkboxOptions.length; j++) {
5723 checkboxEl = YAHOO.util.Dom.get(oSelf.id + "-editor-checkbox" + j);
5724 aCheckboxEls.push(checkboxEl);
5725 for(k=0; k<aCheckedValues.length; k++) {
5726 if(checkboxEl.value === aCheckedValues[k]) {
5727 checkboxEl.checked = true;
5730 // Focus the first checkbox
5732 oSelf._focusEl(checkboxEl);
5735 // Loop through checkboxes to assign click handlers
5736 for(j=0; j<checkboxOptions.length; j++) {
5737 checkboxEl = YAHOO.util.Dom.get(oSelf.id + "-editor-checkbox" + j);
5738 YAHOO.util.Event.addListener(checkboxEl, "click", function(){
5739 var aNewValues = [];
5740 for(var m=0; m<aCheckboxEls.length; m++) {
5741 if(aCheckboxEls[m].checked) {
5742 aNewValues.push(aCheckboxEls[m].value);
5745 oSelf._oCellEditor.value = aNewValues;
5746 oSelf.fireEvent("editorUpdateEvent",{editor:oSelf._oCellEditor});
5753 * Enables Date Editor.
5757 YAHOO.widget.DataTable.editDate = function(oEditor, oSelf) {
5758 var elCell = oEditor.cell;
5759 var oRecord = oEditor.record;
5760 var oColumn = oEditor.column;
5761 var elContainer = oEditor.container;
5762 var value = oRecord.getData(oColumn.key);
5765 if(YAHOO.widget.Calendar) {
5766 var selectedValue = (value.getMonth()+1)+"/"+value.getDate()+"/"+value.getFullYear();
5767 var calContainer = elContainer.appendChild(document.createElement("div"));
5768 calContainer.id = "yui-dt-" + oSelf._nIndex + "-col" + oColumn.getKeyIndex() + "-dateContainer";
5770 new YAHOO.widget.Calendar("yui-dt-" + oSelf._nIndex + "-col" + oColumn.getKeyIndex() + "-date",
5772 {selected:selectedValue, pagedate:value});
5774 calContainer.style.cssFloat = "none";
5776 //var calFloatClearer = elContainer.appendChild(document.createElement("br"));
5777 //calFloatClearer.style.clear = "both";
5779 calendar.selectEvent.subscribe(function(type, args, obj) {
5780 oSelf._oCellEditor.value = new Date(args[0][0][0], args[0][0][1]-1, args[0][0][2]);
5782 oSelf.fireEvent("editorUpdateEvent",{editor:oSelf._oCellEditor});
5790 * Enables SELECT Editor.
5792 * @method editDropdown
5794 YAHOO.widget.DataTable.editDropdown = function(oEditor, oSelf) {
5795 var elCell = oEditor.cell;
5796 var oRecord = oEditor.record;
5797 var oColumn = oEditor.column;
5798 var elContainer = oEditor.container;
5799 var value = oRecord.getData(oColumn.key);
5802 var elDropdown = elContainer.appendChild(document.createElement("select"));
5803 var dropdownOptions = (oColumn.editorOptions && YAHOO.lang.isArray(oColumn.editorOptions.dropdownOptions)) ?
5804 oColumn.editorOptions.dropdownOptions : [];
5805 for(var j=0; j<dropdownOptions.length; j++) {
5806 var dropdownOption = dropdownOptions[j];
5807 var elOption = document.createElement("option");
5808 elOption.value = (YAHOO.lang.isValue(dropdownOption.value)) ?
5809 dropdownOption.value : dropdownOption;
5810 elOption.innerHTML = (YAHOO.lang.isValue(dropdownOption.text)) ?
5811 dropdownOption.text : dropdownOption;
5812 elOption = elDropdown.appendChild(elOption);
5813 if(value === elDropdown.options[j].value) {
5814 elDropdown.options[j].selected = true;
5818 // Set up a listener on each check box to track the input value
5819 YAHOO.util.Event.addListener(elDropdown, "change",
5821 oSelf._oCellEditor.value = elDropdown[elDropdown.selectedIndex].value;
5822 oSelf.fireEvent("editorUpdateEvent",{editor:oSelf._oCellEditor});
5825 // Focus the dropdown
5826 oSelf._focusEl(elDropdown);
5830 * Enables INPUT TYPE=RADIO Editor.
5834 YAHOO.widget.DataTable.editRadio = function(oEditor, oSelf) {
5835 var elCell = oEditor.cell;
5836 var oRecord = oEditor.record;
5837 var oColumn = oEditor.column;
5838 var elContainer = oEditor.container;
5839 var value = oRecord.getData(oColumn.key);
5842 if(oColumn.editorOptions && YAHOO.lang.isArray(oColumn.editorOptions.radioOptions)) {
5843 var radioOptions = oColumn.editorOptions.radioOptions;
5844 var radioValue, radioId, elLabel, j;
5845 // First create the radio buttons in an IE-friendly way
5846 for(j=0; j<radioOptions.length; j++) {
5847 radioValue = YAHOO.lang.isValue(radioOptions[j].label) ?
5848 radioOptions[j].label : radioOptions[j];
5849 radioId = oSelf.id + "-editor-radio" + j;
5850 elContainer.innerHTML += "<input type=\"radio\"" +
5851 " name=\"" + oSelf.id + "-editor-radio\"" +
5852 " value=\"" + radioValue + "\"" +
5853 " id=\"" + radioId + "\">";
5854 // Then create the labels in an IE-friendly way
5855 elLabel = elContainer.appendChild(document.createElement("label"));
5856 elLabel.htmlFor = radioId;
5857 elLabel.innerHTML = radioValue;
5859 // Then check one, and assign click handlers
5860 for(j=0; j<radioOptions.length; j++) {
5861 var radioEl = YAHOO.util.Dom.get(oSelf.id + "-editor-radio" + j);
5862 if(value === radioEl.value) {
5863 radioEl.checked = true;
5864 oSelf._focusEl(radioEl);
5866 YAHOO.util.Event.addListener(radioEl, "click",
5868 oSelf._oCellEditor.value = this.value;
5869 oSelf.fireEvent("editorUpdateEvent",{editor:oSelf._oCellEditor});
5876 * Enables TEXTAREA Editor.
5878 * @method editTextarea
5880 YAHOO.widget.DataTable.editTextarea = function(oEditor, oSelf) {
5881 var elCell = oEditor.cell;
5882 var oRecord = oEditor.record;
5883 var oColumn = oEditor.column;
5884 var elContainer = oEditor.container;
5885 var value = oRecord.getData(oColumn.key);
5888 var elTextarea = elContainer.appendChild(document.createElement("textarea"));
5889 elTextarea.style.width = elCell.offsetWidth + "px"; //(parseInt(elCell.offsetWidth,10)) + "px";
5890 elTextarea.style.height = "3em"; //(parseInt(elCell.offsetHeight,10)) + "px";
5891 elTextarea.value = YAHOO.lang.isValue(value) ? value : "";
5893 // Set up a listener on each check box to track the input value
5894 YAHOO.util.Event.addListener(elTextarea, "keyup", function(){
5895 //TODO: set on a timeout
5896 oSelf._oCellEditor.value = elTextarea.value;
5897 oSelf.fireEvent("editorUpdateEvent",{editor:oSelf._oCellEditor});
5902 elTextarea.select();
5906 * Enables INPUT TYPE=TEXT Editor.
5908 * @method editTextbox
5910 YAHOO.widget.DataTable.editTextbox = function(oEditor, oSelf) {
5911 var elCell = oEditor.cell;
5912 var oRecord = oEditor.record;
5913 var oColumn = oEditor.column;
5914 var elContainer = oEditor.container;
5915 var value = YAHOO.lang.isValue(oRecord.getData(oColumn.key)) ? oRecord.getData(oColumn.key) : "";
5918 var elTextbox = elContainer.appendChild(document.createElement("input"));
5919 elTextbox.type = "text";
5920 elTextbox.style.width = elCell.offsetWidth + "px"; //(parseInt(elCell.offsetWidth,10)) + "px";
5921 //elTextbox.style.height = "1em"; //(parseInt(elCell.offsetHeight,10)) + "px";
5922 elTextbox.value = value;
5924 // Set up a listener on each textbox to track the input value
5925 YAHOO.util.Event.addListener(elTextbox, "keyup", function(){
5926 //TODO: set on a timeout
5927 oSelf._oCellEditor.value = elTextbox.value;
5928 oSelf.fireEvent("editorUpdateEvent",{editor:oSelf._oCellEditor});
5937 * Validates Editor input value to type Number, doing type conversion as
5938 * necessary. A valid Number value is return, else the previous value is returned
5939 * if input value does not validate.
5942 * @method validateNumber
5945 YAHOO.widget.DataTable.validateNumber = function(oData) {
5947 var number = oData * 1;
5950 if(YAHOO.lang.isNumber(number)) {
5998 * Overridable method gives implementers a hook to access data before
5999 * it gets added to RecordSet and rendered to the TBODY.
6001 * @method doBeforeLoadData
6002 * @param sRequest {String} Original request.
6003 * @param oResponse {Object} Response object.
6004 * @return {Boolean} Return true to continue loading data into RecordSet and
6005 * updating DataTable with new Records, false to cancel.
6007 YAHOO.widget.DataTable.prototype.doBeforeLoadData = function(sRequest, oResponse) {
6073 /////////////////////////////////////////////////////////////////////////////
6075 // Public Custom Event Handlers
6077 /////////////////////////////////////////////////////////////////////////////
6080 * Overridable custom event handler to sort Column.
6082 * @method onEventSortColumn
6083 * @param oArgs.event {HTMLEvent} Event object.
6084 * @param oArgs.target {HTMLElement} Target element.
6086 YAHOO.widget.DataTable.prototype.onEventSortColumn = function(oArgs) {
6087 //TODO: support nested header column sorting
6088 var evt = oArgs.event;
6089 var target = oArgs.target;
6090 YAHOO.util.Event.stopEvent(evt);
6092 var el = this.getThEl(target) || this.getTdEl(target);
6093 if(el && YAHOO.lang.isNumber(el.yuiColumnId)) {
6094 this.sortColumn(this._oColumnSet.getColumn(el.yuiColumnId));
6101 * Overridable custom event handler to manage selection according to desktop paradigm.
6103 * @method onEventSelectRow
6104 * @param oArgs.event {HTMLEvent} Event object.
6105 * @param oArgs.target {HTMLElement} Target element.
6107 YAHOO.widget.DataTable.prototype.onEventSelectRow = function(oArgs) {
6108 var sMode = this.get("selectionMode");
6109 if ((sMode == "singlecell") || (sMode == "cellblock") || (sMode == "cellrange")) {
6113 var evt = oArgs.event;
6114 var elTarget = oArgs.target;
6116 var bSHIFT = evt.shiftKey;
6117 var bCTRL = evt.ctrlKey;
6118 var i, nAnchorTrIndex;
6120 // Validate target row
6121 var elTargetRow = this.getTrEl(elTarget);
6123 var allRows = this._elTbody.rows;
6124 var nTargetTrIndex = elTargetRow.sectionRowIndex;
6125 var elAnchorRow = YAHOO.util.Dom.get(this._sSelectionAnchorId);
6127 // Both SHIFT and CTRL
6128 if((sMode != "single") && bSHIFT && bCTRL) {
6129 // Validate anchor row
6130 if(elAnchorRow && YAHOO.lang.isNumber(elAnchorRow.sectionRowIndex)) {
6131 nAnchorTrIndex = elAnchorRow.sectionRowIndex;
6132 if(this.isSelected(elAnchorRow)) {
6133 // Select all rows between anchor row and target row, including target row
6134 if(nAnchorTrIndex < nTargetTrIndex) {
6135 for(i=nAnchorTrIndex+1; i<=nTargetTrIndex; i++) {
6136 if(!this.isSelected(allRows[i])) {
6137 this.selectRow(allRows[i]);
6141 // Select all rows between target row and anchor row, including target row
6143 for(i=nAnchorTrIndex-1; i>=nTargetTrIndex; i--) {
6144 if(!this.isSelected(allRows[i])) {
6145 this.selectRow(allRows[i]);
6151 // Unselect all rows between anchor row and target row
6152 if(nAnchorTrIndex < nTargetTrIndex) {
6153 for(i=nAnchorTrIndex+1; i<=nTargetTrIndex-1; i++) {
6154 if(this.isSelected(allRows[i])) {
6155 this.unselectRow(allRows[i]);
6159 // Unselect all rows between target row and anchor row
6161 for(i=nTargetTrIndex+1; i<=nAnchorTrIndex-1; i++) {
6162 if(this.isSelected(allRows[i])) {
6163 this.unselectRow(allRows[i]);
6167 // Select the target row
6168 this.selectRow(elTargetRow);
6174 this._sSelectionAnchorId = elTargetRow.id;
6176 // Toggle selection of target
6177 if(this.isSelected(elTargetRow)) {
6178 this.unselectRow(elTargetRow);
6181 this.selectRow(elTargetRow);
6186 else if((sMode != "single") && bSHIFT) {
6187 this.unselectAllRows();
6190 if(elAnchorRow && YAHOO.lang.isNumber(elAnchorRow.sectionRowIndex)) {
6191 nAnchorTrIndex = elAnchorRow.sectionRowIndex;
6193 // Select all rows between anchor row and target row,
6194 // including the anchor row and target row
6195 if(nAnchorTrIndex < nTargetTrIndex) {
6196 for(i=nAnchorTrIndex; i<=nTargetTrIndex; i++) {
6197 this.selectRow(allRows[i]);
6200 // Select all rows between target row and anchor row,
6201 // including the target row and anchor row
6203 for(i=nAnchorTrIndex; i>=nTargetTrIndex; i--) {
6204 this.selectRow(allRows[i]);
6211 this._sSelectionAnchorId = elTargetRow.id;
6213 // Select target row only
6214 this.selectRow(elTargetRow);
6218 else if((sMode != "single") && bCTRL) {
6220 this._sSelectionAnchorId = elTargetRow.id;
6222 // Toggle selection of target
6223 if(this.isSelected(elTargetRow)) {
6224 this.unselectRow(elTargetRow);
6227 this.selectRow(elTargetRow);
6230 // Neither SHIFT nor CTRL
6231 else if(sMode == "single") {
6232 this.unselectAllRows();
6233 this.selectRow(elTargetRow);
6235 // Neither SHIFT nor CTRL
6238 this._sSelectionAnchorId = elTargetRow.id;
6240 // Select only target
6241 this.unselectAllRows();
6242 this.selectRow(elTargetRow);
6244 YAHOO.util.Event.stopEvent(evt);
6246 // Clear any selections that are a byproduct of the click or dblclick
6248 if(window.getSelection) {
6249 sel = window.getSelection();
6251 else if(document.getSelection) {
6252 sel = document.getSelection();
6254 else if(document.selection) {
6255 sel = document.selection;
6261 else if (sel.removeAllRanges) {
6262 sel.removeAllRanges();
6264 else if(sel.collapse) {
6274 * Overridable custom event handler to select cell.
6276 * @method onEventSelectCell
6277 * @param oArgs.event {HTMLEvent} Event object.
6278 * @param oArgs.target {HTMLElement} Target element.
6280 YAHOO.widget.DataTable.prototype.onEventSelectCell = function(oArgs) {
6281 var sMode = this.get("selectionMode");
6282 if ((sMode == "standard") || (sMode == "single")) {
6286 var evt = oArgs.event;
6287 var elTarget = oArgs.target;
6289 var bSHIFT = evt.shiftKey;
6290 var bCTRL = evt.ctrlKey;
6291 var i, j, nAnchorTrIndex, nAnchorTdIndex, currentRow, startIndex, endIndex;
6293 var elTargetCell = this.getTdEl(elTarget);
6295 var elTargetRow = this.getTrEl(elTargetCell);
6296 var allRows = this._elTbody.rows;
6297 var nTargetTrIndex = elTargetRow.sectionRowIndex;
6298 var nTargetTdIndex = elTarget.yuiCellIndex;
6299 var elAnchorCell = YAHOO.util.Dom.get(this._sSelectionAnchorId);
6301 // Both SHIFT and CTRL
6302 if((sMode != "singlecell") && bSHIFT && bCTRL) {
6304 if(elAnchorCell && YAHOO.lang.isNumber(elAnchorCell.yuiCellIndex)) {
6305 nAnchorTrIndex = elAnchorCell.parentNode.sectionRowIndex;
6306 nAnchorTdIndex = elAnchorCell.yuiCellIndex;
6308 // Anchor is selected
6309 if(this.isSelected(elAnchorCell)) {
6310 // All cells are on the same row
6311 if(nAnchorTrIndex == nTargetTrIndex) {
6312 // Select all cells between anchor cell and target cell, including target cell
6313 if(nAnchorTdIndex < nTargetTdIndex) {
6314 for(i=nAnchorTdIndex+1; i<=nTargetTdIndex; i++) {
6315 this.selectCell(allRows[nTargetTrIndex].cells[i]);
6318 // Select all cells between target cell and anchor cell, including target cell
6319 else if(nTargetTdIndex < nAnchorTdIndex) {
6320 for(i=nTargetTdIndex; i<nAnchorTdIndex; i++) {
6321 this.selectCell(allRows[nTargetTrIndex].cells[i]);
6325 // Anchor row is above target row
6326 else if(nAnchorTrIndex < nTargetTrIndex) {
6327 if(sMode == "cellrange") {
6328 // Select all cells on anchor row from anchor cell to the end of the row
6329 for(i=nAnchorTdIndex+1; i<allRows[nAnchorTrIndex].cells.length; i++) {
6330 this.selectCell(allRows[nAnchorTrIndex].cells[i]);
6333 // Select all cells on all rows between anchor row and target row
6334 for(i=nAnchorTrIndex+1; i<nTargetTrIndex; i++) {
6335 for(j=0; j<allRows[i].cells.length; j++){
6336 this.selectCell(allRows[i].cells[j]);
6340 // Select all cells on target row from first cell to the target cell
6341 for(i=0; i<=nTargetTdIndex; i++) {
6342 this.selectCell(allRows[nTargetTrIndex].cells[i]);
6345 else if(sMode == "cellblock") {
6346 startIndex = Math.min(nAnchorTdIndex, nTargetTdIndex);
6347 endIndex = Math.max(nAnchorTdIndex, nTargetTdIndex);
6349 // Select all cells from startIndex to endIndex on rows between anchor row and target row
6350 for(i=nAnchorTrIndex; i<=nTargetTrIndex; i++) {
6351 for(j=startIndex; j<=endIndex; j++) {
6352 this.selectCell(allRows[i].cells[j]);
6357 // Anchor row is below target row
6359 if(sMode == "cellrange") {
6360 // Select all cells on target row from target cell to the end of the row
6361 for(i=nTargetTdIndex; i<allRows[nTargetTrIndex].cells.length; i++) {
6362 this.selectCell(allRows[nTargetTrIndex].cells[i]);
6365 // Select all cells on all rows between target row and anchor row
6366 for(i=nTargetTrIndex+1; i<nAnchorTrIndex; i++) {
6367 for(j=0; j<allRows[i].cells.length; j++){
6368 this.selectCell(allRows[i].cells[j]);
6372 // Select all cells on anchor row from first cell to the anchor cell
6373 for(i=0; i<nAnchorTdIndex; i++) {
6374 this.selectCell(allRows[nAnchorTrIndex].cells[i]);
6377 else if(sMode == "cellblock") {
6378 startIndex = Math.min(nAnchorTdIndex, nTargetTdIndex);
6379 endIndex = Math.max(nAnchorTdIndex, nTargetTdIndex);
6381 // Select all cells from startIndex to endIndex on rows between target row and anchor row
6382 for(i=nAnchorTrIndex; i>=nTargetTrIndex; i--) {
6383 for(j=endIndex; j>=startIndex; j--) {
6384 this.selectCell(allRows[i].cells[j]);
6390 // Anchor cell is unselected
6392 // All cells are on the same row
6393 if(nAnchorTrIndex == nTargetTrIndex) {
6394 // Unselect all cells between anchor cell and target cell
6395 if(nAnchorTdIndex < nTargetTdIndex) {
6396 for(i=nAnchorTdIndex+1; i<nTargetTdIndex; i++) {
6397 this.unselectCell(allRows[nTargetTrIndex].cells[i]);
6400 // Select all cells between target cell and anchor cell
6401 else if(nTargetTdIndex < nAnchorTdIndex) {
6402 for(i=nTargetTdIndex+1; i<nAnchorTdIndex; i++) {
6403 this.unselectCell(allRows[nTargetTrIndex].cells[i]);
6407 // Anchor row is above target row
6408 if(nAnchorTrIndex < nTargetTrIndex) {
6409 // Unselect all cells from anchor cell to target cell
6410 for(i=nAnchorTrIndex; i<=nTargetTrIndex; i++) {
6411 currentRow = allRows[i];
6412 for(j=0; j<currentRow.cells.length; j++) {
6413 // This is the anchor row, only unselect cells after the anchor cell
6414 if(currentRow.sectionRowIndex == nAnchorTrIndex) {
6415 if(j>nAnchorTdIndex) {
6416 this.unselectCell(currentRow.cells[j]);
6419 // This is the target row, only unelect cells before the target cell
6420 else if(currentRow.sectionRowIndex == nTargetTrIndex) {
6421 if(j<nTargetTdIndex) {
6422 this.unselectCell(currentRow.cells[j]);
6425 // Unselect all cells on this row
6427 this.unselectCell(currentRow.cells[j]);
6432 // Anchor row is below target row
6434 // Unselect all cells from target cell to anchor cell
6435 for(i=nTargetTrIndex; i<=nAnchorTrIndex; i++) {
6436 currentRow = allRows[i];
6437 for(j=0; j<currentRow.cells.length; j++) {
6438 // This is the target row, only unselect cells after the target cell
6439 if(currentRow.sectionRowIndex == nTargetTrIndex) {
6440 if(j>nTargetTdIndex) {
6441 this.unselectCell(currentRow.cells[j]);
6444 // This is the anchor row, only unselect cells before the anchor cell
6445 else if(currentRow.sectionRowIndex == nAnchorTrIndex) {
6446 if(j<nAnchorTdIndex) {
6447 this.unselectCell(currentRow.cells[j]);
6450 // Unselect all cells on this row
6452 this.unselectCell(currentRow.cells[j]);
6458 // Select the target cell
6459 this.selectCell(elTargetCell);
6465 this._sSelectionAnchorId = elTargetCell.id;
6467 // Toggle selection of target
6468 if(this.isSelected(elTargetCell)) {
6469 this.unselectCell(elTargetCell);
6472 this.selectCell(elTargetCell);
6477 else if((sMode != "singlecell") && bSHIFT) {
6478 this.unselectAllCells();
6481 if(elAnchorCell && YAHOO.lang.isNumber(elAnchorCell.yuiCellIndex)) {
6482 nAnchorTrIndex = elAnchorCell.parentNode.sectionRowIndex;
6483 nAnchorTdIndex = elAnchorCell.yuiCellIndex;
6485 // All cells are on the same row
6486 if(nAnchorTrIndex == nTargetTrIndex) {
6487 // Select all cells between anchor cell and target cell,
6488 // including the anchor cell and target cell
6489 if(nAnchorTdIndex < nTargetTdIndex) {
6490 for(i=nAnchorTdIndex; i<=nTargetTdIndex; i++) {
6491 this.selectCell(allRows[nTargetTrIndex].cells[i]);
6494 // Select all cells between target cell and anchor cell
6495 // including the target cell and anchor cell
6496 else if(nTargetTdIndex < nAnchorTdIndex) {
6497 for(i=nTargetTdIndex; i<=nAnchorTdIndex; i++) {
6498 this.selectCell(allRows[nTargetTrIndex].cells[i]);
6502 // Anchor row is above target row
6503 else if(nAnchorTrIndex < nTargetTrIndex) {
6504 if(sMode == "cellrange") {
6505 // Select all cells from anchor cell to target cell
6506 // including the anchor cell and target cell
6507 for(i=nAnchorTrIndex; i<=nTargetTrIndex; i++) {
6508 currentRow = allRows[i];
6509 for(j=0; j<currentRow.cells.length; j++) {
6510 // This is the anchor row, only select the anchor cell and after
6511 if(currentRow.sectionRowIndex == nAnchorTrIndex) {
6512 if(j>=nAnchorTdIndex) {
6513 this.selectCell(currentRow.cells[j]);
6516 // This is the target row, only select the target cell and before
6517 else if(currentRow.sectionRowIndex == nTargetTrIndex) {
6518 if(j<=nTargetTdIndex) {
6519 this.selectCell(currentRow.cells[j]);
6522 // Select all cells on this row
6524 this.selectCell(currentRow.cells[j]);
6529 else if(sMode == "cellblock") {
6530 // Select the cellblock from anchor cell to target cell
6531 // including the anchor cell and the target cell
6532 startIndex = Math.min(nAnchorTdIndex, nTargetTdIndex);
6533 endIndex = Math.max(nAnchorTdIndex, nTargetTdIndex);
6535 for(i=nAnchorTrIndex; i<=nTargetTrIndex; i++) {
6536 for(j=startIndex; j<=endIndex; j++) {
6537 this.selectCell(allRows[i].cells[j]);
6541 this._sLastSelectedId = allRows[nTargetTrIndex].cells[nTargetTdIndex].id;
6544 // Anchor row is below target row
6546 if(sMode == "cellrange") {
6547 // Select all cells from target cell to anchor cell,
6548 // including the target cell and anchor cell
6549 for(i=nTargetTrIndex; i<=nAnchorTrIndex; i++) {
6550 currentRow = allRows[i];
6551 for(j=0; j<currentRow.cells.length; j++) {
6552 // This is the target row, only select the target cell and after
6553 if(currentRow.sectionRowIndex == nTargetTrIndex) {
6554 if(j>=nTargetTdIndex) {
6555 this.selectCell(currentRow.cells[j]);
6558 // This is the anchor row, only select the anchor cell and before
6559 else if(currentRow.sectionRowIndex == nAnchorTrIndex) {
6560 if(j<=nAnchorTdIndex) {
6561 this.selectCell(currentRow.cells[j]);
6564 // Select all cells on this row
6566 this.selectCell(currentRow.cells[j]);
6571 else if(sMode == "cellblock") {
6572 // Select the cellblock from target cell to anchor cell
6573 // including the target cell and the anchor cell
6574 startIndex = Math.min(nAnchorTdIndex, nTargetTdIndex);
6575 endIndex = Math.max(nAnchorTdIndex, nTargetTdIndex);
6577 for(i=nTargetTrIndex; i<=nAnchorTrIndex; i++) {
6578 for(j=startIndex; j<=endIndex; j++) {
6579 this.selectCell(allRows[i].cells[j]);
6583 this._sLastSelectedId = allRows[nTargetTrIndex].cells[nTargetTdIndex].id;
6590 this._sSelectionAnchorId = elTargetCell.id;
6592 // Select target only
6593 this.selectCell(elTargetCell);
6597 else if((sMode != "singlecell") && bCTRL) {
6599 this._sSelectionAnchorId = elTargetCell.id;
6601 // Toggle selection of target
6602 if(this.isSelected(elTargetCell)) {
6603 this.unselectCell(elTargetCell);
6606 this.selectCell(elTargetCell);
6609 // Neither SHIFT nor CTRL, or multi-selection has been disabled
6612 this._sSelectionAnchorId = elTargetCell.id;
6614 // Select only target
6615 this.unselectAllCells();
6616 this.selectCell(elTargetCell);
6619 YAHOO.util.Event.stopEvent(evt);
6621 // Clear any selections that are a byproduct of the click or dblclick
6623 if(window.getSelection) {
6624 sel = window.getSelection();
6626 else if(document.getSelection) {
6627 sel = document.getSelection();
6629 else if(document.selection) {
6630 sel = document.selection;
6636 else if (sel.removeAllRanges) {
6637 sel.removeAllRanges();
6639 else if(sel.collapse) {
6659 * Overridable custom event handler to highlight row.
6661 * @method onEventHighlightRow
6662 * @param oArgs.event {HTMLEvent} Event object.
6663 * @param oArgs.target {HTMLElement} Target element.
6665 YAHOO.widget.DataTable.prototype.onEventHighlightRow = function(oArgs) {
6666 var evt = oArgs.event;
6667 var elTarget = oArgs.target;
6668 this.highlightRow(elTarget);
6672 * Overridable custom event handler to unhighlight row.
6674 * @method onEventUnhighlightRow
6675 * @param oArgs.event {HTMLEvent} Event object.
6676 * @param oArgs.target {HTMLElement} Target element.
6678 YAHOO.widget.DataTable.prototype.onEventUnhighlightRow = function(oArgs) {
6679 var evt = oArgs.event;
6680 var elTarget = oArgs.target;
6681 this.unhighlightRow(elTarget);
6685 * Overridable custom event handler to highlight cell.
6687 * @method onEventHighlightCell
6688 * @param oArgs.event {HTMLEvent} Event object.
6689 * @param oArgs.target {HTMLElement} Target element.
6691 YAHOO.widget.DataTable.prototype.onEventHighlightCell = function(oArgs) {
6692 var evt = oArgs.event;
6693 var elTarget = oArgs.target;
6694 this.highlightCell(elTarget);
6698 * Overridable custom event handler to unhighlight cell.
6700 * @method onEventUnhighlightCell
6701 * @param oArgs.event {HTMLEvent} Event object.
6702 * @param oArgs.target {HTMLElement} Target element.
6704 YAHOO.widget.DataTable.prototype.onEventUnhighlightCell = function(oArgs) {
6705 var evt = oArgs.event;
6706 var elTarget = oArgs.target;
6707 this.unhighlightCell(elTarget);
6711 * Overridable custom event handler to format cell.
6713 * @method onEventFormatCell
6714 * @param oArgs.event {HTMLEvent} Event object.
6715 * @param oArgs.target {HTMLElement} Target element.
6717 YAHOO.widget.DataTable.prototype.onEventFormatCell = function(oArgs) {
6718 var evt = oArgs.event;
6719 var target = oArgs.target;
6720 var elTag = target.tagName.toLowerCase();
6722 var elCell = this.getTdEl(target);
6723 if(elCell && YAHOO.lang.isNumber(elCell.yuiColumnId)) {
6724 var oColumn = this._oColumnSet.getColumn(elCell.yuiColumnId);
6725 this.formatCell(elCell, this.getRecord(elCell), oColumn);
6732 * Overridable custom event handler to edit cell.
6734 * @method onEventShowCellEditor
6735 * @param oArgs.event {HTMLEvent} Event object.
6736 * @param oArgs.target {HTMLElement} Target element.
6738 YAHOO.widget.DataTable.prototype.onEventShowCellEditor = function(oArgs) {
6739 var evt = oArgs.event;
6740 var target = oArgs.target;
6741 var elTag = target.tagName.toLowerCase();
6743 var elCell = this.getTdEl(target);
6745 this.showCellEditor(elCell);
6750 // Backward compatibility
6751 YAHOO.widget.DataTable.prototype.onEventEditCell = function(oArgs) {
6752 this.onEventShowCellEditor(oArgs);
6756 * Overridable custom event handler to save Cell Editor input.
6758 * @method onEventSaveCellEditor
6759 * @param oArgs.editor {Object} Cell Editor object literal.
6761 YAHOO.widget.DataTable.prototype.onEventSaveCellEditor = function(oArgs) {
6762 this.saveCellEditor();
6766 * Callback function for creating a progressively enhanced DataTable first
6767 * receives data from DataSource and populates the RecordSet, then initializes
6770 * @method _onDataReturnEnhanceTable
6771 * @param sRequest {String} Original request.
6772 * @param oResponse {Object} Response object.
6773 * @param bError {Boolean} (optional) True if there was a data error.
6776 YAHOO.widget.DataTable.prototype._onDataReturnEnhanceTable = function(sRequest, oResponse) {
6777 // Pass data through abstract method for any transformations
6778 var ok = this.doBeforeLoadData(sRequest, oResponse);
6780 // Data ok to populate
6781 if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) {
6783 this._oRecordSet.addRecords(oResponse.results);
6785 // Initialize DOM elements
6786 this._initTableEl();
6787 if(!this._elTable || !this._elThead || !this._elTbody) {
6791 // Call Element's constructor after DOM elements are created
6792 // but *before* UI is updated with data
6793 YAHOO.widget.DataTable.superclass.constructor.call(this, this._elContainer, this._oConfigs);
6795 //HACK: Set the Paginator values
6796 if(this._oConfigs.paginator) {
6797 this.updatePaginator(this._oConfigs.paginator);
6804 else if(ok && oResponse.error) {
6805 this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR);
6809 this.showTableMessage(YAHOO.widget.DataTable.MSG_EMPTY, YAHOO.widget.DataTable.CLASS_EMPTY);
6814 * Callback function receives data from DataSource and populates an entire
6815 * DataTable with Records and TR elements, clearing previous Records, if any.
6817 * @method onDataReturnInitializeTable
6818 * @param sRequest {String} Original request.
6819 * @param oResponse {Object} Response object.
6820 * @param bError {Boolean} (optional) True if there was a data error.
6822 YAHOO.widget.DataTable.prototype.onDataReturnInitializeTable = function(sRequest, oResponse) {
6823 this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse});
6825 // Pass data through abstract method for any transformations
6826 var ok = this.doBeforeLoadData(sRequest, oResponse);
6828 // Data ok to populate
6829 if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) {
6830 this.initializeTable(oResponse.results);
6833 else if(ok && oResponse.error) {
6834 this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR);
6838 this.showTableMessage(YAHOO.widget.DataTable.MSG_EMPTY, YAHOO.widget.DataTable.CLASS_EMPTY);
6841 // Backward compatibility
6842 YAHOO.widget.DataTable.prototype.onDataReturnReplaceRows = function(sRequest, oResponse) {
6843 this.onDataReturnInitializeTable(sRequest, oResponse);
6847 * Callback function receives data from DataSource and appends to an existing
6848 * DataTable new Records and, if applicable, creates or updates
6849 * corresponding TR elements.
6851 * @method onDataReturnAppendRows
6852 * @param sRequest {String} Original request.
6853 * @param oResponse {Object} Response object.
6854 * @param bError {Boolean} (optional) True if there was a data error.
6856 YAHOO.widget.DataTable.prototype.onDataReturnAppendRows = function(sRequest, oResponse) {
6857 this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse});
6859 // Pass data through abstract method for any transformations
6860 var ok = this.doBeforeLoadData(sRequest, oResponse);
6862 // Data ok to append
6863 if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) {
6864 this.addRows(oResponse.results);
6867 else if(ok && oResponse.error) {
6868 this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR);
6873 * Callback function receives data from DataSource and inserts into top of an
6874 * existing DataTable new Records and, if applicable, creates or updates
6875 * corresponding TR elements.
6877 * @method onDataReturnInsertRows
6878 * @param sRequest {String} Original request.
6879 * @param oResponse {Object} Response object.
6880 * @param bError {Boolean} (optional) True if there was a data error.
6882 YAHOO.widget.DataTable.prototype.onDataReturnInsertRows = function(sRequest, oResponse) {
6883 this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse});
6885 // Pass data through abstract method for any transformations
6886 var ok = this.doBeforeLoadData(sRequest, oResponse);
6888 // Data ok to append
6889 if(ok && oResponse && !oResponse.error && YAHOO.lang.isArray(oResponse.results)) {
6890 this.addRows(oResponse.results, 0);
6893 else if(ok && oResponse.error) {
6894 this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR);
6932 /////////////////////////////////////////////////////////////////////////////
6936 /////////////////////////////////////////////////////////////////////////////
6939 * Fired when the DataTable instance's initialization is complete.
6945 * Fired when the DataTable's view is refreshed.
6947 * @event refreshEvent
6951 * Fired when data is returned from DataSource.
6953 * @event dataReturnEvent
6954 * @param oArgs.request {String} Original request.
6955 * @param oArgs.response {Object} Response object.
6959 * Fired when the DataTable has a focus.
6961 * @event tableFocusEvent
6965 * Fired when the DataTable has a blur.
6967 * @event tableBlurEvent
6971 * Fired when the DataTable has a mouseover.
6973 * @event tableMouseoverEvent
6974 * @param oArgs.event {HTMLEvent} The event object.
6975 * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
6980 * Fired when the DataTable has a mouseout.
6982 * @event tableMouseoutEvent
6983 * @param oArgs.event {HTMLEvent} The event object.
6984 * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
6989 * Fired when the DataTable has a mousedown.
6991 * @event tableMousedownEvent
6992 * @param oArgs.event {HTMLEvent} The event object.
6993 * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
6998 * Fired when the DataTable has a click.
7000 * @event tableClickEvent
7001 * @param oArgs.event {HTMLEvent} The event object.
7002 * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
7007 * Fired when the DataTable has a dblclick.
7009 * @event tableDblclickEvent
7010 * @param oArgs.event {HTMLEvent} The event object.
7011 * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
7016 * Fired when a fixed scrolling DataTable has a scroll.
7018 * @event tableScrollEvent
7019 * @param oArgs.event {HTMLEvent} The event object.
7020 * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE)
7021 * or the DataTable's TBODY element (everyone else).
7026 * Fired when a message is shown in the DataTable's message element.
7028 * @event tableMsgShowEvent
7029 * @param oArgs.html {String} The HTML displayed.
7030 * @param oArgs.className {String} The className assigned.
7035 * Fired when the DataTable's message element is hidden.
7037 * @event tableMsgHideEvent
7041 * Fired when a header row has a mouseover.
7043 * @event headerRowMouseoverEvent
7044 * @param oArgs.event {HTMLEvent} The event object.
7045 * @param oArgs.target {HTMLElement} The TR element.
7049 * Fired when a header row has a mouseout.
7051 * @event headerRowMouseoutEvent
7052 * @param oArgs.event {HTMLEvent} The event object.
7053 * @param oArgs.target {HTMLElement} The TR element.
7057 * Fired when a header row has a mousedown.
7059 * @event headerRowMousedownEvent
7060 * @param oArgs.event {HTMLEvent} The event object.
7061 * @param oArgs.target {HTMLElement} The TR element.
7065 * Fired when a header row has a click.
7067 * @event headerRowClickEvent
7068 * @param oArgs.event {HTMLEvent} The event object.
7069 * @param oArgs.target {HTMLElement} The TR element.
7073 * Fired when a header row has a dblclick.
7075 * @event headerRowDblclickEvent
7076 * @param oArgs.event {HTMLEvent} The event object.
7077 * @param oArgs.target {HTMLElement} The TR element.
7081 * Fired when a header cell has a mouseover.
7083 * @event headerCellMouseoverEvent
7084 * @param oArgs.event {HTMLEvent} The event object.
7085 * @param oArgs.target {HTMLElement} The TH element.
7090 * Fired when a header cell has a mouseout.
7092 * @event headerCellMouseoutEvent
7093 * @param oArgs.event {HTMLEvent} The event object.
7094 * @param oArgs.target {HTMLElement} The TH element.
7099 * Fired when a header cell has a mousedown.
7101 * @event headerCellMousedownEvent
7102 * @param oArgs.event {HTMLEvent} The event object.
7103 * @param oArgs.target {HTMLElement} The TH element.
7107 * Fired when a header cell has a click.
7109 * @event headerCellClickEvent
7110 * @param oArgs.event {HTMLEvent} The event object.
7111 * @param oArgs.target {HTMLElement} The TH element.
7115 * Fired when a header cell has a dblclick.
7117 * @event headerCellDblclickEvent
7118 * @param oArgs.event {HTMLEvent} The event object.
7119 * @param oArgs.target {HTMLElement} The TH element.
7123 * Fired when a header label has a mouseover.
7125 * @event headerLabelMouseoverEvent
7126 * @param oArgs.event {HTMLEvent} The event object.
7127 * @param oArgs.target {HTMLElement} The SPAN element.
7132 * Fired when a header label has a mouseout.
7134 * @event headerLabelMouseoutEvent
7135 * @param oArgs.event {HTMLEvent} The event object.
7136 * @param oArgs.target {HTMLElement} The SPAN element.
7141 * Fired when a header label has a mousedown.
7143 * @event headerLabelMousedownEvent
7144 * @param oArgs.event {HTMLEvent} The event object.
7145 * @param oArgs.target {HTMLElement} The SPAN element.
7149 * Fired when a header label has a click.
7151 * @event headerLabelClickEvent
7152 * @param oArgs.event {HTMLEvent} The event object.
7153 * @param oArgs.target {HTMLElement} The SPAN element.
7157 * Fired when a header label has a dblclick.
7159 * @event headerLabelDblclickEvent
7160 * @param oArgs.event {HTMLEvent} The event object.
7161 * @param oArgs.target {HTMLElement} The SPAN element.
7165 * Fired when a column is sorted.
7167 * @event columnSortEvent
7168 * @param oArgs.column {YAHOO.widget.Column} The Column instance.
7169 * @param oArgs.dir {String} Sort direction "asc" or "desc".
7173 * Fired when a column is resized.
7175 * @event columnResizeEvent
7176 * @param oArgs.column {YAHOO.widget.Column} The Column instance.
7177 * @param oArgs.target {HTMLElement} The TH element.
7181 * Fired when a row has a mouseover.
7183 * @event rowMouseoverEvent
7184 * @param oArgs.event {HTMLEvent} The event object.
7185 * @param oArgs.target {HTMLElement} The TR element.
7189 * Fired when a row has a mouseout.
7191 * @event rowMouseoutEvent
7192 * @param oArgs.event {HTMLEvent} The event object.
7193 * @param oArgs.target {HTMLElement} The TR element.
7197 * Fired when a row has a mousedown.
7199 * @event rowMousedownEvent
7200 * @param oArgs.event {HTMLEvent} The event object.
7201 * @param oArgs.target {HTMLElement} The TR element.
7205 * Fired when a row has a click.
7207 * @event rowClickEvent
7208 * @param oArgs.event {HTMLEvent} The event object.
7209 * @param oArgs.target {HTMLElement} The TR element.
7213 * Fired when a row has a dblclick.
7215 * @event rowDblclickEvent
7216 * @param oArgs.event {HTMLEvent} The event object.
7217 * @param oArgs.target {HTMLElement} The TR element.
7221 * Fired when a row is added.
7223 * @event rowAddEvent
7224 * @param oArgs.record {YAHOO.widget.Record} The added Record.
7228 * Fired when a row is updated.
7230 * @event rowUpdateEvent
7231 * @param oArgs.record {YAHOO.widget.Record} The updated Record.
7232 * @param oArgs.oldData {Object} Object literal of the old data.
7236 * Fired when a row is deleted.
7238 * @event rowDeleteEvent
7239 * @param oArgs.oldData {Object} Object literal of the deleted data.
7240 * @param oArgs.recordIndex {Number} Index of the deleted Record.
7241 * @param oArgs.trElIndex {Number} Index of the deleted TR element, if in view.
7245 * Fired when a row is selected.
7247 * @event rowSelectEvent
7248 * @param oArgs.el {HTMLElement} The selected TR element, if applicable.
7249 * @param oArgs.record {YAHOO.widget.Record} The selected Record.
7253 * Fired when a row is unselected.
7255 * @event rowUnselectEvent
7256 * @param oArgs.el {HTMLElement} The unselected TR element, if applicable.
7257 * @param oArgs.record {YAHOO.widget.Record} The unselected Record.
7260 /*TODO: delete and use rowUnselectEvent?
7261 * Fired when all row selections are cleared.
7263 * @event unselectAllRowsEvent
7267 * Fired when a row is highlighted.
7269 * @event rowHighlightEvent
7270 * @param oArgs.el {HTMLElement} The highlighted TR element.
7271 * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
7275 * Fired when a row is unhighlighted.
7277 * @event rowUnhighlightEvent
7278 * @param oArgs.el {HTMLElement} The highlighted TR element.
7279 * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
7283 * Fired when a cell has a mouseover.
7285 * @event cellMouseoverEvent
7286 * @param oArgs.event {HTMLEvent} The event object.
7287 * @param oArgs.target {HTMLElement} The TD element.
7291 * Fired when a cell has a mouseout.
7293 * @event cellMouseoutEvent
7294 * @param oArgs.event {HTMLEvent} The event object.
7295 * @param oArgs.target {HTMLElement} The TD element.
7299 * Fired when a cell has a mousedown.
7301 * @event cellMousedownEvent
7302 * @param oArgs.event {HTMLEvent} The event object.
7303 * @param oArgs.target {HTMLElement} The TD element.
7307 * Fired when a cell has a click.
7309 * @event cellClickEvent
7310 * @param oArgs.event {HTMLEvent} The event object.
7311 * @param oArgs.target {HTMLElement} The TD element.
7315 * Fired when a cell has a dblclick.
7317 * @event cellDblclickEvent
7318 * @param oArgs.event {HTMLEvent} The event object.
7319 * @param oArgs.target {HTMLElement} The TD element.
7323 * Fired when a cell is formatted.
7325 * @event cellFormatEvent
7326 * @param oArgs.el {HTMLElement} The formatted TD element.
7327 * @param oArgs.record {YAHOO.widget.Record} The formatted Record.
7328 * @param oArgs.key {String} The key of the formatted cell.
7332 * Fired when a cell is selected.
7334 * @event cellSelectEvent
7335 * @param oArgs.el {HTMLElement} The selected TD element.
7336 * @param oArgs.record {YAHOO.widget.Record} The selected Record.
7337 * @param oArgs.key {String} The key of the selected cell.
7341 * Fired when a cell is unselected.
7343 * @event cellUnselectEvent
7344 * @param oArgs.el {HTMLElement} The unselected TD element.
7345 * @param oArgs.record {YAHOO.widget.Record} The unselected Record.
7346 * @param oArgs.key {String} The key of the unselected cell.
7350 * Fired when a cell is highlighted.
7352 * @event cellHighlightEvent
7353 * @param oArgs.el {HTMLElement} The highlighted TD element.
7354 * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
7355 * @param oArgs.key {String} The key of the highlighted cell.
7359 * Fired when a cell is unhighlighted.
7361 * @event cellUnhighlightEvent
7362 * @param oArgs.el {HTMLElement} The unhighlighted TD element.
7363 * @param oArgs.record {YAHOO.widget.Record} The unhighlighted Record.
7364 * @param oArgs.key {String} The key of the unhighlighted cell.
7367 /*TODO: hide from doc and use cellUnselectEvent
7368 * Fired when all cell selections are cleared.
7370 * @event unselectAllCellsEvent
7374 * Fired when DataTable paginator is updated.
7376 * @event paginatorUpdateEvent
7377 * @param paginator {Object} Object literal of Paginator values.
7381 * Fired when an Editor is activated.
7383 * @event editorShowEvent
7384 * @param oArgs.editor {Object} The Editor object literal.
7388 * Fired when an active Editor has a keydown.
7390 * @event editorKeydownEvent
7391 * @param oArgs.editor {Object} The Editor object literal.
7392 * @param oArgs.event {HTMLEvent} The event object.
7396 * Fired when Editor input is reverted.
7398 * @event editorRevertEvent
7399 * @param oArgs.editor {Object} The Editor object literal.
7400 * @param oArgs.newData {Object} New data value.
7401 * @param oArgs.oldData {Object} Old data value.
7405 * Fired when Editor input is saved.
7407 * @event editorSaveEvent
7408 * @param oArgs.editor {Object} The Editor object literal.
7409 * @param oArgs.newData {Object} New data value.
7410 * @param oArgs.oldData {Object} Old data value.
7414 * Fired when Editor input is canceled.
7416 * @event editorCancelEvent
7417 * @param oArgs.editor {Object} The Editor object literal.
7421 * Fired when an active Editor has a blur.
7423 * @event editorBlurEvent
7424 * @param oArgs.editor {Object} The Editor object literal.
7434 * Fired when a link is clicked.
7436 * @event linkClickEvent
7437 * @param oArgs.event {HTMLEvent} The event object.
7438 * @param oArgs.target {HTMLElement} The A element.
7442 * Fired when a BUTTON element is clicked.
7444 * @event buttonClickEvent
7445 * @param oArgs.event {HTMLEvent} The event object.
7446 * @param oArgs.target {HTMLElement} The BUTTON element.
7450 * Fired when a CHECKBOX element is clicked.
7452 * @event checkboxClickEvent
7453 * @param oArgs.event {HTMLEvent} The event object.
7454 * @param oArgs.target {HTMLElement} The CHECKBOX element.
7458 * Fired when a SELECT element is changed.
7460 * @event dropdownChangeEvent
7461 * @param oArgs.event {HTMLEvent} The event object.
7462 * @param oArgs.target {HTMLElement} The SELECT element.
7466 * Fired when a RADIO element is clicked.
7468 * @event radioClickEvent
7469 * @param oArgs.event {HTMLEvent} The event object.
7470 * @param oArgs.target {HTMLElement} The RADIO element.
7474 /****************************************************************************/
7475 /****************************************************************************/
7476 /****************************************************************************/
7479 * The ColumnSet class defines and manages a DataTable's Columns,
7480 * including nested hierarchies and access to individual Column instances.
7482 * @namespace YAHOO.widget
7484 * @uses YAHOO.util.EventProvider
7486 * @param aHeaders {Object[]} Array of object literals that define header cells.
7488 YAHOO.widget.ColumnSet = function(aHeaders) {
7489 this._sName = "instance" + YAHOO.widget.ColumnSet._nCount;
7491 // DOM tree representation of all Columns
7493 // Flat representation of all Columns
7495 // Flat representation of only Columns that are meant to display data
7497 // Array of HEADERS attribute values for all keys in the "keys" array
7500 // Tracks current node list depth being tracked
7503 // Internal recursive function to defined Column instances
7504 var parseColumns = function(nodeList, parent) {
7508 // Create corresponding tree node if not already there for this depth
7509 if(!tree[nodeDepth]) {
7510 tree[nodeDepth] = [];
7514 // Parse each node at this depth for attributes and any children
7515 for(var j=0; j<nodeList.length; j++) {
7516 var currentNode = nodeList[j];
7518 // Instantiate a new Column for each node
7519 var oColumn = new YAHOO.widget.Column(currentNode);
7521 // Add the new Column to the flat list
7524 // Assign its parent as an attribute, if applicable
7526 oColumn.parent = parent;
7529 // The Column has descendants
7530 if(YAHOO.lang.isArray(currentNode.children)) {
7531 oColumn.children = currentNode.children;
7533 // Determine COLSPAN value for this Column
7534 var terminalChildNodes = 0;
7535 var countTerminalChildNodes = function(ancestor) {
7536 var descendants = ancestor.children;
7537 // Drill down each branch and count terminal nodes
7538 for(var k=0; k<descendants.length; k++) {
7539 // Keep drilling down
7540 if(YAHOO.lang.isArray(descendants[k].children)) {
7541 countTerminalChildNodes(descendants[k]);
7543 // Reached branch terminus
7545 terminalChildNodes++;
7549 countTerminalChildNodes(currentNode);
7550 oColumn._colspan = terminalChildNodes;
7552 // Cascade certain properties to children if not defined on their own
7553 var currentChildren = currentNode.children;
7554 for(var k=0; k<currentChildren.length; k++) {
7555 var child = currentChildren[k];
7556 if(oColumn.className && (child.className === undefined)) {
7557 child.className = oColumn.className;
7559 if(oColumn.editor && (child.editor === undefined)) {
7560 child.editor = oColumn.editor;
7562 if(oColumn.editorOptions && (child.editorOptions === undefined)) {
7563 child.editorOptions = oColumn.editorOptions;
7565 if(oColumn.formatter && (child.formatter === undefined)) {
7566 child.formatter = oColumn.formatter;
7568 if(oColumn.resizeable && (child.resizeable === undefined)) {
7569 child.resizeable = oColumn.resizeable;
7571 if(oColumn.sortable && (child.sortable === undefined)) {
7572 child.sortable = oColumn.sortable;
7574 if(oColumn.width && (child.width === undefined)) {
7575 child.width = oColumn.width;
7577 // Backward compatibility
7578 if(oColumn.type && (child.type === undefined)) {
7579 child.type = oColumn.type;
7581 if(oColumn.type && !oColumn.formatter) {
7582 oColumn.formatter = oColumn.type;
7584 if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
7585 oColumn.label = oColumn.text;
7587 if(oColumn.parser) {
7589 if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
7590 (oColumn.sortOptions.descFunction))) {
7594 // The children themselves must also be parsed for Column instances
7595 if(!tree[nodeDepth+1]) {
7596 tree[nodeDepth+1] = [];
7598 parseColumns(currentChildren, oColumn);
7600 // This Column does not have any children
7602 oColumn._nKeyIndex = keys.length;
7603 oColumn._colspan = 1;
7607 // Add the Column to the top-down tree
7608 tree[nodeDepth].push(oColumn);
7613 // Parse out Column instances from the array of object literals
7614 if(YAHOO.lang.isArray(aHeaders)) {
7615 parseColumns(aHeaders);
7618 // Determine ROWSPAN value for each Column in the tree
7619 var parseTreeForRowspan = function(tree) {
7620 var maxRowDepth = 1;
7624 // Calculate the max depth of descendants for this row
7625 var countMaxRowDepth = function(row, tmpRowDepth) {
7626 tmpRowDepth = tmpRowDepth || 1;
7628 for(var n=0; n<row.length; n++) {
7630 // Column has children, so keep counting
7631 if(YAHOO.lang.isArray(col.children)) {
7633 countMaxRowDepth(col.children, tmpRowDepth);
7636 // No children, is it the max depth?
7638 if(tmpRowDepth > maxRowDepth) {
7639 maxRowDepth = tmpRowDepth;
7646 // Count max row depth for each row
7647 for(var m=0; m<tree.length; m++) {
7648 currentRow = tree[m];
7649 countMaxRowDepth(currentRow);
7651 // Assign the right ROWSPAN values to each Column in the row
7652 for(var p=0; p<currentRow.length; p++) {
7653 currentColumn = currentRow[p];
7654 if(!YAHOO.lang.isArray(currentColumn.children)) {
7655 currentColumn._rowspan = maxRowDepth;
7658 currentColumn._rowspan = 1;
7662 // Reset counter for next row
7666 parseTreeForRowspan(tree);
7672 // Store header relationships in an array for HEADERS attribute
7673 var recurseAncestorsForHeaders = function(i, oColumn) {
7674 headers[i].push(oColumn._nId);
7675 if(oColumn.parent) {
7676 recurseAncestorsForHeaders(i, oColumn.parent);
7679 for(var i=0; i<keys.length; i++) {
7681 recurseAncestorsForHeaders(i, keys[i]);
7682 headers[i] = headers[i].reverse();
7683 headers[i] = headers[i].join(" ");
7686 // Save to the ColumnSet instance
7690 this.headers = headers;
7692 YAHOO.widget.ColumnSet._nCount++;
7695 /////////////////////////////////////////////////////////////////////////////
7697 // Public member variables
7699 /////////////////////////////////////////////////////////////////////////////
7702 * Internal class variable to index multiple data table instances.
7704 * @property ColumnSet._nCount
7709 YAHOO.widget.ColumnSet._nCount = 0;
7712 * Unique instance name.
7718 YAHOO.widget.ColumnSet.prototype._sName = null;
7720 /////////////////////////////////////////////////////////////////////////////
7722 // Public member variables
7724 /////////////////////////////////////////////////////////////////////////////
7727 * Top-down tree representation of Column hierarchy.
7730 * @type YAHOO.widget.Column[]
7732 YAHOO.widget.ColumnSet.prototype.tree = null;
7735 * Flattened representation of all Columns.
7738 * @type YAHOO.widget.Column[]
7741 YAHOO.widget.ColumnSet.prototype.flat = null;
7744 * Array of Columns that map one-to-one to a table column.
7747 * @type YAHOO.widget.Column[]
7750 YAHOO.widget.ColumnSet.prototype.keys = null;
7753 * ID index of nested parent hierarchies for HEADERS accessibility attribute.
7759 YAHOO.widget.ColumnSet.prototype.headers = null;
7761 /////////////////////////////////////////////////////////////////////////////
7765 /////////////////////////////////////////////////////////////////////////////
7768 * Public accessor to the unique name of the ColumnSet instance.
7771 * @return {String} Unique name of the ColumnSet instance.
7774 YAHOO.widget.ColumnSet.prototype.toString = function() {
7775 return "ColumnSet " + this._sName;
7779 * Returns Column instance with given ID number or key.
7782 * @param column {Number | String} ID number or unique key.
7783 * @return {YAHOO.widget.Column} Column instance.
7786 YAHOO.widget.ColumnSet.prototype.getColumn = function(column) {
7787 var allColumns = this.flat;
7788 if(YAHOO.lang.isNumber(column)) {
7789 for(var i=0; i<allColumns.length; i++) {
7790 if(allColumns[i]._nId === column) {
7791 return allColumns[i];
7795 else if(YAHOO.lang.isString(column)) {
7796 for(i=0; i<allColumns.length; i++) {
7797 if(allColumns[i].key === column) {
7798 return allColumns[i];
7805 /****************************************************************************/
7806 /****************************************************************************/
7807 /****************************************************************************/
7810 * The Column class defines and manages attributes of DataTable Columns
7812 * @namespace YAHOO.widget
7815 * @param oConfigs {Object} Object literal of configuration values.
7817 YAHOO.widget.Column = function(oConfigs) {
7818 // Internal variables
7819 this._nId = YAHOO.widget.Column._nCount;
7820 this._sName = "Column instance" + this._nId;
7822 // Object literal defines Column attributes
7823 if(oConfigs && (oConfigs.constructor == Object)) {
7824 for(var sConfig in oConfigs) {
7826 this[sConfig] = oConfigs[sConfig];
7831 if(!YAHOO.lang.isValue(this.key)) {
7832 this.key = "yui-dt-column"+this._nId;
7834 YAHOO.widget.Column._nCount++;
7837 /////////////////////////////////////////////////////////////////////////////
7839 // Private member variables
7841 /////////////////////////////////////////////////////////////////////////////
7844 * Internal instance counter.
7846 * @property Column._nCount
7852 YAHOO.widget.Column._nCount = 0;
7855 * Unique instance name.
7861 YAHOO.widget.Column.prototype._sName = null;
7865 * Unique number assigned at instantiation, indicates original order within
7872 YAHOO.widget.Column.prototype._nId = null;
7875 * Reference to Column's index within its ColumnSet's keys array, or null if not applicable.
7877 * @property _nKeyIndex
7881 YAHOO.widget.Column.prototype._nKeyIndex = null;
7884 * Number of table cells the Column spans.
7886 * @property _colspan
7890 YAHOO.widget.Column.prototype._colspan = 1;
7893 * Number of table rows the Column spans.
7895 * @property _rowspan
7899 YAHOO.widget.Column.prototype._rowspan = 1;
7902 * Column's parent Column instance, or null.
7905 * @type YAHOO.widget.Column
7908 YAHOO.widget.Column.prototype._parent = null;
7911 * Current offsetWidth of the Column (in pixels).
7917 YAHOO.widget.Column.prototype._width = null;
7920 * Minimum width the Column can support (in pixels). Value is populated only if table
7921 * is fixedWidth, null otherwise.
7923 * @property _minWidth
7927 YAHOO.widget.Column.prototype._minWidth = null;
7929 /////////////////////////////////////////////////////////////////////////////
7931 // Public member variables
7933 /////////////////////////////////////////////////////////////////////////////
7936 * Associated database field, or null.
7941 YAHOO.widget.Column.prototype.key = null;
7944 * Text or HTML for display as Column's label in the TH element.
7949 YAHOO.widget.Column.prototype.label = null;
7952 * Column head cell ABBR for accessibility.
7957 YAHOO.widget.Column.prototype.abbr = null;
7960 * Array of object literals that define children (nested headers) of a Column.
7962 * @property children
7965 YAHOO.widget.Column.prototype.children = null;
7973 YAHOO.widget.Column.prototype.width = null;
7976 * Custom CSS class or array of classes to be applied to every cell in the Column.
7978 * @property className
7979 * @type String || String[]
7981 YAHOO.widget.Column.prototype.className = null;
7984 * Defines a format function.
7986 * @property formatter
7987 * @type String || HTMLFunction
7989 YAHOO.widget.Column.prototype.formatter = null;
7992 * Defines an editor function, otherwise Column is not editable.
7995 * @type String || HTMLFunction
7997 YAHOO.widget.Column.prototype.editor = null;
8000 * Defines editor options for Column in an object literal of param:value pairs.
8002 * @property editorOptions
8005 YAHOO.widget.Column.prototype.editorOptions = null;
8008 * True if Column is resizeable, false otherwise.
8010 * @property resizeable
8014 YAHOO.widget.Column.prototype.resizeable = false;
8017 * True if Column is sortable, false otherwise.
8019 * @property sortable
8023 YAHOO.widget.Column.prototype.sortable = false;
8026 * Default sort order for Column: "asc" or "desc".
8028 * @property sortOptions.defaultOrder
8033 * Custom sort handler.
8035 * @property sortOptions.sortFunction
8039 YAHOO.widget.Column.prototype.sortOptions = null;
8055 /////////////////////////////////////////////////////////////////////////////
8059 /////////////////////////////////////////////////////////////////////////////
8062 * Public accessor to the unique name of the Column instance.
8065 * @return {String} Column's unique name.
8067 YAHOO.widget.Column.prototype.toString = function() {
8072 * Returns unique number assigned at instantiation, indicates original order
8076 * @return {Number} Column's unique ID number.
8078 YAHOO.widget.Column.prototype.getId = function() {
8083 * Public accessor returns Column's key index within its ColumnSet's keys array, or
8084 * null if not applicable.
8086 * @method getKeyIndex
8087 * @return {Number} Column's key index within its ColumnSet keys array, if applicable.
8089 YAHOO.widget.Column.prototype.getKeyIndex = function() {
8090 return this._nKeyIndex;
8094 * Public accessor returns Column's parent instance if any, or null otherwise.
8097 * @return {YAHOO.widget.Column} Column's parent instance.
8099 YAHOO.widget.Column.prototype.getParent = function() {
8100 return this._parent;
8104 * Public accessor returns Column's calculated COLSPAN value.
8106 * @method getColspan
8107 * @return {Number} Column's COLSPAN value.
8109 YAHOO.widget.Column.prototype.getColspan = function() {
8110 return this._colspan;
8112 // Backward compatibility
8113 YAHOO.widget.Column.prototype.getColSpan = function() {
8114 return this.getColspan();
8118 * Public accessor returns Column's calculated ROWSPAN value.
8120 * @method getRowspan
8121 * @return {Number} Column's ROWSPAN value.
8123 YAHOO.widget.Column.prototype.getRowspan = function() {
8124 return this._rowspan;
8127 // Backward compatibility
8128 YAHOO.widget.Column.prototype.getIndex = function() {
8129 return this.getKeyIndex();
8131 YAHOO.widget.Column.prototype.format = function() {
8133 YAHOO.widget.Column.formatCheckbox = function(elCell, oRecord, oColumn, oData) {
8134 YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
8136 YAHOO.widget.Column.formatCurrency = function(elCell, oRecord, oColumn, oData) {
8137 YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
8139 YAHOO.widget.Column.formatDate = function(elCell, oRecord, oColumn, oData) {
8140 YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
8142 YAHOO.widget.Column.formatEmail = function(elCell, oRecord, oColumn, oData) {
8143 YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
8145 YAHOO.widget.Column.formatLink = function(elCell, oRecord, oColumn, oData) {
8146 YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
8148 YAHOO.widget.Column.formatNumber = function(elCell, oRecord, oColumn, oData) {
8149 YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
8151 YAHOO.widget.Column.formatSelect = function(elCell, oRecord, oColumn, oData) {
8152 YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
8155 /****************************************************************************/
8156 /****************************************************************************/
8157 /****************************************************************************/
8160 * Sort static utility to support Column sorting.
8162 * @namespace YAHOO.util
8167 /////////////////////////////////////////////////////////////////////////////
8171 /////////////////////////////////////////////////////////////////////////////
8174 * Comparator function for simple case-insensitive string sorting.
8177 * @param a {Object} First sort argument.
8178 * @param b {Object} Second sort argument.
8179 * @param desc {Boolean} True if sort direction is descending, false if
8180 * sort direction is ascending.
8182 compare: function(a, b, desc) {
8183 if((a === null) || (typeof a == "undefined")) {
8184 if((b === null) || (typeof b == "undefined")) {
8191 else if((b === null) || (typeof b == "undefined")) {
8195 if(a.constructor == String) {
8196 a = a.toLowerCase();
8198 if(b.constructor == String) {
8199 b = b.toLowerCase();
8202 return (desc) ? 1 : -1;
8205 return (desc) ? -1 : 1;
8213 /****************************************************************************/
8214 /****************************************************************************/
8215 /****************************************************************************/
8218 * ColumnResizer subclasses DragDrop to support resizeable Columns.
8220 * @namespace YAHOO.util
8221 * @class ColumnResizer
8222 * @extends YAHOO.util.DragDrop
8224 * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
8225 * @param oColumn {YAHOO.widget.Column} Column instance.
8226 * @param elThead {HTMLElement} TH element reference.
8227 * @param sHandleElId {String} DOM ID of the handle element that causes the resize.
8228 * @param sGroup {String} Group name of related DragDrop items.
8229 * @param oConfig {Object} (Optional) Object literal of config values.
8231 YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elThead, sHandleId, sGroup, oConfig) {
8232 if(oDataTable && oColumn && elThead && sHandleId) {
8233 this.datatable = oDataTable;
8234 this.column = oColumn;
8235 this.cell = elThead;
8236 this.init(sHandleId, sGroup, oConfig);
8238 this.setYConstraint(0,0);
8245 YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DD);
8248 /////////////////////////////////////////////////////////////////////////////
8250 // Public DOM event handlers
8252 /////////////////////////////////////////////////////////////////////////////
8255 * Handles mousedown events on the Column resizer.
8257 * @method onMouseDown
8258 * @param e {string} The mousedown event
8260 YAHOO.util.ColumnResizer.prototype.onMouseDown = function(e) {
8261 this.startWidth = this.cell.offsetWidth;
8262 this.startPos = YAHOO.util.Dom.getX(this.getDragEl());
8264 if(this.datatable.fixedWidth) {
8265 var cellLabel = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_LABEL,"span",this.cell)[0];
8266 this.minWidth = cellLabel.offsetWidth + 6;
8267 var sib = this.cell.nextSibling;
8268 var sibCellLabel = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.DataTable.CLASS_LABEL,"span",sib)[0];
8269 this.sibMinWidth = sibCellLabel.offsetWidth + 6;
8271 var left = ((this.startWidth - this.minWidth) < 0) ? 0 : (this.startWidth - this.minWidth);
8272 var right = ((sib.offsetWidth - this.sibMinWidth) < 0) ? 0 : (sib.offsetWidth - this.sibMinWidth);
8273 this.setXConstraint(left, right);
8279 * Handles mouseup events on the Column resizer.
8282 * @param e {string} The mouseup event
8284 YAHOO.util.ColumnResizer.prototype.onMouseUp = function(e) {
8285 //TODO: replace the resizer where it belongs:
8286 var resizeStyle = YAHOO.util.Dom.get(this.handleElId).style;
8287 resizeStyle.left = "auto";
8288 resizeStyle.right = 0;
8289 resizeStyle.marginRight = "-6px";
8290 resizeStyle.width = "6px";
8291 //.yui-dt-headresizer {position:absolute;margin-right:-6px;right:0;bottom:0;width:6px;height:100%;cursor:w-resize;cursor:col-resize;}
8294 //var cells = this.datatable._elTable.tHead.rows[this.datatable._elTable.tHead.rows.length-1].cells;
8295 //for(var i=0; i<cells.length; i++) {
8296 //cells[i].style.width = "5px";
8299 //TODO: set new ColumnSet width values
8300 this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.cell});
8304 * Handles drag events on the Column resizer.
8307 * @param e {string} The drag event
8309 YAHOO.util.ColumnResizer.prototype.onDrag = function(e) {
8310 var newPos = YAHOO.util.Dom.getX(this.getDragEl());
8311 var offsetX = newPos - this.startPos;
8312 var newWidth = this.startWidth + offsetX;
8314 if(newWidth < this.minWidth) {
8315 newWidth = this.minWidth;
8318 // Resize the Column
8319 var oDataTable = this.datatable;
8320 var elCell = this.cell;
8323 // Resize the other Columns
8324 if(oDataTable.fixedWidth) {
8325 // Moving right or left?
8326 var sib = elCell.nextSibling;
8327 //var sibIndex = elCell.index + 1;
8328 var sibnewwidth = sib.offsetWidth - offsetX;
8329 if(sibnewwidth < this.sibMinWidth) {
8330 sibnewwidth = this.sibMinWidth;
8333 //TODO: how else to cycle through all the Columns without having to use an index property?
8334 for(var i=0; i<oDataTable._oColumnSet.length; i++) {
8335 //if((i != elCell.index) && (i!=sibIndex)) {
8336 // YAHOO.util.Dom.get(oDataTable._oColumnSet.keys[i].id).style.width = oDataTable._oColumnSet.keys[i].width + "px";
8339 sib.style.width = sibnewwidth;
8340 elCell.style.width = newWidth + "px";
8341 //oDataTable._oColumnSet.flat[sibIndex].width = sibnewwidth;
8342 //oDataTable._oColumnSet.flat[elCell.index].width = newWidth;
8346 elCell.style.width = newWidth + "px";
8353 /****************************************************************************/
8354 /****************************************************************************/
8355 /****************************************************************************/
8358 * A RecordSet defines and manages a set of Records.
8360 * @namespace YAHOO.widget
8362 * @param data {Object || Object[]} An object literal or an array of data.
8365 YAHOO.widget.RecordSet = function(data) {
8366 // Internal variables
8367 this._sName = "RecordSet instance" + YAHOO.widget.RecordSet._nCount;
8368 YAHOO.widget.RecordSet._nCount++;
8373 if(YAHOO.lang.isArray(data)) {
8374 this.addRecords(data);
8376 else if(data.constructor == Object) {
8377 this.addRecord(data);
8382 * Fired when a new Record is added to the RecordSet.
8384 * @event recordAddEvent
8385 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
8386 * @param oArgs.data {Object} Data added.
8388 this.createEvent("recordAddEvent");
8391 * Fired when multiple Records are added to the RecordSet at once.
8393 * @event recordsAddEvent
8394 * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
8395 * @param oArgs.data {Object[]} Data added.
8397 this.createEvent("recordsAddEvent");
8400 * Fired when a Record is updated with new data.
8402 * @event recordUpdateEvent
8403 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
8404 * @param oArgs.newData {Object} New data.
8405 * @param oArgs.oldData {Object} Old data.
8407 this.createEvent("recordUpdateEvent");
8410 * Fired when a Record is deleted from the RecordSet.
8412 * @event recordDeleteEvent
8413 * @param oArgs.data {Object} A copy of the data held by the Record,
8414 * or an array of data object literals if multiple Records were deleted at once.
8415 * @param oArgs.index {Object} Index of the deleted Record.
8417 this.createEvent("recordDeleteEvent");
8420 * Fired when multiple Records are deleted from the RecordSet at once.
8422 * @event recordsDeleteEvent
8423 * @param oArgs.data {Object[]} An array of data object literals copied
8425 * @param oArgs.index {Object} Index of the first deleted Record.
8427 this.createEvent("recordsDeleteEvent");
8430 * Fired when all Records are deleted from the RecordSet at once.
8434 this.createEvent("resetEvent");
8437 * Fired when a Record Key is updated with new data.
8439 * @event keyUpdateEvent
8440 * @param oArgs.record {YAHOO.widget.Record} The Record instance.
8441 * @param oArgs.key {String} The updated key.
8442 * @param oArgs.newData {Object} New data.
8443 * @param oArgs.oldData {Object} Old data.
8446 this.createEvent("keyUpdateEvent");
8450 if(YAHOO.util.EventProvider) {
8451 YAHOO.augment(YAHOO.widget.RecordSet, YAHOO.util.EventProvider);
8456 /////////////////////////////////////////////////////////////////////////////
8458 // Private member variables
8460 /////////////////////////////////////////////////////////////////////////////
8462 * Internal class variable to name multiple Recordset instances.
8464 * @property RecordSet._nCount
8469 YAHOO.widget.RecordSet._nCount = 0;
8472 * Unique instance name.
8478 YAHOO.widget.RecordSet.prototype._sName = null;
8481 * Internal variable to give unique indexes to Record instances.
8487 YAHOO.widget.RecordSet.prototype._nRecordCount = 0;
8490 * Internal counter of how many Records are in the RecordSet.
8496 YAHOO.widget.RecordSet.prototype._length = null;
8498 /////////////////////////////////////////////////////////////////////////////
8502 /////////////////////////////////////////////////////////////////////////////
8505 * Adds one Record to the RecordSet at the given index. If index is null,
8506 * then adds the Record to the end of the RecordSet.
8508 * @method _addRecord
8509 * @param oData {Object} An object literal of data.
8510 * @param index {Number} (optional) Position index.
8511 * @return {YAHOO.widget.Record} A Record instance.
8514 YAHOO.widget.RecordSet.prototype._addRecord = function(oData, index) {
8515 var oRecord = new YAHOO.widget.Record(oData);
8516 oRecord._nId = this._nRecordCount;
8517 this._nRecordCount++;
8519 if(YAHOO.lang.isNumber(index) && (index > -1)) {
8520 this._records.splice(index,0,oRecord);
8523 index = this.getLength();
8524 this._records.push(oRecord);
8531 * Deletes Records from the RecordSet at the given index. If range is null,
8532 * then only one Record is deleted.
8534 * @method _deleteRecord
8535 * @param index {Number} Position index.
8536 * @param range {Number} (optional) How many Records to delete
8539 YAHOO.widget.RecordSet.prototype._deleteRecord = function(index, range) {
8540 if(!YAHOO.lang.isNumber(range) || (range < 0)) {
8543 this._records.splice(index, range);
8544 this._length = this._length - range;
8547 /////////////////////////////////////////////////////////////////////////////
8551 /////////////////////////////////////////////////////////////////////////////
8554 * Public accessor to the unique name of the RecordSet instance.
8557 * @return {String} Unique name of the RecordSet instance.
8559 YAHOO.widget.RecordSet.prototype.toString = function() {
8564 * Returns the number of Records held in the RecordSet.
8567 * @return {Number} Number of records in the RecordSet.
8569 YAHOO.widget.RecordSet.prototype.getLength = function() {
8570 return this._length;
8574 * Returns Record at given position index.
8577 * @param index {Number} Record's Recordset position index.
8578 * @return {YAHOO.widget.Record} Record object.
8580 YAHOO.widget.RecordSet.prototype.getRecord = function(index) {
8581 if(YAHOO.lang.isNumber(index)) {
8582 return this._records[index];
8584 /*else if(YAHOO.lang.isString(identifier)) {
8585 for(var i=0; i<this._records.length; i++) {
8586 if(this._records[i].yuiRecordId == identifier) {
8587 return this._records[i];
8596 * Returns an array of Records from the RecordSet.
8598 * @method getRecords
8599 * @param index {Number} (optional) Recordset position index of which Record to
8601 * @param range {Number} (optional) Number of Records to get.
8602 * @return {YAHOO.widget.Record[]} Array of Records starting at given index and
8603 * length equal to given range. If index is not given, all Records are returned.
8605 YAHOO.widget.RecordSet.prototype.getRecords = function(index, range) {
8606 if(!YAHOO.lang.isNumber(index)) {
8607 return this._records;
8609 if(!YAHOO.lang.isNumber(range)) {
8610 return this._records.slice(index);
8612 return this._records.slice(index, index+range);
8616 * Returns position index for the given Record.
8618 * @method getRecordIndex
8619 * @param oRecord {YAHOO.widget.Record} Record instance.
8620 * @return {Number} Record's RecordSet position index.
8623 YAHOO.widget.RecordSet.prototype.getRecordIndex = function(oRecord) {
8624 for(var i=this._records.length-1; i>-1; i--) {
8625 if(oRecord.getId() === this._records[i].getId()) {
8634 * Adds one Record to the RecordSet at the given index. If index is null,
8635 * then adds the Record to the end of the RecordSet.
8638 * @param oData {Object} An object literal of data.
8639 * @param index {Number} (optional) Position index.
8640 * @return {YAHOO.widget.Record} A Record instance.
8642 YAHOO.widget.RecordSet.prototype.addRecord = function(oData, index) {
8643 if(oData && (oData.constructor == Object)) {
8644 var oRecord = this._addRecord(oData, index);
8645 this.fireEvent("recordAddEvent",{record:oRecord,data:oData});
8654 * Adds multiple Records at once to the RecordSet at the given index with the
8655 * given data. If index is null, then the new Records are added to the end of
8658 * @method addRecords
8659 * @param aData {Object[]} An array of object literal data.
8660 * @param index {Number} (optional) Position index.
8661 * @return {YAHOO.widget.Record[]} An array of Record instances.
8663 YAHOO.widget.RecordSet.prototype.addRecords = function(aData, index) {
8664 if(YAHOO.lang.isArray(aData)) {
8665 var newRecords = [];
8666 // Can't go backwards bc we need to preserve order
8667 for(var i=0; i<aData.length; i++) {
8668 if(aData[i] && (aData[i].constructor == Object)) {
8669 var record = this._addRecord(aData[i], index);
8670 newRecords.push(record);
8673 this.fireEvent("recordsAddEvent",{records:newRecords,data:aData});
8676 else if(aData && (aData.constructor == Object)) {
8677 var oRecord = this._addRecord(aData);
8678 this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
8686 * Updates given Record with given data.
8688 * @method updateRecord
8689 * @param record {YAHOO.widget.Record | Number} A Record instance, or Record's
8690 * RecordSet position index.
8691 * @param oData {Object) Object literal of new data.
8692 * @return {YAHOO.widget.Record} Updated Record, or null.
8694 YAHOO.widget.RecordSet.prototype.updateRecord = function(record, oData) {
8696 if(YAHOO.lang.isNumber(record)) {
8697 oRecord = this._records[record];
8699 else if(record instanceof YAHOO.widget.Record) {
8702 if(oRecord && oData && (oData.constructor == Object)) {
8703 // Copy data from the Record for the event that gets fired later
8705 for(var key in oRecord._oData) {
8706 oldData[key] = oRecord._oData[key];
8708 oRecord._oData = oData;
8709 this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData});
8718 * Updates given Record at given key with given data.
8721 * @param record {YAHOO.widget.Record | Number} A Record instance, or Record's
8722 * RecordSet position index.
8723 * @param sKey {String} Key name.
8724 * @param oData {Object) New data.
8726 YAHOO.widget.RecordSet.prototype.updateKey = function(record, sKey, oData) {
8729 if(YAHOO.lang.isNumber(record)) {
8730 oRecord = this._records[record];
8732 if(record instanceof YAHOO.widget.Record) {
8736 var keyValue = oRecord._oData[sKey];
8737 // Copy data from the Record for the event that gets fired later
8738 if(keyValue && keyValue.constructor == Object) {
8740 for(var key in keyValue) {
8741 oldData[key] = keyValue[key];
8749 oRecord._oData[sKey] = oData;
8750 this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
8757 * Replaces all Records in RecordSet with new data.
8759 * @method replaceRecords
8760 * @param data {Object || Object[]} An object literal of data or an array of
8761 * object literal data.
8762 * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or
8763 * an array of Records.
8765 YAHOO.widget.RecordSet.prototype.replaceRecords = function(data) {
8767 return this.addRecords(data);
8771 * Sorts all Records by given function.
8773 * @method sortRecords
8774 * @param fnSort {Function} Reference to a sort function.
8775 * @param desc {Boolean} True if sort direction is descending, false if sort
8776 * direction is ascending.
8777 * @return {YAHOO.widget.Record[]} Sorted array of Records.
8779 YAHOO.widget.RecordSet.prototype.sortRecords = function(fnSort, desc) {
8780 return this._records.sort(function(a, b) {return fnSort(a, b, desc);});
8785 * Removes the Record at the given position index from the RecordSet. If a range
8786 * is also provided, removes that many Records, starting from the index. Length
8787 * of RecordSet is correspondingly shortened.
8789 * @method deleteRecord
8790 * @param index {Number} Record's RecordSet position index.
8791 * @param range {Number} (optional) How many Records to delete.
8792 * @return {Object} A copy of the data held by the deleted Record.
8794 YAHOO.widget.RecordSet.prototype.deleteRecord = function(index) {
8795 if(YAHOO.lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
8796 // Copy data from the Record for the event that gets fired later
8797 var oRecordData = this.getRecord(index).getData();
8799 for(var key in oRecordData) {
8800 oData[key] = oRecordData[key];
8803 this._deleteRecord(index);
8804 this.fireEvent("recordDeleteEvent",{data:oData,index:index});
8813 * Removes the Record at the given position index from the RecordSet. If a range
8814 * is also provided, removes that many Records, starting from the index. Length
8815 * of RecordSet is correspondingly shortened.
8817 * @method deleteRecords
8818 * @param index {Number} Record's RecordSet position index.
8819 * @param range {Number} (optional) How many Records to delete.
8821 YAHOO.widget.RecordSet.prototype.deleteRecords = function(index, range) {
8822 if(!YAHOO.lang.isNumber(range)) {
8825 if(YAHOO.lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
8826 var recordsToDelete = this.getRecords(index, range);
8827 // Copy data from each Record for the event that gets fired later
8828 var deletedData = [];
8829 for(var i=0; i<recordsToDelete.length; i++) {
8831 for(var key in recordsToDelete[i]) {
8832 oData[key] = recordsToDelete[i][key];
8834 deletedData.push(oData);
8836 this._deleteRecord(index, range);
8838 this.fireEvent("recordsDeleteEvent",{data:deletedData,index:index});
8846 * Deletes all Records from the RecordSet.
8850 YAHOO.widget.RecordSet.prototype.reset = function() {
8853 this.fireEvent("resetEvent");
8857 /****************************************************************************/
8858 /****************************************************************************/
8859 /****************************************************************************/
8862 * The Record class defines a DataTable record.
8864 * @namespace YAHOO.widget
8867 * @param oConfigs {Object} (optional) Object literal of key/value pairs.
8869 YAHOO.widget.Record = function(oLiteral) {
8871 if(oLiteral && (oLiteral.constructor == Object)) {
8872 for(var sKey in oLiteral) {
8873 this._oData[sKey] = oLiteral[sKey];
8878 /////////////////////////////////////////////////////////////////////////////
8880 // Private member variables
8882 /////////////////////////////////////////////////////////////////////////////
8884 * Unique number assigned at instantiation, indicates original order within
8891 YAHOO.widget.Record.prototype._nId = null;
8894 * Holds data for the Record in an object literal.
8900 YAHOO.widget.Record.prototype._oData = null;
8902 /////////////////////////////////////////////////////////////////////////////
8904 // Public member variables
8906 /////////////////////////////////////////////////////////////////////////////
8908 /////////////////////////////////////////////////////////////////////////////
8912 /////////////////////////////////////////////////////////////////////////////
8915 * Returns unique number assigned at instantiation, indicates original order
8921 YAHOO.widget.Record.prototype.getId = function() {
8926 * Returns data for the Record for a key if given, or the entire object
8927 * literal otherwise.
8930 * @param sKey {String} (Optional) The key to retrieve a single data value.
8933 YAHOO.widget.Record.prototype.getData = function(sKey) {
8934 if(YAHOO.lang.isString(sKey)) {
8935 return this._oData[sKey];
8943 YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.3.0", build: "442"});