Deleting the check_defaults test from 19_STABLE. MDL-14854
[moodle-linuxchix.git] / lib / yui / datatable / datatable-beta.js
blobb56afe45a1c21d8462748e609c8cef7057d1d0ad
1 /*
2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.3.0
6 */
7 /**
8 * The DataTable widget provides a progressively enhanced DHTML control for
9 * displaying tabular data across A-grade browsers.
11 * @module datatable
12 * @requires yahoo, dom, event, datasource
13 * @optional dragdrop
14 * @title DataTable Widget
15 * @beta
18 /****************************************************************************/
19 /****************************************************************************/
20 /****************************************************************************/
22 /**
23 * DataTable class for the YUI DataTable widget.
25 * @namespace YAHOO.widget
26 * @class DataTable
27 * @uses YAHOO.util.EventProvider
28 * @constructor
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) {
35 // Internal vars
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) {
43 return;
46 // Initialize configs
47 this._initConfigs(oConfigs);
49 // Initialize ColumnSet
50 this._initColumnSet(aColumnDefs);
51 if(!this._oColumnSet) {
52 return;
55 // Initialize RecordSet
56 this._initRecordSet();
57 if(!this._oRecordSet) {
58 return;
61 // Initialize DataSource
62 this._initDataSource(oDataSource);
63 if(!this._oDataSource) {
64 return;
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);
71 else {
72 // Initialize DOM elements
73 this._initTableEl();
74 if(!this._elTable || !this._elThead || !this._elTbody) {
75 return;
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);
106 else {
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.
120 * @private
123 YAHOO.widget.DataTable.prototype.initAttributes = function(oConfigs) {
124 oConfigs = oConfigs || {};
125 YAHOO.widget.DataTable.superclass.initAttributes.call(this, oConfigs);
128 * @config summary
129 * @description Value for the SUMMARY attribute.
130 * @type String
132 this.setAttributeConfig("summary", {
133 value: null,
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:
143 * <dl>
144 * <dt>"standard"</dt>
145 * <dd>Standard row selection with support for modifier keys to enable
146 * multiple selections.</dd>
148 * <dt>"single"</dt>
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>
163 * </dl>
165 * @default "standard"
166 * @type String
168 this.setAttributeConfig("selectionMode", {
169 value: "standard",
170 validator: YAHOO.lang.isString
174 * @config initialRequest
175 * @description Defines the initial request that gets sent to the DataSource.
176 * @type String
178 this.setAttributeConfig("initialRequest", {
179 value: "",
180 validator: YAHOO.lang.isString
184 * @config sortedBy
185 * @description Object literal provides metadata for initial sort values if
186 * data will arrive pre-sorted:
187 * <dl>
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>
192 * </dl>
193 * @type Object
195 this.setAttributeConfig("sortedBy", {
196 value: null,
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);
213 if(column) {
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);
223 * @config paginator
224 * @description Object literal of pagination values.
225 * @default <br>
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
235 * @type Object
237 this.setAttributeConfig("paginator", {
238 value: {
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)) {
281 return true;
285 return false;
290 * @config paginated
291 * @description True if built-in client-side pagination is enabled
292 * @default false
293 * @type Boolean
295 this.setAttributeConfig("paginated", {
296 value: false,
297 validator: YAHOO.lang.isBoolean,
298 method: function(oParam) {
299 var oPaginator = this.get("paginator");
300 var aContainerEls = oPaginator.containers;
302 // Paginator is enabled
303 if(oParam) {
304 // No containers found, create two from scratch
305 if(aContainerEls.length === 0) {
306 // One before TABLE
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);
313 // One after TABLE
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];
324 else {
325 // Show each container
326 for(var i=0; i<aContainerEls.length; i++) {
327 aContainerEls[i].style.display = "";
331 // Links are enabled
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
343 //TODO: anon fnc
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
363 //TODO: anon fnc
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);
369 // Hide dropdown
370 if(!oPaginator.dropdownOptions) {
371 selectEl.style.display = "none";
375 //TODO: fire paginatorDisabledEvent & add to api doc
377 // Pagination is disabled
378 else {
379 // Containers found
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";
388 /*TODO?
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
404 * @config caption
405 * @description Value for the CAPTION element.
406 * @type String
408 this.setAttributeConfig("caption", {
409 value: null,
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"));
417 else {
418 this._elCaption = this._elTable.insertBefore(document.createElement("caption"), this._elTable.firstChild);
421 // Set CAPTION value
422 this._elCaption.innerHTML = sCaption;
427 * @config scrollable
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.
431 * @default false
432 * @type Boolean
434 this.setAttributeConfig("scrollable", {
435 value: false,
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) {
443 if(oParam) {
444 //TODO: conf height
445 YAHOO.util.Dom.addClass(this._elContainer,YAHOO.widget.DataTable.CLASS_SCROLLABLE);
446 YAHOO.util.Dom.addClass(this._elTbody,YAHOO.widget.DataTable.CLASS_SCROLLBODY);
448 else {
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 /////////////////////////////////////////////////////////////////////////////
459 // Public constants
461 /////////////////////////////////////////////////////////////////////////////
464 * Class name assigned to TABLE element.
466 * @property DataTable.CLASS_TABLE
467 * @type String
468 * @static
469 * @final
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
478 * @type String
479 * @static
480 * @final
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
489 * @type String
490 * @static
491 * @final
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
500 * @type String
501 * @static
502 * @final
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
511 * @type String
512 * @static
513 * @final
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
522 * @type String
523 * @static
524 * @final
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
533 * @type String
534 * @static
535 * @final
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
544 * @type String
545 * @static
546 * @final
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
555 * @type String
556 * @static
557 * @final
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
566 * @type String
567 * @static
568 * @final
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
577 * @type String
578 * @static
579 * @final
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
588 * @type String
589 * @static
590 * @final
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
599 * @type String
600 * @static
601 * @final
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
610 * @type String
611 * @static
612 * @final
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
621 * @type String
622 * @static
623 * @final
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
632 * @type String
633 * @static
634 * @final
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
643 * @type String
644 * @static
645 * @final
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
654 * @type String
655 * @static
656 * @final
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
665 * @type String
666 * @static
667 * @final
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
676 * @type String
677 * @static
678 * @final
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
687 * @type String
688 * @static
689 * @final
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
698 * @type String
699 * @static
700 * @final
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
709 * @type String
710 * @static
711 * @final
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
720 * @type String
721 * @static
722 * @final
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
731 * @type String
732 * @static
733 * @final
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
742 * @type String
743 * @static
744 * @final
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
753 * @type String
754 * @static
755 * @final
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
764 * @type String
765 * @static
766 * @final
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
775 * @type String
776 * @static
777 * @final
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
786 * @type String
787 * @static
788 * @final
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
797 * @type String
798 * @static
799 * @final
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
808 * @type String
809 * @static
810 * @final
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
819 * @type String
820 * @static
821 * @final
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
836 * @type Number
837 * @private
838 * @static
840 YAHOO.widget.DataTable._nCount = 0;
843 * Index assigned to instance.
845 * @property _nIndex
846 * @type Number
847 * @private
849 YAHOO.widget.DataTable.prototype._nIndex = null;
852 * Counter for IDs assigned to TR elements.
854 * @property _nTrCount
855 * @type Number
856 * @private
858 YAHOO.widget.DataTable.prototype._nTrCount = 0;
861 * Unique name assigned to instance.
863 * @property _sName
864 * @type String
865 * @private
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
874 * @type HTMLElement
875 * @private
877 YAHOO.widget.DataTable.prototype._elContainer = null;
880 * DOM reference to the CAPTION element for the DataTable instance.
882 * @property _elCaption
883 * @type HTMLElement
884 * @private
886 YAHOO.widget.DataTable.prototype._elCaption = null;
889 * DOM reference to the TABLE element for the DataTable instance.
891 * @property _elTable
892 * @type HTMLElement
893 * @private
895 YAHOO.widget.DataTable.prototype._elTable = null;
898 * DOM reference to the THEAD element for the DataTable instance.
900 * @property _elThead
901 * @type HTMLElement
902 * @private
904 YAHOO.widget.DataTable.prototype._elThead = null;
907 * DOM reference to the primary TBODY element for the DataTable instance.
909 * @property _elTbody
910 * @type HTMLElement
911 * @private
913 YAHOO.widget.DataTable.prototype._elTbody = null;
916 * DOM reference to the secondary TBODY element used to display DataTable messages.
918 * @property _elMsgTbody
919 * @type HTMLElement
920 * @private
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
928 * @type HTMLElement
929 * @private
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
937 * @type HTMLElement
938 * @private
940 YAHOO.widget.DataTable.prototype._elMsgTbodyCell = null;
943 * DataSource instance for the DataTable instance.
945 * @property _oDataSource
946 * @type YAHOO.util.DataSource
947 * @private
949 YAHOO.widget.DataTable.prototype._oDataSource = null;
952 * ColumnSet instance for the DataTable instance.
954 * @property _oColumnSet
955 * @type YAHOO.widget.ColumnSet
956 * @private
958 YAHOO.widget.DataTable.prototype._oColumnSet = null;
961 * RecordSet instance for the DataTable instance.
963 * @property _oRecordSet
964 * @type YAHOO.widget.RecordSet
965 * @private
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
974 * @type String
975 * @private
977 YAHOO.widget.DataTable.prototype._sFirstLabelLinkId = null;
980 * ID string of first TR element of the current DataTable page.
982 * @property _sFirstTrId
983 * @type String
984 * @private
986 YAHOO.widget.DataTable.prototype._sFirstTrId = null;
989 * ID string of the last TR element of the current DataTable page.
991 * @property _sLastTrId
992 * @type String
993 * @private
995 YAHOO.widget.DataTable.prototype._sLastTrId = null;
1028 /////////////////////////////////////////////////////////////////////////////
1030 // Private methods
1032 /////////////////////////////////////////////////////////////////////////////
1035 * Sets focus on the given element.
1037 * @method _focusEl
1038 * @param el {HTMLElement} Element.
1039 * @private
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);
1053 // INIT FUNCTIONS
1056 * Initializes container element.
1058 * @method _initContainerEl
1059 * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
1060 * @private
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.
1075 * @private
1077 YAHOO.widget.DataTable.prototype._initConfigs = function(oConfigs) {
1078 if(oConfigs) {
1079 if(oConfigs.constructor != Object) {
1080 oConfigs = null;
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.
1094 * @private
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.
1112 * @private
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
1120 else {
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];
1129 break;
1132 if(tmpTable) {
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
1150 * @private
1152 YAHOO.widget.DataTable.prototype._initRecordSet = function() {
1153 if(this._oRecordSet) {
1154 this._oRecordSet.reset();
1156 else {
1157 this._oRecordSet = new YAHOO.widget.RecordSet();
1162 * Creates HTML markup for TABLE, THEAD and TBODY elements.
1164 * @method _initTableEl
1165 * @private
1167 YAHOO.widget.DataTable.prototype._initTableEl = function() {
1168 // Clear the container
1169 YAHOO.util.Event.purgeElement(this._elContainer, true);
1170 this._elContainer.innerHTML = "";
1172 // Create TABLE
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);
1179 // Create THEAD
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
1206 * @private
1208 YAHOO.widget.DataTable.prototype._initTheadEl = function() {
1209 var i,oColumn, colId;
1210 var oColumnSet = this._oColumnSet;
1211 this._sFirstLabelLinkId = null;
1213 // Create THEAD
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;
1222 var elTheadCell;
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
1233 if(i === 0) {
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;
1255 var needDD = 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) {
1261 if(foundDD) {
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";
1286 else {
1287 needDD = true;
1291 if(needDD) {
1297 * Populates TH cell as defined by Column.
1299 * @method _initThEl
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.
1304 * @private
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;
1312 if(oColumn.abbr) {
1313 elTheadCell.abbr = oColumn.abbr;
1315 if(oColumn.width) {
1316 elTheadCell.style.width = oColumn.width;
1319 var aCustomClasses;
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;
1359 else {
1360 elTheadLabel.innerHTML = sLabel;
1365 * Creates HTML markup for Cell Editor.
1367 * @method _initCellEditorEl
1368 * @private
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;
1385 // Handle ESC key
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
1401 * @private
1403 YAHOO.widget.DataTable.prototype._initColumnSort = function() {
1404 this.subscribe("headerCellClickEvent", this.onEventSortColumn);
1408 * Initializes DOM event listeners.
1410 * @method _initDomEvents
1411 * @private
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.
1484 * @method _addTrEl
1485 * @param oRecord {YAHOO.widget.Record} Record instance.
1486 * @param index {Number} (optional) The page row index at which to add the TR
1487 * element.
1488 * @return {String} ID of the added TR element, or null.
1489 * @private
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;
1503 if(isSortedBy) {
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;
1518 this._nTrCount++;
1519 elRow.yuiRecordId = oRecord.getId();
1521 // Create TD cells
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;
1531 // Update UI
1532 this.formatCell(elCell, oRecord, oColumn);
1534 // Set FIRST/LAST on TD
1535 if (j === 0) {
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);
1542 // Remove ASC/DESC
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";
1571 return elRow.id;
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.
1581 * @private
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;
1589 if(isSortedBy) {
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);
1604 // Remove ASC/DESC
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);
1614 // Update Record ID
1615 elRow.yuiRecordId = oRecord.getId();
1617 return elRow.id;
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.
1627 * @private
1629 YAHOO.widget.DataTable.prototype._deleteTrEl = function(row) {
1630 var rowIndex;
1632 // Get page row index for the element
1633 if(!YAHOO.lang.isNumber(row)) {
1634 rowIndex = YAHOO.util.Dom.get(row).sectionRowIndex;
1636 else {
1637 rowIndex = row;
1639 if(YAHOO.lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
1640 this._elTbody.deleteRow(rowIndex);
1641 return true;
1643 else {
1644 return false;
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
1684 * @private
1686 YAHOO.widget.DataTable.prototype._setFirstRow = function() {
1687 var rowEl = this.getFirstTrEl();
1688 if(rowEl) {
1689 // Remove FIRST
1690 if(this._sFirstTrId) {
1691 YAHOO.util.Dom.removeClass(this._sFirstTrId, YAHOO.widget.DataTable.CLASS_FIRST);
1693 // Set FIRST
1694 YAHOO.util.Dom.addClass(rowEl, YAHOO.widget.DataTable.CLASS_FIRST);
1695 this._sFirstTrId = rowEl.id;
1697 else {
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
1707 * @private
1709 YAHOO.widget.DataTable.prototype._setLastRow = function() {
1710 var rowEl = this.getLastTrEl();
1711 if(rowEl) {
1712 // Unassign previous class
1713 if(this._sLastTrId) {
1714 YAHOO.util.Dom.removeClass(this._sLastTrId, YAHOO.widget.DataTable.CLASS_LAST);
1716 // Assign class
1717 YAHOO.util.Dom.addClass(rowEl, YAHOO.widget.DataTable.CLASS_LAST);
1718 this._sLastTrId = rowEl.id;
1720 else {
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.
1735 * @private
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;
1743 // Stripe a subset
1744 if((row !== null) && (row !== undefined)) {
1745 // Validate given start row
1746 var elStartRow = this.getTrEl(row);
1747 if(elStartRow) {
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++) {
1758 if(i%2) {
1759 YAHOO.util.Dom.removeClass(allRows[i], YAHOO.widget.DataTable.CLASS_EVEN);
1760 YAHOO.util.Dom.addClass(allRows[i], YAHOO.widget.DataTable.CLASS_ODD);
1762 else {
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).
1822 * @method _onScroll
1823 * @param e {HTMLEvent} The scroll event.
1824 * @param oSelf {YAHOO.widget.DataTable} DataTable instance.
1825 * @private
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.
1845 * @private
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.
1874 * @private
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.
1892 * @private
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.
1904 * @private
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")) {
1911 switch(elTag) {
1912 case "body":
1913 break;
1914 case "a":
1915 break;
1916 case "td":
1917 oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
1918 break;
1919 case "span":
1920 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
1921 oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
1923 break;
1924 case "th":
1925 oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
1926 break;
1927 case "tr":
1928 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
1929 oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
1931 else {
1932 oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
1934 break;
1935 default:
1936 break;
1938 elTarget = elTarget.parentNode;
1939 if(elTarget) {
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.
1952 * @private
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")) {
1959 switch(elTag) {
1960 case "body":
1961 break;
1962 case "a":
1963 break;
1964 case "td":
1965 oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
1966 break;
1967 case "span":
1968 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
1969 oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
1971 break;
1972 case "th":
1973 oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
1974 break;
1975 case "tr":
1976 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
1977 oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
1979 else {
1980 oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
1982 break;
1983 default:
1984 break;
1986 elTarget = elTarget.parentNode;
1987 if(elTarget) {
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.
2000 * @private
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")) {
2007 switch(elTag) {
2008 case "body":
2009 break;
2010 case "a":
2011 break;
2012 case "td":
2013 oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
2014 break;
2015 case "span":
2016 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
2017 oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
2019 break;
2020 case "th":
2021 oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
2022 break;
2023 case "tr":
2024 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
2025 oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
2027 else {
2028 oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
2030 break;
2031 default:
2032 break;
2034 elTarget = elTarget.parentNode;
2035 if(elTarget) {
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.
2048 * @private
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")) {
2055 switch(elTag) {
2056 case "body":
2057 break;
2058 case "td":
2059 oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
2060 break;
2061 case "span":
2062 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
2063 oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
2065 break;
2066 case "th":
2067 oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
2068 break;
2069 case "tr":
2070 if(elTarget.parentNode.tagName.toLowerCase() == "thead") {
2071 oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
2073 else {
2074 oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
2076 break;
2077 default:
2078 break;
2080 elTarget = elTarget.parentNode;
2081 if(elTarget) {
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.
2094 * @private
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)) {
2102 return;
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));
2113 return;
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);
2126 else {
2127 return;
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;
2145 // Arrow DOWN
2146 if(nKey == 40) {
2147 // Is the anchor cell above, below, or same row
2148 if(anchorEl.parentNode.sectionRowIndex > trIndex) {
2149 anchorPos = 1;
2151 else if(anchorEl.parentNode.sectionRowIndex < trIndex) {
2152 anchorPos = -1;
2154 else {
2155 anchorPos = 0;
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
2174 else {
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;
2182 // Arrow up
2183 else if(nKey == 38) {
2184 // Is the anchor cell above, below, or same row
2185 if(anchorEl.parentNode.sectionRowIndex > trIndex) {
2186 anchorPos = 1;
2188 else if(anchorEl.parentNode.sectionRowIndex < trIndex) {
2189 anchorPos = -1;
2191 else {
2192 anchorPos = 0;
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
2202 if(trIndex > 0) {
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
2211 else {
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;
2219 // Arrow right
2220 else if(nKey == 39) {
2221 // Is the anchor cell left, right, or same column
2222 if(anchorEl.yuiCellIndex > tdIndex) {
2223 anchorPos = 1;
2225 else if(anchorEl.yuiCellIndex < tdIndex) {
2226 anchorPos = -1;
2228 else {
2229 anchorPos = 0;
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
2246 else {
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;
2256 // Arrow left
2257 else if(nKey == 37) {
2258 // Is the anchor cell left, right, or same column
2259 if(anchorEl.yuiCellIndex > tdIndex) {
2260 anchorPos = 1;
2262 else if(anchorEl.yuiCellIndex < tdIndex) {
2263 anchorPos = -1;
2265 else {
2266 anchorPos = 0;
2269 // Selecting away from anchor cell
2270 if(anchorPos >= 0) {
2271 //Select the previous vert block to the left
2272 if(tdIndex > 0) {
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
2283 else {
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) {
2305 anchorPos = 1;
2307 else if(anchorEl.parentNode.sectionRowIndex < trIndex) {
2308 anchorPos = -1;
2310 else {
2311 anchorPos = 0;
2314 // Arrow down
2315 if(nKey == 40) {
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
2333 else {
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;
2346 // Arrow up
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
2357 if(trIndex > 0) {
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
2365 else {
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;
2378 // Arrow right
2379 else if(nKey == 39) {
2380 // Selecting away from anchor cell
2381 if(anchorPos < 0) {
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
2402 else {
2403 oSelf._sLastSelectedId = allRows[trIndex+1].cells[0].id;
2406 // Anchor is on this row
2407 else {
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
2422 else {
2423 // Unselect this cell towards the right
2424 oSelf.unselectCell(allRows[trIndex].cells[tdIndex]);
2425 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex+1].id;
2429 // Arrow left
2430 else if(nKey == 37) {
2431 // Unselecting towards the anchor
2432 if(anchorPos < 0) {
2433 oSelf.unselectCell(allRows[trIndex].cells[tdIndex]);
2435 // Unselect this cell towards the left
2436 if(tdIndex > 0) {
2437 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id;
2439 // Unselect this cell towards the last cell of the previous row
2440 else {
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
2447 if(tdIndex > 0) {
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
2458 else {
2459 // Selecting away from anchor cell
2460 if(anchorEl.yuiCellIndex >= tdIndex) {
2461 // Select the next cell to the left
2462 if(tdIndex > 0) {
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
2473 else {
2474 oSelf.unselectCell(allRows[trIndex].cells[tdIndex]);
2476 // Unselect this cell towards the left
2477 if(tdIndex > 0) {
2478 oSelf._sLastSelectedId = allRows[trIndex].cells[tdIndex-1].id;
2480 // Unselect this cell towards the last cell of the previous row
2481 else {
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;
2497 // Arrow down
2498 if(nKey == 40) {
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
2507 else {
2508 newSelectedEl = lastSelectedEl;
2509 oSelf.selectCell(newSelectedEl);
2512 oSelf._sSelectionAnchorId = newSelectedEl.id;
2514 // Arrow up
2515 else if(nKey == 38) {
2516 oSelf.unselectAllCells();
2518 // Select the next cell up
2519 if(trIndex > 0) {
2520 newSelectedEl = allRows[trIndex-1].cells[tdIndex];
2521 oSelf.selectCell(newSelectedEl);
2523 // Select only the top cell
2524 else {
2525 newSelectedEl = lastSelectedEl;
2526 oSelf.selectCell(newSelectedEl);
2529 oSelf._sSelectionAnchorId = newSelectedEl.id;
2531 // Arrow right
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
2541 else {
2542 newSelectedEl = lastSelectedEl;
2543 oSelf.selectCell(newSelectedEl);
2546 oSelf._sSelectionAnchorId = newSelectedEl.id;
2548 // Arrow left
2549 else if(nKey == 37) {
2550 oSelf.unselectAllCells();
2552 // Select the next cell to the left
2553 if(tdIndex > 0) {
2554 newSelectedEl = lastSelectedEl.parentNode.cells[tdIndex-1];
2555 oSelf.selectCell(newSelectedEl);
2557 // Select only the left cell
2558 else {
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) {
2575 anchorPos = 1;
2577 else if(anchorEl.sectionRowIndex < trIndex) {
2578 anchorPos = -1;
2580 else {
2581 anchorPos = 0;
2584 // Arrow down
2585 if(nKey == 40) {
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
2594 else {
2595 // Unselect this row towards the anchor row down
2596 oSelf.unselectRow(lastSelectedEl);
2597 oSelf._sLastSelectedId = allRows[trIndex+1].id;
2601 // Arrow up
2602 else if(nKey == 38) {
2603 // Selecting away from anchor row
2604 if(anchorPos >= 0) {
2605 // Select the next row up
2606 if(trIndex > 0) {
2607 oSelf.selectRow(trIndex-1);
2610 // Unselect this row towards the anchor row up
2611 else {
2612 oSelf.unselectRow(lastSelectedEl);
2613 oSelf._sLastSelectedId = allRows[trIndex-1].id;
2616 // Arrow right
2617 else if(nKey == 39) {
2618 // Do nothing
2620 // Arrow left
2621 else if(nKey == 37) {
2622 // Do nothing
2625 ////////////////////////////////////////////////////////////////////////
2627 // Simple single row selection
2629 ////////////////////////////////////////////////////////////////////////
2630 else {
2631 trIndex = lastSelectedEl.sectionRowIndex;
2633 // Arrow down
2634 if(nKey == 40) {
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
2643 else {
2644 newSelectedEl = lastSelectedEl;
2645 oSelf.selectRow(lastSelectedEl);
2648 oSelf._sSelectionAnchorId = newSelectedEl.id;
2650 // Arrow up
2651 else if(nKey == 38) {
2652 oSelf.unselectAllRows();
2654 // Select the previous row
2655 if(trIndex > 0) {
2656 newSelectedEl = allRows[trIndex-1];
2657 oSelf.selectRow(newSelectedEl);
2659 // Select only the first row
2660 else {
2661 newSelectedEl = lastSelectedEl;
2662 oSelf.selectRow(newSelectedEl);
2665 oSelf._sSelectionAnchorId = newSelectedEl.id;
2667 // Arrow right
2668 else if(nKey == 39) {
2669 // Do nothing
2671 // Arrow left
2672 else if(nKey == 37) {
2673 // Do nothing
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.
2685 * @private
2687 YAHOO.widget.DataTable.prototype._onTableKeypress = function(e, oSelf) {
2688 var isMac = (navigator.userAgent.toLowerCase().indexOf("mac") != -1);
2689 if(isMac) {
2690 var nKey = YAHOO.util.Event.getCharCode(e);
2691 // arrow down
2692 if(nKey == 40) {
2693 YAHOO.util.Event.stopEvent(e);
2695 // arrow up
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.
2708 * @private
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")) {
2719 switch(elTag) {
2720 case "body":
2721 break;
2722 case "span":
2723 if(YAHOO.util.Dom.hasClass(elTarget, YAHOO.widget.DataTable.CLASS_LABEL)) {
2724 oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
2726 break;
2727 case "th":
2728 oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
2729 break;
2730 case "tr":
2731 oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
2732 break;
2733 default:
2734 break;
2736 elTarget = elTarget.parentNode;
2737 if(elTarget) {
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.
2750 * @private
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")) {
2761 switch(elTag) {
2762 case "body":
2763 break;
2764 case "input":
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});
2771 break;
2772 case "a":
2773 oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
2774 break;
2775 case "button":
2776 oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
2777 break;
2778 case "td":
2779 oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
2780 break;
2781 case "tr":
2782 oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
2783 break;
2784 default:
2785 break;
2787 elTarget = elTarget.parentNode;
2788 if(elTarget) {
2789 elTag = elTarget.tagName.toLowerCase();
2792 oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elTable),event:e});
2795 /*TODO: delete
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.
2801 * @private
2803 /*YAHOO.widget.DataTable.prototype._onTbodyKeyup = function(e, oSelf) {
2804 var nKey = YAHOO.util.Event.getCharCode(e);
2805 // delete
2806 if(nKey == 46) {//TODO: if something is selected
2807 //TODO: delete row
2809 };*/
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.
2817 * @private
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")) {
2828 switch(elTag) {
2829 case "body":
2830 return;
2831 case "a":
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));
2838 return;
2839 case YAHOO.widget.DataTable.CLASS_FIRST:
2840 oSelf.showPage(1);
2841 return;
2842 case YAHOO.widget.DataTable.CLASS_LAST:
2843 oSelf.showPage(oSelf.get("paginator").totalPages);
2844 return;
2845 case YAHOO.widget.DataTable.CLASS_PREVIOUS:
2846 oSelf.showPage(oSelf.get("paginator").currentPage - 1);
2847 return;
2848 case YAHOO.widget.DataTable.CLASS_NEXT:
2849 oSelf.showPage(oSelf.get("paginator").currentPage + 1);
2850 return;
2852 break;
2853 default:
2854 return;
2856 elTarget = elTarget.parentNode;
2857 if(elTarget) {
2858 elTag = elTarget.tagName.toLowerCase();
2860 else {
2861 return;
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.
2872 * @private
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();
2884 else {
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.
2894 * @private
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 /////////////////////////////////////////////////////////////////////////////
2950 // Public methods
2952 /////////////////////////////////////////////////////////////////////////////
2954 // OBJECT ACCESSORS
2957 * Public accessor to the unique name of the DataSource instance.
2959 * @method toString
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:
3000 * <dl>
3001 * <dt>cell</dt>
3002 * <dd>Cell element being edited</dd>
3004 * <dt>column</dt>
3005 * <dd>Associated Column instance</dd>
3007 * <dt>container</dt>
3008 * <dd>Reference to editor's container DIV element</dd>
3010 * <dt>isActive</dt>
3011 * <dd>True if cell is currently being edited</dd>
3013 * <dt>record</dt>
3014 * <dd>Associated Record instance</dd>
3016 * <dt>validator</dt>
3017 * <dd>Associated validator function</dd>
3019 * <dt>value</dt>
3020 * <dd>Current input value</dd>
3021 * </dl>
3028 * @method getCellEditor
3029 * @return {Object} Cell Editor object literal values.
3031 YAHOO.widget.DataTable.prototype.getCellEditor = function() {
3032 return this._oCellEditor;
3077 // DOM ACCESSORS
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
3139 * null.
3141 * @method getTrEl
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;
3149 // By Record
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
3159 else {
3160 var elRow;
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");
3170 else {
3171 elRow = el;
3174 // Make sure the TR is in this TBODY
3175 if(elRow && (elRow.parentNode == this._elTbody)) {
3176 // Now we can return the TR element
3177 return elRow;
3182 return null;
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.
3215 * @method getTdEl
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) {
3220 var elCell;
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");
3230 else {
3231 elCell = el;
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
3237 return elCell;
3241 return null;
3245 * Returns DOM reference to the TH element at given DataTable page coordinates, or null.
3247 * @method getThEl
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) {
3253 var elHeader;
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());
3259 if(elHeader) {
3260 return elHeader;
3263 // Validate HTML element
3264 else {
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");
3273 else {
3274 elHeader = el;
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
3280 return elHeader;
3285 return null;
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) {
3299 var nRecordIndex;
3301 // By Record
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)) {
3307 nRecordIndex = 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
3320 else {
3321 return null;
3324 // Not paginated, just return the Record index
3325 else {
3326 return nRecordIndex;
3330 // By element reference or ID string
3331 else {
3332 // Validate TR element
3333 elRow = this.getTrEl(row);
3334 if(elRow && (elRow.ownerDocument == document) &&
3335 (elRow.parentNode == this._elTbody)) {
3336 return elRow.sectionRowIndex;
3340 return null;
3388 // TABLE FUNCTIONS
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);
3410 // Clear selections
3411 this._unselectAllTrEls();
3412 this._unselectAllTdEls();
3413 this._aSelections = null;
3414 this._sLastSelectedId = null;
3415 this._sSelectionAnchorId = null;
3417 // Refresh the view
3418 this.refreshView();
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();
3440 // Show all records
3441 else {
3442 aRecords = this._oRecordSet.getRecords();
3445 var elTbody = this._elTbody;
3446 var elRows = elTbody.rows;
3448 // Has 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
3465 if(bReselect) {
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;
3482 if(bReselect) {
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")) {
3488 // Set SELECTED
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;
3499 else {
3500 // Loop over each cell
3501 for(k=0; k<thisRow.cells.length; k++) {
3502 var thisCell = thisRow.cells[k];
3503 // Set SELECTED
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();
3521 this._setLastRow();
3522 this._setRowStripes();
3524 this.fireEvent("refreshEvent");
3526 // Empty
3527 else {
3528 // Remove all rows
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!
3543 * @method destroy
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 = "";
3563 // Null out objects
3564 for(var param in this) {
3565 if(this.hasOwnProperty(param)) {
3566 this[param] = null;
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.
3606 * @method focus
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) {
3688 var nTrIndex;
3690 if(!YAHOO.lang.isNumber(row)) {
3691 // By Record
3692 if(row instanceof YAHOO.widget.Record) {
3693 return this._oRecordSet.getRecordIndex(row);
3695 // By element reference
3696 else {
3697 // Find the TR element
3698 var el = this.getTrEl(row);
3699 if(el) {
3700 nTrIndex = el.sectionRowIndex;
3704 // By page row index
3705 else {
3706 nTrIndex = row;
3709 if(YAHOO.lang.isNumber(nTrIndex)) {
3710 if(this.get("paginated")) {
3711 return this.get("paginator").startRecordIndex + nTrIndex;
3713 else {
3714 return nTrIndex;
3718 return null;
3722 * For the given identifier, returns the associated Record instance.
3724 * @method getRecord
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);
3736 if(elRow) {
3737 nRecordIndex = this.getRecordIndex(row);
3740 // By Record index
3741 if(YAHOO.lang.isNumber(nRecordIndex)) {
3742 return this._oRecordSet.getRecord(nRecordIndex);
3745 return null;
3793 // COLUMN FUNCTIONS
3796 * For the given identifier, returns the associated Column instance.
3798 * @method getColumn
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);
3810 if(elCell) {
3811 nColumnIndex = elCell.yuiColumnId;
3813 // Validate TH element
3814 else {
3815 elCell = this.getThEl(column);
3816 if(elCell) {
3817 nColumnIndex = elCell.yuiColumnId;
3822 // By Column index
3823 if(YAHOO.lang.isNumber(nColumnIndex)) {
3824 return this._oColumnSet.getColumn(nColumnIndex);
3827 return null;
3831 * Sorts given Column.
3833 * @method sortColumn
3834 * @param oColumn {YAHOO.widget.Column} Column instance.
3836 YAHOO.widget.DataTable.prototype.sortColumn = function(oColumn) {
3837 if(!oColumn) {
3838 return;
3840 if(!oColumn instanceof YAHOO.widget.Column) {
3841 //TODO: accept the TH or TH.key
3842 //TODO: Get the column based on TH.yuiColumnId
3843 return;
3845 if(oColumn.sortable) {
3846 // What is the default sort direction?
3847 var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultOrder) ? oColumn.sortOptions.defaultOrder : "asc";
3849 // Already sorted?
3850 var oSortedBy = this.get("sortedBy");
3851 if(oSortedBy && (oSortedBy.key === oColumn.key)) {
3852 if(oSortedBy.dir) {
3853 sortDir = (oSortedBy.dir == "asc") ? "desc" : "asc";
3855 else {
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);
3864 if(sorted === 0) {
3865 return YAHOO.util.Sort.compare(a.getId(),b.getId(), desc);
3867 else {
3868 return sorted;
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});
3883 // Update the UI
3884 this.refreshView();
3886 this.fireEvent("columnSortEvent",{column:oColumn,dir:sortDir});
3888 else {
3889 //TODO
3937 // ROW FUNCTIONS
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.
3945 * @method addRow
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);
3952 if(oRecord) {
3953 var nTrIndex = this.getTrIndex(oRecord);
3955 // Row is in view
3956 if(YAHOO.lang.isNumber(nTrIndex)) {
3957 // Paginated so just refresh the view to keep pagination state
3958 if(this.get("paginated")) {
3959 this.refreshView();
3961 // Add the TR element
3962 else {
3963 var newTrId = this._addTrEl(oRecord, nTrIndex);
3964 if(newTrId) {
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
3970 if(append) {
3971 if((this._elTbody.rows.length-1)%2) {
3972 YAHOO.util.Dom.addClass(newTrId, YAHOO.widget.DataTable.CLASS_ODD);
3974 else {
3975 YAHOO.util.Dom.addClass(newTrId, YAHOO.widget.DataTable.CLASS_EVEN);
3978 // Restripe all the rows after the new one
3979 else {
3980 this._setRowStripes(nTrIndex);
3983 // If new row is at the bottom
3984 if(append) {
3985 this._setLastRow();
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
3995 else {
3996 this.updatePaginator();
3999 // TODO: what args to pass?
4000 this.fireEvent("rowAddEvent", {record:oRecord});
4002 // For log message
4003 nTrIndex = (YAHOO.lang.isValue(nTrIndex))? nTrIndex : "n/a";
4005 return;
4011 * Convenience method to add multiple rows.
4013 * @method addRows
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)) {
4019 var i;
4020 if(YAHOO.lang.isNumber(index)) {
4021 for(i=aData.length-1; i>-1; i--) {
4022 this.addRow(aData[i], index);
4025 else {
4026 for(i=0; i<aData.length; i++) {
4027 this.addRow(aData[i]);
4031 else {
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.
4039 * @method updateRow
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
4058 else {
4059 elRow = this.getTrEl(row);
4060 if(elRow) {
4061 oldRecord = this._oRecordSet.getRecord(this.getRecordIndex(elRow));
4065 // Update the Record
4066 if(oldRecord) {
4067 // Copy data from the Record for the event that gets fired later
4068 var oRecordData = oldRecord.getData();
4069 var oldData = {};
4070 for(var param in oRecordData) {
4071 oldData[param] = oRecordData[param];
4074 updatedRecord = this._oRecordSet.updateRecord(oldRecord, oData);
4076 else {
4077 return;
4081 // Update the TR only if row is in view
4082 if(elRow) {
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.
4093 * @method deleteRow
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)) {
4102 nRecordIndex = row;
4104 // ...by element reference
4105 else {
4106 var elRow = YAHOO.util.Dom.get(row);
4107 elRow = this.getTrEl(elRow);
4108 if(elRow) {
4109 nRecordIndex = this.getRecordIndex(elRow);
4112 if(nRecordIndex !== null) {
4113 var oRecord = this._oRecordSet.getRecord(nRecordIndex);
4114 if(oRecord) {
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();
4128 var oData = {};
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) ?
4140 true : false;
4141 this._deleteTrEl(nTrIndex);
4143 // Empty body
4144 if(this._elTbody.rows.length === 0) {
4145 this.showTableMessage(YAHOO.widget.DataTable.MSG_EMPTY, YAHOO.widget.DataTable.CLASS_EMPTY);
4147 // Update UI
4148 else {
4149 // Set FIRST/LAST
4150 if(nTrIndex === 0) {
4151 this._setFirstRow();
4153 if(isLast) {
4154 this._setLastRow();
4156 // Set EVEN/ODD
4157 if(nTrIndex != this._elTbody.rows.length) {
4158 this._setRowStripes(nTrIndex);
4163 this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,
4164 oldData:oData, trElIndex:nTrIndex});
4167 else {
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)) {
4185 nRecordIndex = row;
4187 // ...by element reference
4188 else {
4189 var elRow = YAHOO.util.Dom.get(row);
4190 elRow = this.getTrEl(elRow);
4191 if(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--) {
4201 this.deleteRow(i);
4204 else {
4205 this.deleteRow(nRecordIndex);
4208 else {
4257 // CELL FUNCTIONS
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);
4279 var fnFormatter;
4280 if(YAHOO.lang.isString(oColumn.formatter)) {
4281 switch(oColumn.formatter) {
4282 case "button":
4283 fnFormatter = YAHOO.widget.DataTable.formatButton;
4284 break;
4285 case "checkbox":
4286 fnFormatter = YAHOO.widget.DataTable.formatCheckbox;
4287 break;
4288 case "currency":
4289 fnFormatter = YAHOO.widget.DataTable.formatCurrency;
4290 break;
4291 case "date":
4292 fnFormatter = YAHOO.widget.DataTable.formatDate;
4293 break;
4294 case "dropdown":
4295 fnFormatter = YAHOO.widget.DataTable.formatDropdown;
4296 break;
4297 case "email":
4298 fnFormatter = YAHOO.widget.DataTable.formatEmail;
4299 break;
4300 case "link":
4301 fnFormatter = YAHOO.widget.DataTable.formatLink;
4302 break;
4303 case "number":
4304 fnFormatter = YAHOO.widget.DataTable.formatNumber;
4305 break;
4306 case "radio":
4307 fnFormatter = YAHOO.widget.DataTable.formatRadio;
4308 break;
4309 case "text":
4310 fnFormatter = YAHOO.widget.DataTable.formatText;
4311 break;
4312 case "textarea":
4313 fnFormatter = YAHOO.widget.DataTable.formatTextarea;
4314 break;
4315 case "textbox":
4316 fnFormatter = YAHOO.widget.DataTable.formatTextbox;
4317 break;
4318 case "html":
4319 // This is the default
4320 break;
4321 default:
4322 fnFormatter = null;
4325 else if(YAHOO.lang.isFunction(oColumn.formatter)) {
4326 fnFormatter = oColumn.formatter;
4329 // Apply special formatter
4330 if(fnFormatter) {
4331 fnFormatter.call(this, elCell, oRecord, oColumn, oData);
4333 else {
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);
4353 // Is editable?
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});
4360 else {
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.
4374 * @static
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) {
4382 //else {
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
4398 * formatter.
4399 * @static
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.
4416 * @static
4418 YAHOO.widget.DataTable.formatCurrency = function(el, oRecord, oColumn, oData) {
4419 if(YAHOO.lang.isNumber(oData)) {
4420 var nAmount = oData;
4421 var markup;
4423 // Round to the penny
4424 nAmount = Math.round(nAmount*100)/100;
4426 // Default currency is USD
4427 markup = "$"+nAmount;
4429 // Normalize digits
4430 var dotIndex = markup.indexOf(".");
4431 if(dotIndex < 0) {
4432 markup += ".00";
4434 else {
4435 while(dotIndex > markup.length-3) {
4436 markup += "0";
4439 el.innerHTML = markup;
4441 else {
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.
4454 * @static
4456 YAHOO.widget.DataTable.formatDate = function(el, oRecord, oColumn, oData) {
4457 var oDate = oData;
4458 if(oDate instanceof Date) {
4459 el.innerHTML = (oDate.getMonth()+1) + "/" + oDate.getDate() + "/" + oDate.getFullYear();
4461 else {
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.
4474 * @static
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;
4481 var selectEl;
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
4499 if(selectEl) {
4500 // Clear out previous options
4501 selectEl.innerHTML = "";
4503 // We have options to populate
4504 if(options) {
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
4517 else {
4518 selectEl.innerHTML = "<option value=\"" + selectedValue + "\">" + selectedValue + "</option>";
4521 else {
4522 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4527 * Formats emails.
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.
4534 * @static
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>";
4540 else {
4541 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4546 * Formats links.
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.
4553 * @static
4555 YAHOO.widget.DataTable.formatLink = function(el, oRecord, oColumn, oData) {
4556 if(YAHOO.lang.isString(oData)) {
4557 el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>";
4559 else {
4560 el.innerHTML = YAHOO.lang.isValue(oData) ? oData : "";
4565 * Formats numbers.
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.
4572 * @static
4574 YAHOO.widget.DataTable.formatNumber = function(el, oRecord, oColumn, oData) {
4575 if(YAHOO.lang.isNumber(oData)) {
4576 el.innerHTML = oData;
4578 else {
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.
4591 * @static
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.
4609 * @static
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, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;");
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.
4626 * @static
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.
4643 * @static
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;
4699 // PAGINATION
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) {
4712 // Complete the set
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.
4734 * @method showPage
4735 * @param nPage {Number} Which page.
4737 YAHOO.widget.DataTable.prototype.showPage = function(nPage) {
4738 // Validate input
4739 if(!YAHOO.lang.isNumber(nPage) || (nPage < 1) || (nPage > this.get("paginator").totalPages)) {
4740 nPage = 1;
4742 this.updatePaginator({currentPage:nPage});
4743 this.refreshView();
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);
4770 else {
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 = "";
4819 return;
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 + "\">&lt;&lt;</span> " :
4843 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_FIRST + "\">&lt;&lt;</a> ";
4844 var sPrevLinkMarkup = (bIsFirstPage) ?
4845 " <span class=\"" + YAHOO.widget.DataTable.CLASS_DISABLED +
4846 " " + YAHOO.widget.DataTable.CLASS_PREVIOUS + "\">&lt;</span> " :
4847 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_PREVIOUS + "\">&lt;</a> " ;
4848 var sNextLinkMarkup = (bIsLastPage) ?
4849 " <span class=\"" + YAHOO.widget.DataTable.CLASS_DISABLED +
4850 " " + YAHOO.widget.DataTable.CLASS_NEXT + "\">&gt;</span> " :
4851 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_NEXT + "\">&gt;</a> " ;
4852 var sLastLinkMarkup = (bIsLastPage) ?
4853 " <span class=\"" + YAHOO.widget.DataTable.CLASS_DISABLED +
4854 " " + YAHOO.widget.DataTable.CLASS_LAST + "\">&gt;&gt;</span> " :
4855 " <a href=\"#\" class=\"" + YAHOO.widget.DataTable.CLASS_LAST + "\">&gt;&gt;</a> ";
4857 // Start with first and previous
4858 var sMarkup = sFirstLinkMarkup + sPrevLinkMarkup;
4860 // Ok to show all links
4861 var nMaxLinks = nTotalPages;
4862 var nFirstLink = 1;
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) {
4885 nLastLink--;
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> ";
4894 else {
4895 sMarkup += " <span class=\"" + YAHOO.widget.DataTable.CLASS_SELECTED + "\">" + i + "</span>";
4898 sMarkup += sNextLinkMarkup + sLastLinkMarkup;
4899 elContainer.innerHTML = sMarkup;
4900 return;
4952 // SELECTION/HIGHLIGHTING
4955 * ID string of last highlighted cell element
4957 * @property _sLastHighlightedCellId
4958 * @type String
4959 * @private
4961 YAHOO.widget.DataTable.prototype._sLastHighlightedCellId = null;
4964 * ID string of last highlighted row element
4966 * @property _sLastHighlightedRowId
4967 * @type String
4968 * @private
4970 YAHOO.widget.DataTable.prototype._sLastHighlightedRowId = null;
4973 * Array of selections: {recordId:nRecordId, cellIndex:nCellIndex}
4975 * @property _aSelections
4976 * @type Object[]
4977 * @private
4979 YAHOO.widget.DataTable.prototype._aSelections = null;
4982 * ID string of last selected element
4984 * @property _sLastSelectedId
4985 * @type String
4986 * @private
4988 YAHOO.widget.DataTable.prototype._sLastSelectedId = null;
4991 * ID string of the selection anchor element.
4993 * @property _sSelectionAnchorId
4994 * @type String
4995 * @private
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
5004 * @private
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.
5024 * @method selectRow
5025 * @param row {HTMLElement | String} HTML element reference or ID.
5027 YAHOO.widget.DataTable.prototype.selectRow = function(row) {
5028 // Validate the row
5029 var elRow = this.getTrEl(row);
5030 if(elRow) {
5031 var oRecord = this.getRecord(elRow);
5032 if(oRecord) {
5033 // Get Record ID
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
5043 else {
5044 for(var j=0; j<tracker.length; j++) {
5045 if(tracker[j] === nRecordId){
5046 tracker.splice(j,1);
5050 // Add to the end
5051 tracker.push(nRecordId);
5053 // Update trackers
5054 this._sLastSelectedId = elRow.id;
5055 if(!this._sSelectionAnchorId) {
5056 this._sSelectionAnchorId = elRow.id;
5058 this._aSelections = tracker;
5060 // Update UI
5061 YAHOO.util.Dom.addClass(elRow, YAHOO.widget.DataTable.CLASS_SELECTED);
5063 this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
5065 return;
5069 // Backward compatibility
5070 YAHOO.widget.DataTable.prototype.select = function(els) {
5071 if(!YAHOO.lang.isArray(els)) {
5072 els = [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) {
5086 // Validate the row
5087 var elRow = this.getTrEl(row);
5088 if(elRow) {
5089 var oRecord = this.getRecord(elRow);
5090 if(oRecord) {
5091 // Get Record ID
5092 var tracker = this._aSelections || [];
5093 var nRecordId = oRecord.getId();
5095 // Remove if there
5096 var bFound = false;
5098 // Use Array.indexOf if available...
5099 if(tracker.indexOf && (tracker.indexOf(nRecordId) > -1)) {
5100 tracker.splice(tracker.indexOf(nRecordId),1);
5101 bFound = true;
5103 // ...or do it the old-fashioned way
5104 else {
5105 for(var j=0; j<tracker.length; j++) {
5106 if(tracker[j] === nRecordId){
5107 tracker.splice(j,1);
5108 bFound = true;
5113 if(bFound) {
5114 // Update tracker
5115 this._aSelections = tracker;
5117 // Update the UI
5118 YAHOO.util.Dom.removeClass(elRow, YAHOO.widget.DataTable.CLASS_SELECTED);
5120 this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
5122 return;
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);
5142 // Update tracker
5143 this._aSelections = tracker;
5145 // Update UI
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
5159 * @private
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);
5186 if(elCell) {
5187 var oRecord = this.getRecord(elCell);
5188 var nColumnId = elCell.yuiColumnId;
5190 if(oRecord && YAHOO.lang.isNumber(nColumnId)) {
5191 // Get Record ID
5192 var tracker = this._aSelections || [];
5193 var nRecordId = oRecord.getId();
5195 // Remove if there
5196 for(var j=0; j<tracker.length; j++) {
5197 if((tracker[j].recordId === nRecordId) && (tracker[j].columnId === nColumnId)){
5198 tracker.splice(j,1);
5202 // Add to the end
5203 tracker.push({recordId:nRecordId, columnId:nColumnId});
5205 // Update trackers
5206 this._aSelections = tracker;
5207 this._sLastSelectedId = elCell.id;
5208 if(!this._sSelectionAnchorId) {
5209 this._sSelectionAnchorId = elCell.id;
5212 // Update the UI
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});
5217 return;
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);
5232 if(elCell) {
5233 var oRecord = this.getRecord(elCell);
5234 var nColumnId = elCell.yuiColumnId;
5236 if(oRecord && YAHOO.lang.isNumber(nColumnId)) {
5237 // Get Record ID
5238 var tracker = this._aSelections || [];
5239 var id = oRecord.getId();
5241 // Is it selected?
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);
5247 // Update tracker
5248 this._aSelections = tracker;
5250 // Update the UI
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});
5256 return;
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);
5277 // Update tracker
5278 this._aSelections = tracker;
5280 // Update UI
5281 this._unselectAllTdEls();
5283 //TODO: send data
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);
5343 if(elRow) {
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});
5352 return;
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);
5365 if(elRow) {
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});
5369 return;
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);
5382 if(elCell) {
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});
5393 return;
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);
5406 if(elCell) {
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});
5411 return;
5459 // INLINE EDITING
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) {
5468 //};
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) {
5498 return;
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);
5510 // Move Editor
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";
5530 // Show Editor
5531 elContainer.style.display = "";
5533 // Render Editor markup
5534 var fnEditor;
5535 if(YAHOO.lang.isString(oColumn.editor)) {
5536 switch(oColumn.editor) {
5537 case "checkbox":
5538 fnEditor = YAHOO.widget.DataTable.editCheckbox;
5539 break;
5540 case "date":
5541 fnEditor = YAHOO.widget.DataTable.editDate;
5542 break;
5543 case "dropdown":
5544 fnEditor = YAHOO.widget.DataTable.editDropdown;
5545 break;
5546 case "radio":
5547 fnEditor = YAHOO.widget.DataTable.editRadio;
5548 break;
5549 case "textarea":
5550 fnEditor = YAHOO.widget.DataTable.editTextarea;
5551 break;
5552 case "textbox":
5553 fnEditor = YAHOO.widget.DataTable.editTextbox;
5554 break;
5555 default:
5556 fnEditor = null;
5559 else if(YAHOO.lang.isFunction(oColumn.editor)) {
5560 fnEditor = oColumn.editor;
5563 if(fnEditor) {
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});
5579 return;
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) {
5601 // Buttons
5602 var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
5603 YAHOO.util.Dom.addClass(elBtnsDiv, YAHOO.widget.DataTable.CLASS_BUTTON);
5605 // Save 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);
5611 // Cancel button
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});
5650 return;
5654 // Update the Record
5655 this._oRecordSet.updateKey(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
5657 // Update the UI
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});
5666 else {
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});
5681 else {
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];
5701 // Checkboxes
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 = [];
5720 var checkboxEl;
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
5731 if(j===0) {
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.
5755 * @method editDate
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);
5764 // Calendar widget
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";
5769 var calendar =
5770 new YAHOO.widget.Calendar("yui-dt-" + oSelf._nIndex + "-col" + oColumn.getKeyIndex() + "-date",
5771 calContainer.id,
5772 {selected:selectedValue, pagedate:value});
5773 calendar.render();
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});
5784 else {
5785 //TODO;
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);
5801 // Textbox
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",
5820 function(){
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.
5832 * @method editRadio
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);
5841 // Radios
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",
5867 function(){
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);
5887 // Textarea
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});
5900 // Select the text
5901 elTextarea.focus();
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) : "";
5917 // Textbox
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});
5931 // Select the text
5932 elTextbox.focus();
5933 elTextbox.select();
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
5943 * @static
5945 YAHOO.widget.DataTable.validateNumber = function(oData) {
5946 //Convert to number
5947 var number = oData * 1;
5949 // Validate
5950 if(YAHOO.lang.isNumber(number)) {
5951 return number;
5953 else {
5954 return null;
5995 // ABSTRACT METHODS
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) {
6008 return true;
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));
6096 else {
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")) {
6110 return;
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);
6122 if(elTargetRow) {
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
6142 else {
6143 for(i=nAnchorTrIndex-1; i>=nTargetTrIndex; i--) {
6144 if(!this.isSelected(allRows[i])) {
6145 this.selectRow(allRows[i]);
6150 else {
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
6160 else {
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);
6171 // Invalid anchor
6172 else {
6173 // Set anchor
6174 this._sSelectionAnchorId = elTargetRow.id;
6176 // Toggle selection of target
6177 if(this.isSelected(elTargetRow)) {
6178 this.unselectRow(elTargetRow);
6180 else {
6181 this.selectRow(elTargetRow);
6185 // Only SHIFT
6186 else if((sMode != "single") && bSHIFT) {
6187 this.unselectAllRows();
6189 // Validate anchor
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
6202 else {
6203 for(i=nAnchorTrIndex; i>=nTargetTrIndex; i--) {
6204 this.selectRow(allRows[i]);
6208 // Invalid anchor
6209 else {
6210 // Set anchor
6211 this._sSelectionAnchorId = elTargetRow.id;
6213 // Select target row only
6214 this.selectRow(elTargetRow);
6217 // Only CTRL
6218 else if((sMode != "single") && bCTRL) {
6219 // Set anchor
6220 this._sSelectionAnchorId = elTargetRow.id;
6222 // Toggle selection of target
6223 if(this.isSelected(elTargetRow)) {
6224 this.unselectRow(elTargetRow);
6226 else {
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
6236 else {
6237 // Set anchor
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
6247 var sel;
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;
6257 if(sel) {
6258 if(sel.empty) {
6259 sel.empty();
6261 else if (sel.removeAllRanges) {
6262 sel.removeAllRanges();
6264 else if(sel.collapse) {
6265 sel.collapse();
6269 else {
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")) {
6283 return;
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);
6294 if(elTargetCell) {
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) {
6303 // Validate anchor
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
6358 else {
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
6391 else {
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
6426 else {
6427 this.unselectCell(currentRow.cells[j]);
6432 // Anchor row is below target row
6433 else {
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
6451 else {
6452 this.unselectCell(currentRow.cells[j]);
6458 // Select the target cell
6459 this.selectCell(elTargetCell);
6462 // Invalid anchor
6463 else {
6464 // Set anchor
6465 this._sSelectionAnchorId = elTargetCell.id;
6467 // Toggle selection of target
6468 if(this.isSelected(elTargetCell)) {
6469 this.unselectCell(elTargetCell);
6471 else {
6472 this.selectCell(elTargetCell);
6476 // Only SHIFT
6477 else if((sMode != "singlecell") && bSHIFT) {
6478 this.unselectAllCells();
6480 // Validate anchor
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
6523 else {
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
6545 else {
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
6565 else {
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;
6587 // Invalid anchor
6588 else {
6589 // Set anchor
6590 this._sSelectionAnchorId = elTargetCell.id;
6592 // Select target only
6593 this.selectCell(elTargetCell);
6596 // Only CTRL
6597 else if((sMode != "singlecell") && bCTRL) {
6598 // Set anchor
6599 this._sSelectionAnchorId = elTargetCell.id;
6601 // Toggle selection of target
6602 if(this.isSelected(elTargetCell)) {
6603 this.unselectCell(elTargetCell);
6605 else {
6606 this.selectCell(elTargetCell);
6609 // Neither SHIFT nor CTRL, or multi-selection has been disabled
6610 else {
6611 // Set anchor
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
6622 var sel;
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;
6632 if(sel) {
6633 if(sel.empty) {
6634 sel.empty();
6636 else if (sel.removeAllRanges) {
6637 sel.removeAllRanges();
6639 else if(sel.collapse) {
6640 sel.collapse();
6644 else {
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);
6727 else {
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);
6744 if(elCell) {
6745 this.showCellEditor(elCell);
6747 else {
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
6768 * DOM elements.
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.
6774 * @private
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)) {
6782 // Update RecordSet
6783 this._oRecordSet.addRecords(oResponse.results);
6785 // Initialize DOM elements
6786 this._initTableEl();
6787 if(!this._elTable || !this._elThead || !this._elTbody) {
6788 return;
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);
6800 // Update the UI
6801 this.refreshView();
6803 // Error
6804 else if(ok && oResponse.error) {
6805 this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR);
6807 // Empty
6808 else if(ok){
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);
6832 // Error
6833 else if(ok && oResponse.error) {
6834 this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR);
6836 // Empty
6837 else if(ok){
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);
6866 // Error
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);
6892 // Error
6893 else if(ok && oResponse.error) {
6894 this.showTableMessage(YAHOO.widget.DataTable.MSG_ERROR, YAHOO.widget.DataTable.CLASS_ERROR);
6932 /////////////////////////////////////////////////////////////////////////////
6934 // Custom Events
6936 /////////////////////////////////////////////////////////////////////////////
6939 * Fired when the DataTable instance's initialization is complete.
6941 * @event initEvent
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
7373 /*TODO: implement
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.
7457 /*TODO
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
7483 * @class ColumnSet
7484 * @uses YAHOO.util.EventProvider
7485 * @constructor
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
7492 var tree = [];
7493 // Flat representation of all Columns
7494 var flat = [];
7495 // Flat representation of only Columns that are meant to display data
7496 var keys = [];
7497 // Array of HEADERS attribute values for all keys in the "keys" array
7498 var headers = [];
7500 // Tracks current node list depth being tracked
7501 var nodeDepth = -1;
7503 // Internal recursive function to defined Column instances
7504 var parseColumns = function(nodeList, parent) {
7505 // One level down
7506 nodeDepth++;
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
7522 flat.push(oColumn);
7524 // Assign its parent as an attribute, if applicable
7525 if(parent) {
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
7544 else {
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
7601 else {
7602 oColumn._nKeyIndex = keys.length;
7603 oColumn._colspan = 1;
7604 keys.push(oColumn);
7607 // Add the Column to the top-down tree
7608 tree[nodeDepth].push(oColumn);
7610 nodeDepth--;
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;
7621 var currentRow;
7622 var currentColumn;
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++) {
7629 var col = row[n];
7630 // Column has children, so keep counting
7631 if(YAHOO.lang.isArray(col.children)) {
7632 tmpRowDepth++;
7633 countMaxRowDepth(col.children, tmpRowDepth);
7634 tmpRowDepth--;
7636 // No children, is it the max depth?
7637 else {
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;
7657 else {
7658 currentColumn._rowspan = 1;
7662 // Reset counter for next row
7663 maxRowDepth = 1;
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++) {
7680 headers[i] = [];
7681 recurseAncestorsForHeaders(i, keys[i]);
7682 headers[i] = headers[i].reverse();
7683 headers[i] = headers[i].join(" ");
7686 // Save to the ColumnSet instance
7687 this.tree = tree;
7688 this.flat = flat;
7689 this.keys = keys;
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
7705 * @type number
7706 * @private
7707 * @static
7709 YAHOO.widget.ColumnSet._nCount = 0;
7712 * Unique instance name.
7714 * @property _sName
7715 * @type String
7716 * @private
7718 YAHOO.widget.ColumnSet.prototype._sName = null;
7720 /////////////////////////////////////////////////////////////////////////////
7722 // Public member variables
7724 /////////////////////////////////////////////////////////////////////////////
7727 * Top-down tree representation of Column hierarchy.
7729 * @property tree
7730 * @type YAHOO.widget.Column[]
7732 YAHOO.widget.ColumnSet.prototype.tree = null;
7735 * Flattened representation of all Columns.
7737 * @property flat
7738 * @type YAHOO.widget.Column[]
7739 * @default []
7741 YAHOO.widget.ColumnSet.prototype.flat = null;
7744 * Array of Columns that map one-to-one to a table column.
7746 * @property keys
7747 * @type YAHOO.widget.Column[]
7748 * @default []
7750 YAHOO.widget.ColumnSet.prototype.keys = null;
7753 * ID index of nested parent hierarchies for HEADERS accessibility attribute.
7755 * @property headers
7756 * @type String[]
7757 * @default []
7759 YAHOO.widget.ColumnSet.prototype.headers = null;
7761 /////////////////////////////////////////////////////////////////////////////
7763 // Public methods
7765 /////////////////////////////////////////////////////////////////////////////
7768 * Public accessor to the unique name of the ColumnSet instance.
7770 * @method toString
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.
7781 * @method getColumn
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];
7802 return null;
7805 /****************************************************************************/
7806 /****************************************************************************/
7807 /****************************************************************************/
7810 * The Column class defines and manages attributes of DataTable Columns
7812 * @namespace YAHOO.widget
7813 * @class Column
7814 * @constructor
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) {
7825 if(sConfig) {
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
7847 * @type Number
7848 * @private
7849 * @static
7850 * @default 0
7852 YAHOO.widget.Column._nCount = 0;
7855 * Unique instance name.
7857 * @property _sName
7858 * @type String
7859 * @private
7861 YAHOO.widget.Column.prototype._sName = null;
7865 * Unique number assigned at instantiation, indicates original order within
7866 * ColumnSet.
7868 * @property _nId
7869 * @type Number
7870 * @private
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
7878 * @type Number
7879 * @private
7881 YAHOO.widget.Column.prototype._nKeyIndex = null;
7884 * Number of table cells the Column spans.
7886 * @property _colspan
7887 * @type Number
7888 * @private
7890 YAHOO.widget.Column.prototype._colspan = 1;
7893 * Number of table rows the Column spans.
7895 * @property _rowspan
7896 * @type Number
7897 * @private
7899 YAHOO.widget.Column.prototype._rowspan = 1;
7902 * Column's parent Column instance, or null.
7904 * @property _parent
7905 * @type YAHOO.widget.Column
7906 * @private
7908 YAHOO.widget.Column.prototype._parent = null;
7911 * Current offsetWidth of the Column (in pixels).
7913 * @property _width
7914 * @type Number
7915 * @private
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
7924 * @type Number
7925 * @private
7927 YAHOO.widget.Column.prototype._minWidth = null;
7929 /////////////////////////////////////////////////////////////////////////////
7931 // Public member variables
7933 /////////////////////////////////////////////////////////////////////////////
7936 * Associated database field, or null.
7938 * @property key
7939 * @type String
7941 YAHOO.widget.Column.prototype.key = null;
7944 * Text or HTML for display as Column's label in the TH element.
7946 * @property label
7947 * @type String
7949 YAHOO.widget.Column.prototype.label = null;
7952 * Column head cell ABBR for accessibility.
7954 * @property abbr
7955 * @type String
7957 YAHOO.widget.Column.prototype.abbr = null;
7960 * Array of object literals that define children (nested headers) of a Column.
7962 * @property children
7963 * @type Object[]
7965 YAHOO.widget.Column.prototype.children = null;
7968 * Column width.
7970 * @property width
7971 * @type String
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.
7994 * @property editor
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
8003 * @type Object
8005 YAHOO.widget.Column.prototype.editorOptions = null;
8008 * True if Column is resizeable, false otherwise.
8010 * @property resizeable
8011 * @type Boolean
8012 * @default false
8014 YAHOO.widget.Column.prototype.resizeable = false;
8017 * True if Column is sortable, false otherwise.
8019 * @property sortable
8020 * @type Boolean
8021 * @default false
8023 YAHOO.widget.Column.prototype.sortable = false;
8026 * Default sort order for Column: "asc" or "desc".
8028 * @property sortOptions.defaultOrder
8029 * @type String
8030 * @default null
8033 * Custom sort handler.
8035 * @property sortOptions.sortFunction
8036 * @type Function
8037 * @default null
8039 YAHOO.widget.Column.prototype.sortOptions = null;
8055 /////////////////////////////////////////////////////////////////////////////
8057 // Public methods
8059 /////////////////////////////////////////////////////////////////////////////
8062 * Public accessor to the unique name of the Column instance.
8064 * @method toString
8065 * @return {String} Column's unique name.
8067 YAHOO.widget.Column.prototype.toString = function() {
8068 return this._sName;
8072 * Returns unique number assigned at instantiation, indicates original order
8073 * within ColumnSet.
8075 * @method getId
8076 * @return {Number} Column's unique ID number.
8078 YAHOO.widget.Column.prototype.getId = function() {
8079 return this._nId;
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.
8096 * @method getParent
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
8163 * @class Sort
8164 * @static
8166 YAHOO.util.Sort = {
8167 /////////////////////////////////////////////////////////////////////////////
8169 // Public methods
8171 /////////////////////////////////////////////////////////////////////////////
8174 * Comparator function for simple case-insensitive string sorting.
8176 * @method compare
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")) {
8185 return 0;
8187 else {
8188 return 1;
8191 else if((b === null) || (typeof b == "undefined")) {
8192 return -1;
8195 if(a.constructor == String) {
8196 a = a.toLowerCase();
8198 if(b.constructor == String) {
8199 b = b.toLowerCase();
8201 if(a < b) {
8202 return (desc) ? 1 : -1;
8204 else if (a > b) {
8205 return (desc) ? -1 : 1;
8207 else {
8208 return 0;
8213 /****************************************************************************/
8214 /****************************************************************************/
8215 /****************************************************************************/
8218 * ColumnResizer subclasses DragDrop to support resizeable Columns.
8220 * @namespace YAHOO.util
8221 * @class ColumnResizer
8222 * @extends YAHOO.util.DragDrop
8223 * @constructor
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);
8237 //this.initFrame();
8238 this.setYConstraint(0,0);
8240 else {
8244 if(YAHOO.util.DD) {
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;
8270 //!!
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.
8281 * @method onMouseUp
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.
8306 * @method onDrag
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;
8345 else {
8346 elCell.style.width = newWidth + "px";
8353 /****************************************************************************/
8354 /****************************************************************************/
8355 /****************************************************************************/
8358 * A RecordSet defines and manages a set of Records.
8360 * @namespace YAHOO.widget
8361 * @class RecordSet
8362 * @param data {Object || Object[]} An object literal or an array of data.
8363 * @constructor
8365 YAHOO.widget.RecordSet = function(data) {
8366 // Internal variables
8367 this._sName = "RecordSet instance" + YAHOO.widget.RecordSet._nCount;
8368 YAHOO.widget.RecordSet._nCount++;
8369 this._records = [];
8370 this._length = 0;
8372 if(data) {
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
8424 * from the Records.
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.
8432 * @event resetEvent
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);
8453 else {
8456 /////////////////////////////////////////////////////////////////////////////
8458 // Private member variables
8460 /////////////////////////////////////////////////////////////////////////////
8462 * Internal class variable to name multiple Recordset instances.
8464 * @property RecordSet._nCount
8465 * @type Number
8466 * @private
8467 * @static
8469 YAHOO.widget.RecordSet._nCount = 0;
8472 * Unique instance name.
8474 * @property _sName
8475 * @type String
8476 * @private
8478 YAHOO.widget.RecordSet.prototype._sName = null;
8481 * Internal variable to give unique indexes to Record instances.
8483 * @property _nCount
8484 * @type Number
8485 * @private
8487 YAHOO.widget.RecordSet.prototype._nRecordCount = 0;
8490 * Internal counter of how many Records are in the RecordSet.
8492 * @property _length
8493 * @type Number
8494 * @private
8496 YAHOO.widget.RecordSet.prototype._length = null;
8498 /////////////////////////////////////////////////////////////////////////////
8500 // Private methods
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.
8512 * @private
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);
8522 else {
8523 index = this.getLength();
8524 this._records.push(oRecord);
8526 this._length++;
8527 return 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
8537 * @private
8539 YAHOO.widget.RecordSet.prototype._deleteRecord = function(index, range) {
8540 if(!YAHOO.lang.isNumber(range) || (range < 0)) {
8541 range = 1;
8543 this._records.splice(index, range);
8544 this._length = this._length - range;
8547 /////////////////////////////////////////////////////////////////////////////
8549 // Public methods
8551 /////////////////////////////////////////////////////////////////////////////
8554 * Public accessor to the unique name of the RecordSet instance.
8556 * @method toString
8557 * @return {String} Unique name of the RecordSet instance.
8559 YAHOO.widget.RecordSet.prototype.toString = function() {
8560 return this._sName;
8564 * Returns the number of Records held in the RecordSet.
8566 * @method getLength
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.
8576 * @method getRecord
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];
8591 return null;
8596 * Returns an array of Records from the RecordSet.
8598 * @method getRecords
8599 * @param index {Number} (optional) Recordset position index of which Record to
8600 * start at.
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()) {
8626 return i;
8629 return null;
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.
8637 * @method addRecord
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});
8646 return oRecord;
8648 else {
8649 return null;
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
8656 * the RecordSet.
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});
8674 return newRecords;
8676 else if(aData && (aData.constructor == Object)) {
8677 var oRecord = this._addRecord(aData);
8678 this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
8679 return oRecord;
8681 else {
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) {
8695 var oRecord = null;
8696 if(YAHOO.lang.isNumber(record)) {
8697 oRecord = this._records[record];
8699 else if(record instanceof YAHOO.widget.Record) {
8700 oRecord = record;
8702 if(oRecord && oData && (oData.constructor == Object)) {
8703 // Copy data from the Record for the event that gets fired later
8704 var oldData = {};
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});
8710 return oRecord;
8712 else {
8713 return null;
8718 * Updates given Record at given key with given data.
8720 * @method updateKey
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) {
8727 var oRecord;
8729 if(YAHOO.lang.isNumber(record)) {
8730 oRecord = this._records[record];
8732 if(record instanceof YAHOO.widget.Record) {
8733 oRecord = record;
8735 var oldData = null;
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) {
8739 oldData = {};
8740 for(var key in keyValue) {
8741 oldData[key] = keyValue[key];
8744 // Copy by value
8745 else {
8746 oldData = keyValue;
8749 oRecord._oData[sKey] = oData;
8750 this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
8752 else {
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) {
8766 this.reset();
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();
8798 var oData = {};
8799 for(var key in oRecordData) {
8800 oData[key] = oRecordData[key];
8803 this._deleteRecord(index);
8804 this.fireEvent("recordDeleteEvent",{data:oData,index:index});
8805 return oData;
8807 else {
8808 return null;
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)) {
8823 range = 1;
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++) {
8830 var oData = {};
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});
8841 else {
8846 * Deletes all Records from the RecordSet.
8848 * @method reset
8850 YAHOO.widget.RecordSet.prototype.reset = function() {
8851 this._records = [];
8852 this._length = 0;
8853 this.fireEvent("resetEvent");
8857 /****************************************************************************/
8858 /****************************************************************************/
8859 /****************************************************************************/
8862 * The Record class defines a DataTable record.
8864 * @namespace YAHOO.widget
8865 * @class Record
8866 * @constructor
8867 * @param oConfigs {Object} (optional) Object literal of key/value pairs.
8869 YAHOO.widget.Record = function(oLiteral) {
8870 this._oData = {};
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
8885 * RecordSet.
8887 * @property _nId
8888 * @type Number
8889 * @private
8891 YAHOO.widget.Record.prototype._nId = null;
8894 * Holds data for the Record in an object literal.
8896 * @property _oData
8897 * @type Object
8898 * @private
8900 YAHOO.widget.Record.prototype._oData = null;
8902 /////////////////////////////////////////////////////////////////////////////
8904 // Public member variables
8906 /////////////////////////////////////////////////////////////////////////////
8908 /////////////////////////////////////////////////////////////////////////////
8910 // Public methods
8912 /////////////////////////////////////////////////////////////////////////////
8915 * Returns unique number assigned at instantiation, indicates original order
8916 * within RecordSet.
8918 * @method getId
8919 * @return Number
8921 YAHOO.widget.Record.prototype.getId = function() {
8922 return this._nId;
8926 * Returns data for the Record for a key if given, or the entire object
8927 * literal otherwise.
8929 * @method getData
8930 * @param sKey {String} (Optional) The key to retrieve a single data value.
8931 * @return Object
8933 YAHOO.widget.Record.prototype.getData = function(sKey) {
8934 if(YAHOO.lang.isString(sKey)) {
8935 return this._oData[sKey];
8937 else {
8938 return this._oData;
8943 YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.3.0", build: "442"});