2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
8
* Mechanism to execute a series of callbacks in a non-blocking queue. Each callback is executed via setTimout unless configured with a negative timeout, in which case it is run in blocking mode in the same execution thread as the previous callback. Callbacks can be function references or object literals with the following keys:
10
* <li><code>method</code> - {Function} REQUIRED the callback function.</li>
11
* <li><code>scope</code> - {Object} the scope from which to execute the callback. Default is the global window scope.</li>
12
* <li><code>argument</code> - {Array} parameters to be passed to method as individual arguments.</li>
13
* <li><code>timeout</code> - {number} millisecond delay to wait after previous callback completion before executing this callback. Negative values cause immediate blocking execution. Default 0.</li>
14
* <li><code>until</code> - {Function} boolean function executed before each iteration. Return true to indicate completion and proceed to the next callback.</li>
15
* <li><code>iterations</code> - {Number} number of times to execute the callback before proceeding to the next callback in the chain. Incompatible with <code>until</code>.</li>
18
* @namespace YAHOO.util
21
* @param callback* {Function|Object} Any number of callbacks to initialize the queue
23
YAHOO.util.Chain = function () {
30
this.q = [].slice.call(arguments);
33
* Event fired when the callback queue is emptied via execution (not via
34
* a call to chain.stop().
37
this.createEvent('end');
40
YAHOO.util.Chain.prototype = {
42
* Timeout id used to pause or stop execution and indicate the execution state of the Chain. 0 indicates paused or stopped, -1 indicates blocking execution, and any positive number indicates non-blocking execution.
50
* Begin executing the chain, or resume execution from the last paused position.
52
* @return {Chain} the Chain instance
55
// Grab the first callback in the queue
59
// If there is no callback in the queue or the Chain is currently
60
// in an execution mode, return
62
this.fireEvent('end');
70
if (typeof fn === 'function') {
71
var o = c.scope || {},
72
args = c.argument || [],
76
if (!(args instanceof Array)) {
80
// Execute immediately if the callback timeout is negative.
85
// Execute the callback from scope, with argument
88
} else if (c.iterations) {
89
for (;c.iterations-- > 0;) {
99
// If the until condition is set, check if we're done
102
// Shift this callback from the queue and execute the next
107
// Otherwise if either iterations is not set or we're
108
// executing the last iteration, shift callback from the queue
109
} else if (!c.iterations || !--c.iterations) {
113
// Otherwise set to execute after the configured timeout
114
this.id = setTimeout(function () {
115
// Execute the callback from scope, with argument
117
// Check if the Chain was not paused from inside the callback
119
// Indicate ready to run state
121
// Start the fun all over again
132
* Add a callback to the end of the queue
134
* @param c {Function|Object} the callback function ref or object literal
135
* @return {Chain} the Chain instance
143
* Pause the execution of the Chain after the current execution of the
144
* current callback completes. If called interstitially, clears the
145
* timeout for the pending callback. Paused Chains can be restarted with
148
* @return {Chain} the Chain instance
151
clearTimeout(this.id);
157
* Stop and clear the Chain's queue after the current execution of the
158
* current callback completes.
160
* @return {Chain} the Chain instance
168
YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider);
170
/****************************************************************************/
171
/****************************************************************************/
172
/****************************************************************************/
175
* The ColumnSet class defines and manages a DataTable's Columns,
176
* including nested hierarchies and access to individual Column instances.
178
* @namespace YAHOO.widget
180
* @uses YAHOO.util.EventProvider
182
* @param aDefinitions {Object[]} Array of object literals that define cells in
185
YAHOO.widget.ColumnSet = function(aDefinitions) {
186
this._sId = "yui-cs" + YAHOO.widget.ColumnSet._nCount;
188
// First clone the defs
189
aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions);
190
this._init(aDefinitions);
192
YAHOO.widget.ColumnSet._nCount++;
193
YAHOO.log("ColumnSet initialized", "info", this.toString());
196
/////////////////////////////////////////////////////////////////////////////
198
// Private member variables
200
/////////////////////////////////////////////////////////////////////////////
203
* Internal class variable to index multiple ColumnSet instances.
205
* @property ColumnSet._nCount
210
YAHOO.widget.ColumnSet._nCount = 0;
212
YAHOO.widget.ColumnSet.prototype = {
214
* Unique instance name.
223
* Array of object literal Column definitions passed to the constructor.
225
* @property _aDefinitions
229
_aDefinitions : null,
231
/////////////////////////////////////////////////////////////////////////////
233
// Public member variables
235
/////////////////////////////////////////////////////////////////////////////
238
* Top-down tree representation of Column hierarchy.
241
* @type YAHOO.widget.Column[]
246
* Flattened representation of all Columns.
249
* @type YAHOO.widget.Column[]
255
* Array of Columns that map one-to-one to a table column.
258
* @type YAHOO.widget.Column[]
264
* ID index of nested parent hierarchies for HEADERS accessibility attribute.
272
/////////////////////////////////////////////////////////////////////////////
276
/////////////////////////////////////////////////////////////////////////////
279
* Initializes ColumnSet instance with data from Column definitions.
282
* @param aDefinitions {Object[]} Array of object literals that define cells in
287
_init : function(aDefinitions) {
288
// DOM tree representation of all Columns
290
// Flat representation of all Columns
292
// Flat representation of only Columns that are meant to display data
294
// Array of HEADERS attribute values for all keys in the "keys" array
297
// Tracks current node list depth being tracked
300
// Internal recursive function to define Column instances
301
var parseColumns = function(nodeList, parent) {
305
// Create corresponding tree node if not already there for this depth
306
if(!tree[nodeDepth]) {
307
tree[nodeDepth] = [];
311
// Parse each node at this depth for attributes and any children
312
for(var j=0; j<nodeList.length; j++) {
313
var currentNode = nodeList[j];
315
// Instantiate a new Column for each node
316
var oColumn = new YAHOO.widget.Column(currentNode);
318
// Cross-reference Column ID back to the original object literal definition
319
currentNode.yuiColumnId = oColumn._sId;
321
// Add the new Column to the flat list
324
// Assign its parent as an attribute, if applicable
326
oColumn._oParent = parent;
329
// The Column has descendants
330
if(YAHOO.lang.isArray(currentNode.children)) {
331
oColumn.children = currentNode.children;
333
// Determine COLSPAN value for this Column
334
var terminalChildNodes = 0;
335
var countTerminalChildNodes = function(ancestor) {
336
var descendants = ancestor.children;
337
// Drill down each branch and count terminal nodes
338
for(var k=0; k<descendants.length; k++) {
339
// Keep drilling down
340
if(YAHOO.lang.isArray(descendants[k].children)) {
341
countTerminalChildNodes(descendants[k]);
343
// Reached branch terminus
345
terminalChildNodes++;
349
countTerminalChildNodes(currentNode);
350
oColumn._nColspan = terminalChildNodes;
352
// Cascade certain properties to children if not defined on their own
353
var currentChildren = currentNode.children;
354
for(var k=0; k<currentChildren.length; k++) {
355
var child = currentChildren[k];
356
if(oColumn.className && (child.className === undefined)) {
357
child.className = oColumn.className;
359
if(oColumn.editor && (child.editor === undefined)) {
360
child.editor = oColumn.editor;
363
if(oColumn.editorOptions && (child.editorOptions === undefined)) {
364
child.editorOptions = oColumn.editorOptions;
366
if(oColumn.formatter && (child.formatter === undefined)) {
367
child.formatter = oColumn.formatter;
369
if(oColumn.resizeable && (child.resizeable === undefined)) {
370
child.resizeable = oColumn.resizeable;
372
if(oColumn.sortable && (child.sortable === undefined)) {
373
child.sortable = oColumn.sortable;
378
if(oColumn.width && (child.width === undefined)) {
379
child.width = oColumn.width;
381
if(oColumn.minWidth && (child.minWidth === undefined)) {
382
child.minWidth = oColumn.minWidth;
384
if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) {
385
child.maxAutoWidth = oColumn.maxAutoWidth;
387
// Backward compatibility
388
if(oColumn.type && (child.type === undefined)) {
389
child.type = oColumn.type;
391
if(oColumn.type && !oColumn.formatter) {
392
YAHOO.log("The property type has been" +
393
" deprecated in favor of formatter", "warn", oColumn.toString());
394
oColumn.formatter = oColumn.type;
396
if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
397
YAHOO.log("The property text has been" +
398
" deprecated in favor of label", "warn", oColumn.toString());
399
oColumn.label = oColumn.text;
402
YAHOO.log("The property parser is no longer supported",
403
"warn", this.toString());
405
if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
406
(oColumn.sortOptions.descFunction))) {
407
YAHOO.log("The properties sortOptions.ascFunction and " +
408
" sortOptions.descFunction have been deprecated in favor " +
409
" of sortOptions.sortFunction", "warn", oColumn.toString());
413
// The children themselves must also be parsed for Column instances
414
if(!tree[nodeDepth+1]) {
415
tree[nodeDepth+1] = [];
417
parseColumns(currentChildren, oColumn);
419
// This Column does not have any children
421
oColumn._nKeyIndex = keys.length;
422
oColumn._nColspan = 1;
426
// Add the Column to the top-down tree
427
tree[nodeDepth].push(oColumn);
432
// Parse out Column instances from the array of object literals
433
if(YAHOO.lang.isArray(aDefinitions)) {
434
parseColumns(aDefinitions);
437
this._aDefinitions = aDefinitions;
440
YAHOO.log("Could not initialize ColumnSet due to invalid definitions","error");
446
// Determine ROWSPAN value for each Column in the tree
447
var parseTreeForRowspan = function(tree) {
452
// Calculate the max depth of descendants for this row
453
var countMaxRowDepth = function(row, tmpRowDepth) {
454
tmpRowDepth = tmpRowDepth || 1;
456
for(var n=0; n<row.length; n++) {
458
// Column has children, so keep counting
459
if(YAHOO.lang.isArray(col.children)) {
461
countMaxRowDepth(col.children, tmpRowDepth);
464
// No children, is it the max depth?
466
if(tmpRowDepth > maxRowDepth) {
467
maxRowDepth = tmpRowDepth;
474
// Count max row depth for each row
475
for(var m=0; m<tree.length; m++) {
476
currentRow = tree[m];
477
countMaxRowDepth(currentRow);
479
// Assign the right ROWSPAN values to each Column in the row
480
for(var p=0; p<currentRow.length; p++) {
481
currentColumn = currentRow[p];
482
if(!YAHOO.lang.isArray(currentColumn.children)) {
483
currentColumn._nRowspan = maxRowDepth;
486
currentColumn._nRowspan = 1;
490
// Reset counter for next row
494
parseTreeForRowspan(tree);
496
// Store tree index values
497
for(i=0; i<tree[0].length; i++) {
498
tree[0][i]._nTreeIndex = i;
501
// Store header relationships in an array for HEADERS attribute
502
var recurseAncestorsForHeaders = function(i, oColumn) {
503
headers[i].push(oColumn.getSanitizedKey());
504
if(oColumn._oParent) {
505
recurseAncestorsForHeaders(i, oColumn._oParent);
508
for(i=0; i<keys.length; i++) {
510
recurseAncestorsForHeaders(i, keys[i]);
511
headers[i] = headers[i].reverse();
514
// Save to the ColumnSet instance
518
this.headers = headers;
521
/////////////////////////////////////////////////////////////////////////////
525
/////////////////////////////////////////////////////////////////////////////
528
* Returns unique name of the ColumnSet instance.
531
* @return {String} Unique name of the ColumnSet instance.
539
* ColumnSet instance name, for logging.
542
* @return {String} Unique name of the ColumnSet instance.
545
toString : function() {
546
return "ColumnSet instance " + this._sId;
550
* Public accessor to the definitions array.
552
* @method getDefinitions
553
* @return {Object[]} Array of object literal Column definitions.
556
getDefinitions : function() {
557
var aDefinitions = this._aDefinitions;
559
// Internal recursive function to define Column instances
560
var parseColumns = function(nodeList, oSelf) {
561
// Parse each node at this depth for attributes and any children
562
for(var j=0; j<nodeList.length; j++) {
563
var currentNode = nodeList[j];
565
// Get the Column for each node
566
var oColumn = oSelf.getColumnById(currentNode.yuiColumnId);
569
// Update the current values
570
var oDefinition = oColumn.getDefinition();
571
for(var name in oDefinition) {
572
if(YAHOO.lang.hasOwnProperty(oDefinition, name)) {
573
currentNode[name] = oDefinition[name];
578
// The Column has descendants
579
if(YAHOO.lang.isArray(currentNode.children)) {
580
// The children themselves must also be parsed for Column instances
581
parseColumns(currentNode.children, oSelf);
586
parseColumns(aDefinitions, this);
587
this._aDefinitions = aDefinitions;
592
* Returns Column instance with given ID.
594
* @method getColumnById
595
* @param column {String} Column ID.
596
* @return {YAHOO.widget.Column} Column instance.
599
getColumnById : function(column) {
600
if(YAHOO.lang.isString(column)) {
601
var allColumns = this.flat;
602
for(var i=allColumns.length-1; i>-1; i--) {
603
if(allColumns[i]._sId === column) {
604
return allColumns[i];
612
* Returns Column instance with given key or ColumnSet key index.
615
* @param column {String | Number} Column key or ColumnSet key index.
616
* @return {YAHOO.widget.Column} Column instance.
619
getColumn : function(column) {
620
if(YAHOO.lang.isNumber(column) && this.keys[column]) {
621
return this.keys[column];
623
else if(YAHOO.lang.isString(column)) {
624
var allColumns = this.flat;
626
for(var i=0; i<allColumns.length; i++) {
627
if(allColumns[i].key === column) {
628
aColumns.push(allColumns[i]);
631
if(aColumns.length === 1) {
634
else if(aColumns.length > 1) {
642
* Public accessor returns array of given Column's desendants (if any), including itself.
644
* @method getDescendants
645
* @parem {YAHOO.widget.Column} Column instance.
646
* @return {Array} Array including the Column itself and all descendants (if any).
648
getDescendants : function(oColumn) {
650
var allDescendants = [];
653
// Recursive function to loop thru all children
654
var parse = function(oParent) {
655
allDescendants.push(oParent);
656
// This Column has children
657
if(oParent.children) {
658
for(i=0; i<oParent.children.length; i++) {
659
parse(oSelf.getColumn(oParent.children[i].key));
665
return allDescendants;
669
/****************************************************************************/
670
/****************************************************************************/
671
/****************************************************************************/
674
* The Column class defines and manages attributes of DataTable Columns
676
* @namespace YAHOO.widget
679
* @param oConfigs {Object} Object literal of definitions.
681
YAHOO.widget.Column = function(oConfigs) {
682
this._sId = "yui-col" + YAHOO.widget.Column._nCount;
684
// Object literal defines Column attributes
685
if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
686
for(var sConfig in oConfigs) {
688
this[sConfig] = oConfigs[sConfig];
693
// Assign a key if not found
694
if(!YAHOO.lang.isValue(this.key)) {
695
this.key = "yui-dt-col" + YAHOO.widget.Column._nCount;
698
// Assign a field if not found, defaults to key
699
if(!YAHOO.lang.isValue(this.field)) {
700
this.field = this.key;
704
YAHOO.widget.Column._nCount++;
706
// Backward compatibility
707
if(this.width && !YAHOO.lang.isNumber(this.width)) {
709
YAHOO.log("The Column property width must be a number", "warn", this.toString());
711
if(this.editor && YAHOO.lang.isString(this.editor)) {
712
this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions);
713
YAHOO.log("The Column property editor must be an instance of YAHOO.widget.CellEditor", "warn", this.toString());
717
/////////////////////////////////////////////////////////////////////////////
719
// Private member variables
721
/////////////////////////////////////////////////////////////////////////////
723
YAHOO.lang.augmentObject(YAHOO.widget.Column, {
725
* Internal class variable to index multiple Column instances.
727
* @property Column._nCount
734
formatCheckbox : function(elCell, oRecord, oColumn, oData) {
735
YAHOO.log("The method YAHOO.widget.Column.formatCheckbox() has been" +
736
" deprecated in favor of YAHOO.widget.DataTable.formatCheckbox()", "warn",
737
"YAHOO.widget.Column.formatCheckbox");
738
YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
741
formatCurrency : function(elCell, oRecord, oColumn, oData) {
742
YAHOO.log("The method YAHOO.widget.Column.formatCurrency() has been" +
743
" deprecated in favor of YAHOO.widget.DataTable.formatCurrency()", "warn",
744
"YAHOO.widget.Column.formatCurrency");
745
YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
748
formatDate : function(elCell, oRecord, oColumn, oData) {
749
YAHOO.log("The method YAHOO.widget.Column.formatDate() has been" +
750
" deprecated in favor of YAHOO.widget.DataTable.formatDate()", "warn",
751
"YAHOO.widget.Column.formatDate");
752
YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
755
formatEmail : function(elCell, oRecord, oColumn, oData) {
756
YAHOO.log("The method YAHOO.widget.Column.formatEmail() has been" +
757
" deprecated in favor of YAHOO.widget.DataTable.formatEmail()", "warn",
758
"YAHOO.widget.Column.formatEmail");
759
YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
762
formatLink : function(elCell, oRecord, oColumn, oData) {
763
YAHOO.log("The method YAHOO.widget.Column.formatLink() has been" +
764
" deprecated in favor of YAHOO.widget.DataTable.formatLink()", "warn",
765
"YAHOO.widget.Column.formatLink");
766
YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
769
formatNumber : function(elCell, oRecord, oColumn, oData) {
770
YAHOO.log("The method YAHOO.widget.Column.formatNumber() has been" +
771
" deprecated in favor of YAHOO.widget.DataTable.formatNumber()", "warn",
772
"YAHOO.widget.Column.formatNumber");
773
YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
776
formatSelect : function(elCell, oRecord, oColumn, oData) {
777
YAHOO.log("The method YAHOO.widget.Column.formatSelect() has been" +
778
" deprecated in favor of YAHOO.widget.DataTable.formatDropdown()", "warn",
779
"YAHOO.widget.Column.formatSelect");
780
YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
784
YAHOO.widget.Column.prototype = {
786
* Unique String identifier assigned at instantiation.
795
* Reference to Column's current position index within its ColumnSet's keys
796
* array, if applicable. This property only applies to non-nested and bottom-
797
* level child Columns.
799
* @property _nKeyIndex
806
* Reference to Column's current position index within its ColumnSet's tree
807
* array, if applicable. This property only applies to non-nested and top-
808
* level parent Columns.
810
* @property _nTreeIndex
817
* Number of table cells the Column spans.
819
* @property _nColspan
826
* Number of table rows the Column spans.
828
* @property _nRowspan
835
* Column's parent Column instance, or null.
838
* @type YAHOO.widget.Column
844
* The DOM reference to the associated TH element.
853
* The DOM reference to the associated TH element's liner DIV element.
855
* @property _elThLiner
862
* The DOM reference to the associated TH element's label SPAN element.
864
* @property _elThLabel
871
* The DOM reference to the associated resizerelement (if any).
873
* @property _elResizer
880
* Internal width tracker.
889
* For unreg() purposes, a reference to the Column's DragDrop instance.
892
* @type YAHOO.util.DragDrop
898
* For unreg() purposes, a reference to the Column resizer's DragDrop instance.
900
* @property _ddResizer
901
* @type YAHOO.util.DragDrop
906
/////////////////////////////////////////////////////////////////////////////
908
// Public member variables
910
/////////////////////////////////////////////////////////////////////////////
913
* Unique name, required.
921
* Associated database field, or null.
929
* Text or HTML for display as Column's label in the TH element.
937
* Column head cell ABBR for accessibility.
945
* Array of object literals that define children (nested headers) of a Column.
953
* Column width (in pixels).
961
* Minimum Column width (in pixels).
970
* When a width is not defined for a Column, maxAutoWidth defines an upper
971
* limit that the Column should be auto-sized to. If resizeable is enabled,
972
* users may still resize to a greater width. Most useful for Columns intended
973
* to hold long unbroken, unwrapped Strings, such as URLs, to prevent very
974
* wide Columns from disrupting visual readability by inducing truncation.
976
* @property maxAutoWidth
983
* True if Column is in hidden state.
992
* True if Column is in selected state.
1001
* Custom CSS class or array of classes to be applied to every cell in the Column.
1003
* @property className
1004
* @type String || String[]
1009
* Defines a format function.
1011
* @property formatter
1012
* @type String || HTMLFunction
1017
* Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
1019
* @property currencyOptions
1023
currencyOptions : null,
1026
* Config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
1028
* @property dateOptions
1035
* A CellEditor instance, otherwise Column is not editable.
1038
* @type YAHOO.widget.CellEditor
1043
* True if Column is resizeable, false otherwise. The Drag & Drop Utility is
1044
* required to enable this feature. Only bottom-level and non-nested Columns are
1047
* @property resizeable
1054
* True if Column is sortable, false otherwise.
1056
* @property sortable
1063
* @property sortOptions.defaultOrder
1064
* @deprecated Use sortOptions.defaultDir.
1067
* Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC.
1069
* @property sortOptions.defaultDir
1074
* Custom field to sort on.
1076
* @property sortOptions.field
1081
* Custom sort handler.
1083
* @property sortOptions.sortFunction
1103
/////////////////////////////////////////////////////////////////////////////
1107
/////////////////////////////////////////////////////////////////////////////
1110
* Returns unique ID string.
1113
* @return {String} Unique ID string.
1115
getId : function() {
1120
* Column instance name, for logging.
1123
* @return {String} Column's unique name.
1125
toString : function() {
1126
return "Column instance " + this._sId;
1130
* Returns object literal definition.
1132
* @method getDefinition
1133
* @return {Object} Object literal definition.
1135
getDefinition : function() {
1136
var oDefinition = {};
1138
// Update the definition
1139
oDefinition.abbr = this.abbr;
1140
oDefinition.className = this.className;
1141
oDefinition.editor = this.editor;
1142
oDefinition.editorOptions = this.editorOptions; //TODO: deprecated
1143
oDefinition.field = this.field;
1144
oDefinition.formatter = this.formatter;
1145
oDefinition.hidden = this.hidden;
1146
oDefinition.key = this.key;
1147
oDefinition.label = this.label;
1148
oDefinition.minWidth = this.minWidth;
1149
oDefinition.maxAutoWidth = this.maxAutoWidth;
1150
oDefinition.resizeable = this.resizeable;
1151
oDefinition.selected = this.selected;
1152
oDefinition.sortable = this.sortable;
1153
oDefinition.sortOptions = this.sortOptions;
1154
oDefinition.width = this.width;
1160
* Returns unique Column key.
1163
* @return {String} Column key.
1165
getKey : function() {
1173
* @return {String} Column field.
1175
getField : function() {
1180
* Returns Column key which has been sanitized for DOM (class and ID) usage
1181
* starts with letter, contains only letters, numbers, hyphen, or period.
1183
* @method getSanitizedKey
1184
* @return {String} Sanitized Column key.
1186
getSanitizedKey : function() {
1187
return this.getKey().replace(/[^\w\-]/g,"");
1191
* Public accessor returns Column's current position index within its
1192
* ColumnSet's keys array, if applicable. Only non-nested and bottom-level
1193
* child Columns will return a value.
1195
* @method getKeyIndex
1196
* @return {Number} Position index, or null.
1198
getKeyIndex : function() {
1199
return this._nKeyIndex;
1203
* Public accessor returns Column's current position index within its
1204
* ColumnSet's tree array, if applicable. Only non-nested and top-level parent
1205
* Columns will return a value;
1207
* @method getTreeIndex
1208
* @return {Number} Position index, or null.
1210
getTreeIndex : function() {
1211
return this._nTreeIndex;
1215
* Public accessor returns Column's parent instance if any, or null otherwise.
1218
* @return {YAHOO.widget.Column} Column's parent instance.
1220
getParent : function() {
1221
return this._oParent;
1225
* Public accessor returns Column's calculated COLSPAN value.
1227
* @method getColspan
1228
* @return {Number} Column's COLSPAN value.
1230
getColspan : function() {
1231
return this._nColspan;
1233
// Backward compatibility
1234
getColSpan : function() {
1235
YAHOO.log("The method getColSpan() has been" +
1236
" deprecated in favor of getColspan()", "warn", this.toString());
1237
return this.getColspan();
1241
* Public accessor returns Column's calculated ROWSPAN value.
1243
* @method getRowspan
1244
* @return {Number} Column's ROWSPAN value.
1246
getRowspan : function() {
1247
return this._nRowspan;
1251
* Returns DOM reference to the key TH element.
1254
* @return {HTMLElement} TH element.
1256
getThEl : function() {
1261
* Returns DOM reference to the TH's liner DIV element. Introduced since
1262
* resizeable Columns may have an extra resizer liner, making the DIV liner
1263
* not reliably the TH element's first child.
1265
* @method getThLInerEl
1266
* @return {HTMLElement} TH element.
1268
getThLinerEl : function() {
1269
return this._elThLiner;
1273
* Returns DOM reference to the resizer element, or null.
1275
* @method getResizerEl
1276
* @return {HTMLElement} DIV element.
1278
getResizerEl : function() {
1279
return this._elResizer;
1282
// Backward compatibility
1285
* @deprecated Use getThEl
1287
getColEl : function() {
1288
YAHOO.log("The method getColEl() has been" +
1289
" deprecated in favor of getThEl()", "warn",
1291
return this.getThEl();
1293
getIndex : function() {
1294
YAHOO.log("The method getIndex() has been" +
1295
" deprecated in favor of getKeyIndex()", "warn",
1297
return this.getKeyIndex();
1299
format : function() {
1300
YAHOO.log("The method format() has been deprecated in favor of the " +
1301
"DataTable method formatCell()", "error", this.toString());
1305
/****************************************************************************/
1306
/****************************************************************************/
1307
/****************************************************************************/
1310
* Sort static utility to support Column sorting.
1312
* @namespace YAHOO.util
1317
/////////////////////////////////////////////////////////////////////////////
1321
/////////////////////////////////////////////////////////////////////////////
1324
* Comparator function for simple case-insensitive string sorting.
1327
* @param a {Object} First sort argument.
1328
* @param b {Object} Second sort argument.
1329
* @param desc {Boolean} True if sort direction is descending, false if
1330
* sort direction is ascending.
1332
compare: function(a, b, desc) {
1333
if((a === null) || (typeof a == "undefined")) {
1334
if((b === null) || (typeof b == "undefined")) {
1341
else if((b === null) || (typeof b == "undefined")) {
1345
if(a.constructor == String) {
1346
a = a.toLowerCase();
1348
if(b.constructor == String) {
1349
b = b.toLowerCase();
1352
return (desc) ? 1 : -1;
1355
return (desc) ? -1 : 1;
1363
/****************************************************************************/
1364
/****************************************************************************/
1365
/****************************************************************************/
1368
* ColumnDD subclasses DragDrop to support rearrangeable Columns.
1370
* @namespace YAHOO.util
1372
* @extends YAHOO.util.DDProxy
1374
* @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
1375
* @param oColumn {YAHOO.widget.Column} Column instance.
1376
* @param elTh {HTMLElement} TH element reference.
1377
* @param elTarget {HTMLElement} Drag target element.
1379
YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) {
1380
if(oDataTable && oColumn && elTh && elTarget) {
1381
this.datatable = oDataTable;
1382
this.table = oDataTable.getTableEl();
1383
this.column = oColumn;
1384
this.headCell = elTh;
1385
this.pointer = elTarget;
1386
this.newIndex = null;
1388
this.initFrame(); // Needed for DDProxy
1389
this.invalidHandleTypes = {};
1391
// Set top/bottom padding to account for children of nested columns
1392
this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0);
1394
YAHOO.util.Event.on(window, 'resize', function() {
1395
this.initConstraints();
1399
YAHOO.log("Column dragdrop could not be created","warn",oDataTable.toString());
1403
if(YAHOO.util.DDProxy) {
1404
YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, {
1405
initConstraints: function() {
1406
//Get the top, right, bottom and left positions
1407
var region = YAHOO.util.Dom.getRegion(this.table),
1408
//Get the element we are working on
1410
//Get the xy position of it
1411
xy = YAHOO.util.Dom.getXY(el),
1412
//Get the width and height
1413
width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10),
1414
height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10),
1415
//Set left to x minus left
1416
left = ((xy[0] - region.left) + 15), //Buffer of 15px
1417
//Set right to right minus x minus width
1418
right = ((region.right - xy[0] - width) + 15);
1420
//Set the constraints based on the above calculations
1421
this.setXConstraint(left, right);
1422
this.setYConstraint(10, 10);
1424
_resizeProxy: function() {
1425
this.constructor.superclass._resizeProxy.apply(this, arguments);
1426
var dragEl = this.getDragEl(),
1429
YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px');
1430
YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block');
1431
var xy = YAHOO.util.Dom.getXY(el);
1432
YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
1434
YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px");
1435
YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
1436
YAHOO.util.Dom.setXY(this.dragEl, xy);
1438
onMouseDown: function() {
1439
this.initConstraints();
1440
this.resetConstraints();
1442
clickValidator: function(e) {
1443
if(!this.column.hidden) {
1444
var target = YAHOO.util.Event.getTarget(e);
1445
return ( this.isValidHandleChild(target) &&
1446
(this.id == this.handleElId ||
1447
this.DDM.handleWasClicked(target, this.id)) );
1450
onDragOver: function(ev, id) {
1451
// Validate target as a Column
1452
var target = this.datatable.getColumn(id);
1454
// Validate target as a top-level parent
1455
var targetIndex = target.getTreeIndex();
1456
while((targetIndex === null) && target.getParent()) {
1457
target = target.getParent();
1458
targetIndex = target.getTreeIndex();
1460
if(targetIndex !== null) {
1461
// Are we placing to left or right of target?
1462
var elTarget = target.getThEl();
1463
var newIndex = targetIndex;
1464
var mouseX = YAHOO.util.Event.getPageX(ev),
1465
targetX = YAHOO.util.Dom.getX(elTarget),
1466
midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2),
1467
currentIndex = this.column.getTreeIndex();
1469
if (mouseX < midX) {
1470
YAHOO.util.Dom.setX(this.pointer, targetX);
1472
var targetWidth = parseInt(elTarget.offsetWidth, 10);
1473
YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth));
1476
if (targetIndex > currentIndex) {
1482
else if(newIndex > this.datatable.getColumnSet().tree[0].length) {
1483
newIndex = this.datatable.getColumnSet().tree[0].length;
1485
this.newIndex = newIndex;
1489
onDragDrop: function() {
1490
this.datatable.reorderColumn(this.column, this.newIndex);
1492
endDrag: function() {
1493
this.newIndex = null;
1494
YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none');
1499
/****************************************************************************/
1500
/****************************************************************************/
1501
/****************************************************************************/
1504
* ColumnResizer subclasses DragDrop to support resizeable Columns.
1506
* @namespace YAHOO.util
1507
* @class ColumnResizer
1508
* @extends YAHOO.util.DDProxy
1510
* @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
1511
* @param oColumn {YAHOO.widget.Column} Column instance.
1512
* @param elTh {HTMLElement} TH element reference.
1513
* @param sHandleElId {String} DOM ID of the handle element that causes the resize.
1514
* @param elProxy {HTMLElement} Resizer proxy element.
1516
YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) {
1517
if(oDataTable && oColumn && elTh && sHandleId) {
1518
this.datatable = oDataTable;
1519
this.column = oColumn;
1520
this.headCell = elTh;
1521
this.headCellLiner = oColumn.getThLinerEl();
1522
this.resizerLiner = elTh.firstChild;
1523
this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id});
1524
this.initFrame(); // Needed for proxy
1525
this.resetResizerEl(); // Needed when rowspan > 0
1527
// Set right padding for bug 1858462
1528
this.setPadding(0, 1, 0, 0);
1531
YAHOO.log("Column resizer could not be created","warn",oDataTable.toString());
1536
YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, {
1537
/////////////////////////////////////////////////////////////////////////////
1541
/////////////////////////////////////////////////////////////////////////////
1543
* Resets resizer element.
1545
* @method resetResizerEl
1547
resetResizerEl : function() {
1548
var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style;
1549
resizerStyle.left = "auto";
1550
resizerStyle.right = 0;
1551
resizerStyle.top = "auto";
1552
resizerStyle.bottom = 0;
1553
resizerStyle.height = this.headCell.offsetHeight+"px";
1556
/////////////////////////////////////////////////////////////////////////////
1558
// Public DOM event handlers
1560
/////////////////////////////////////////////////////////////////////////////
1563
* Handles mouseup events on the Column resizer.
1566
* @param e {string} The mouseup event
1568
onMouseUp : function(e) {
1569
// Reset height of all resizer els in case TH's have changed height
1570
var allKeys = this.datatable.getColumnSet().keys,
1572
for(var i=0, len=allKeys.length; i<len; i++) {
1574
if(col._ddResizer) {
1575
col._ddResizer.resetResizerEl();
1578
this.resetResizerEl();
1580
var el = this.headCellLiner;
1581
var newWidth = el.offsetWidth -
1582
(parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) -
1583
(parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0);
1585
this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth});
1589
* Handles mousedown events on the Column resizer.
1591
* @method onMouseDown
1592
* @param e {string} The mousedown event
1594
onMouseDown : function(e) {
1595
this.startWidth = this.headCellLiner.offsetWidth;
1596
this.startX = YAHOO.util.Event.getXY(e)[0];
1597
this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) +
1598
(parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
1602
* Custom clickValidator to ensure Column is not in hidden state.
1604
* @method clickValidator
1608
clickValidator : function(e) {
1609
if(!this.column.hidden) {
1610
var target = YAHOO.util.Event.getTarget(e);
1611
return ( this.isValidHandleChild(target) &&
1612
(this.id == this.handleElId ||
1613
this.DDM.handleWasClicked(target, this.id)) );
1618
* Handles start drag on the Column resizer.
1621
* @param e {string} The drag event
1623
startDrag : function() {
1624
// Shrinks height of all resizer els to not hold open TH els
1625
var allKeys = this.datatable.getColumnSet().keys,
1626
thisKey = this.column.getKeyIndex(),
1628
for(var i=0, len=allKeys.length; i<len; i++) {
1630
if(col._ddResizer) {
1631
YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em";
1637
* Handles drag events on the Column resizer.
1640
* @param e {string} The drag event
1642
onDrag : function(e) {
1643
var newX = YAHOO.util.Event.getXY(e)[0];
1644
if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) {
1645
var offsetX = newX - this.startX;
1646
var newWidth = this.startWidth + offsetX - this.nLinerPadding;
1648
this.datatable.setColumnWidth(this.column, newWidth);
1655
/////////////////////////////////////////////////////////////////////////////
1659
/////////////////////////////////////////////////////////////////////////////
1662
* @property editorOptions
1663
* @deprecated Pass configs directly to CellEditor constructor.
1669
var lang = YAHOO.lang,
1671
widget = YAHOO.widget,
1675
DT = widget.DataTable;
1677
/****************************************************************************/
1678
/****************************************************************************/
1679
/****************************************************************************/
1682
* A RecordSet defines and manages a set of Records.
1684
* @namespace YAHOO.widget
1686
* @param data {Object || Object[]} An object literal or an array of data.
1689
YAHOO.widget.RecordSet = function(data) {
1690
// Internal variables
1691
this._sId = "yui-rs" + widget.RecordSet._nCount;
1692
widget.RecordSet._nCount++;
1697
if(lang.isArray(data)) {
1698
this.addRecords(data);
1700
else if(lang.isObject(data)) {
1701
this.addRecord(data);
1705
YAHOO.log("RecordSet initialized", "info", this.toString());
1708
var RS = widget.RecordSet;
1711
* Internal class variable to name multiple Recordset instances.
1713
* @property RecordSet._nCount
1722
/////////////////////////////////////////////////////////////////////////////
1724
// Private member variables
1726
/////////////////////////////////////////////////////////////////////////////
1728
* Unique String identifier assigned at instantiation.
1737
* Internal counter of how many Records are in the RecordSet.
1742
* @deprecated No longer used
1746
/////////////////////////////////////////////////////////////////////////////
1750
/////////////////////////////////////////////////////////////////////////////
1753
* Adds one Record to the RecordSet at the given index. If index is null,
1754
* then adds the Record to the end of the RecordSet.
1756
* @method _addRecord
1757
* @param oData {Object} An object literal of data.
1758
* @param index {Number} (optional) Position index.
1759
* @return {YAHOO.widget.Record} A Record instance.
1762
_addRecord : function(oData, index) {
1763
var oRecord = new YAHOO.widget.Record(oData);
1765
if(YAHOO.lang.isNumber(index) && (index > -1)) {
1766
this._records.splice(index,0,oRecord);
1769
//index = this.getLength();
1770
//this._records[index] = oRecord;
1771
this._records[this._records.length] = oRecord;
1778
* Sets/replaces one Record to the RecordSet at the given index. Existing
1779
* Records with higher indexes are not shifted. If no index specified, the
1780
* Record is added to the end of the RecordSet.
1782
* @method _setRecord
1783
* @param oData {Object} An object literal of data.
1784
* @param index {Number} (optional) Position index.
1785
* @return {YAHOO.widget.Record} A Record instance.
1788
_setRecord : function(oData, index) {
1789
if (!lang.isNumber(index) || index < 0) {
1790
index = this._records.length;
1792
return (this._records[index] = new widget.Record(oData));
1794
if(lang.isNumber(index) && (index > -1)) {
1795
this._records[index] = oRecord;
1796
if((index+1) > this.getLength()) {
1797
this._length = index+1;
1801
this._records[this.getLength()] = oRecord;
1809
* Deletes Records from the RecordSet at the given index. If range is null,
1810
* then only one Record is deleted.
1812
* @method _deleteRecord
1813
* @param index {Number} Position index.
1814
* @param range {Number} (optional) How many Records to delete
1817
_deleteRecord : function(index, range) {
1818
if(!lang.isNumber(range) || (range < 0)) {
1821
this._records.splice(index, range);
1822
//this._length = this._length - range;
1825
/////////////////////////////////////////////////////////////////////////////
1829
/////////////////////////////////////////////////////////////////////////////
1832
* Returns unique name of the RecordSet instance.
1835
* @return {String} Unique name of the RecordSet instance.
1837
getId : function() {
1842
* Public accessor to the unique name of the RecordSet instance.
1845
* @return {String} Unique name of the RecordSet instance.
1847
toString : function() {
1848
return "RecordSet instance " + this._sId;
1852
* Returns the number of Records held in the RecordSet.
1855
* @return {Number} Number of records in the RecordSet.
1857
getLength : function() {
1858
//return this._length;
1859
return this._records.length;
1863
* Returns Record by ID or RecordSet position index.
1866
* @param record {YAHOO.widget.Record | Number | String} Record instance,
1867
* RecordSet position index, or Record ID.
1868
* @return {YAHOO.widget.Record} Record object.
1870
getRecord : function(record) {
1872
if(record instanceof widget.Record) {
1873
for(i=0; i<this._records.length; i++) {
1874
if(this._records[i] && (this._records[i]._sId === record._sId)) {
1879
else if(lang.isNumber(record)) {
1880
if((record > -1) && (record < this.getLength())) {
1881
return this._records[record];
1884
else if(lang.isString(record)) {
1885
for(i=0; i<this._records.length; i++) {
1886
if(this._records[i] && (this._records[i]._sId === record)) {
1887
return this._records[i];
1891
// Not a valid Record for this RecordSet
1897
* Returns an array of Records from the RecordSet.
1899
* @method getRecords
1900
* @param index {Number} (optional) Recordset position index of which Record to
1902
* @param range {Number} (optional) Number of Records to get.
1903
* @return {YAHOO.widget.Record[]} Array of Records starting at given index and
1904
* length equal to given range. If index is not given, all Records are returned.
1906
getRecords : function(index, range) {
1907
if(!lang.isNumber(index)) {
1908
return this._records;
1910
if(!lang.isNumber(range)) {
1911
return this._records.slice(index);
1913
return this._records.slice(index, index+range);
1917
* Returns a boolean indicating whether Records exist in the RecordSet at the
1918
* specified index range. Returns true if and only if a Record exists at each
1919
* index in the range.
1920
* @method hasRecords
1923
* @return {Boolean} true if all indices are populated in the RecordSet
1925
hasRecords : function (index, range) {
1926
var recs = this.getRecords(index,range);
1927
for (var i = 0; i < range; ++i) {
1928
if (typeof recs[i] === 'undefined') {
1936
* Returns current position index for the given Record.
1938
* @method getRecordIndex
1939
* @param oRecord {YAHOO.widget.Record} Record instance.
1940
* @return {Number} Record's RecordSet position index.
1943
getRecordIndex : function(oRecord) {
1945
for(var i=this._records.length-1; i>-1; i--) {
1946
if(this._records[i] && oRecord.getId() === this._records[i].getId()) {
1956
* Adds one Record to the RecordSet at the given index. If index is null,
1957
* then adds the Record to the end of the RecordSet.
1960
* @param oData {Object} An object literal of data.
1961
* @param index {Number} (optional) Position index.
1962
* @return {YAHOO.widget.Record} A Record instance.
1964
addRecord : function(oData, index) {
1965
if(lang.isObject(oData)) {
1966
var oRecord = this._addRecord(oData, index);
1967
this.fireEvent("recordAddEvent",{record:oRecord,data:oData});
1968
YAHOO.log("Added Record at index " + index +
1969
" with data " + lang.dump(oData), "info", this.toString());
1973
YAHOO.log("Could not add Record with data" +
1974
lang.dump(oData), "info", this.toString());
1980
* Adds multiple Records at once to the RecordSet at the given index with the
1981
* given object literal data. If index is null, then the new Records are
1982
* added to the end of the RecordSet.
1984
* @method addRecords
1985
* @param aData {Object[]} An object literal data or an array of data object literals.
1986
* @param index {Number} (optional) Position index.
1987
* @return {YAHOO.widget.Record[]} An array of Record instances.
1989
addRecords : function(aData, index) {
1990
if(lang.isArray(aData)) {
1991
var newRecords = [],
1994
index = lang.isNumber(index) ? index : this._records.length;
1997
// Can't go backwards bc we need to preserve order
1998
for(i=0,len=aData.length; i<len; ++i) {
1999
if(lang.isObject(aData[i])) {
2000
var record = this._addRecord(aData[i], idx++);
2001
newRecords.push(record);
2004
this.fireEvent("recordsAddEvent",{records:newRecords,data:aData});
2005
YAHOO.log("Added " + newRecords.length + " Record(s) at index " + index +
2006
" with data " + lang.dump(aData), "info", this.toString());
2009
else if(lang.isObject(aData)) {
2010
var oRecord = this._addRecord(aData);
2011
this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
2012
YAHOO.log("Added 1 Record at index " + index +
2013
" with data " + lang.dump(aData), "info", this.toString());
2017
YAHOO.log("Could not add Records with data " +
2018
lang.dump(aData), "info", this.toString());
2024
* Sets or replaces one Record to the RecordSet at the given index. Unlike
2025
* addRecord, an existing Record at that index is not shifted to preserve it.
2026
* If no index is specified, it adds the Record to the end of the RecordSet.
2029
* @param oData {Object} An object literal of data.
2030
* @param index {Number} (optional) Position index.
2031
* @return {YAHOO.widget.Record} A Record instance.
2033
setRecord : function(oData, index) {
2034
if(lang.isObject(oData)) {
2035
var oRecord = this._setRecord(oData, index);
2036
this.fireEvent("recordSetEvent",{record:oRecord,data:oData});
2037
YAHOO.log("Set Record at index " + index +
2038
" with data " + lang.dump(oData), "info", this.toString());
2042
YAHOO.log("Could not set Record with data" +
2043
lang.dump(oData), "info", this.toString());
2049
* Sets or replaces multiple Records at once to the RecordSet with the given
2050
* data, starting at the given index. If index is not specified, then the new
2051
* Records are added to the end of the RecordSet.
2053
* @method setRecords
2054
* @param aData {Object[]} An array of object literal data.
2055
* @param index {Number} (optional) Position index.
2056
* @return {YAHOO.widget.Record[]} An array of Record instances.
2058
setRecords : function(aData, index) {
2059
var Rec = widget.Record,
2060
a = lang.isArray(aData) ? aData : [aData],
2062
i = 0, l = a.length, j = 0;
2064
index = parseInt(index,10)|0;
2067
if (typeof a[i] === 'object' && a[i]) {
2068
added[j++] = this._records[index + i] = new Rec(a[i]);
2072
this.fireEvent("recordsSetEvent",{records:added,data:aData});
2073
// Backward compatibility for bug 1918245
2074
this.fireEvent("recordsSet",{records:added,data:aData});
2075
YAHOO.log("Set "+j+" Record(s) at index "+index, "info",
2078
if (a.length && !added.length) {
2079
YAHOO.log("Could not set Records with data " +
2080
lang.dump(aData), "info", this.toString());
2083
return added.length > 1 ? added : added[0];
2087
* Updates given Record with given data.
2089
* @method updateRecord
2090
* @param record {YAHOO.widget.Record | Number | String} A Record instance,
2091
* a RecordSet position index, or a Record ID.
2092
* @param oData {Object} Object literal of new data.
2093
* @return {YAHOO.widget.Record} Updated Record, or null.
2095
updateRecord : function(record, oData) {
2096
var oRecord = this.getRecord(record);
2097
if(oRecord && lang.isObject(oData)) {
2098
// Copy data from the Record for the event that gets fired later
2100
for(var key in oRecord._oData) {
2101
if(lang.hasOwnProperty(oRecord._oData, key)) {
2102
oldData[key] = oRecord._oData[key];
2105
oRecord._oData = oData;
2106
this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData});
2107
YAHOO.log("Record at index " + this.getRecordIndex(oRecord) +
2108
" updated with data " + lang.dump(oData), "info", this.toString());
2112
YAHOO.log("Could not update Record " + record, "error", this.toString());
2119
* @deprecated Use updateRecordValue
2121
updateKey : function(record, sKey, oData) {
2122
this.updateRecordValue(record, sKey, oData);
2125
* Sets given Record at given key to given data.
2127
* @method updateRecordValue
2128
* @param record {YAHOO.widget.Record | Number | String} A Record instance,
2129
* a RecordSet position index, or a Record ID.
2130
* @param sKey {String} Key name.
2131
* @param oData {Object} New data.
2133
updateRecordValue : function(record, sKey, oData) {
2134
var oRecord = this.getRecord(record);
2137
var keyValue = oRecord._oData[sKey];
2138
// Copy data from the Record for the event that gets fired later
2139
if(keyValue && lang.isObject(keyValue)) {
2141
for(var key in keyValue) {
2142
if(lang.hasOwnProperty(keyValue, key)) {
2143
oldData[key] = keyValue[key];
2152
oRecord._oData[sKey] = oData;
2153
this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
2154
this.fireEvent("recordValueUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
2155
YAHOO.log("Key \"" + sKey +
2156
"\" for Record at index " + this.getRecordIndex(oRecord) +
2157
" updated to \"" + lang.dump(oData) + "\"", "info", this.toString());
2160
YAHOO.log("Could not update key " + sKey + " for Record " + record, "error", this.toString());
2165
* Replaces all Records in RecordSet with new object literal data.
2167
* @method replaceRecords
2168
* @param data {Object || Object[]} An object literal of data or an array of
2169
* data object literals.
2170
* @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or
2171
* an array of Records.
2173
replaceRecords : function(data) {
2175
return this.addRecords(data);
2179
* Sorts all Records by given function. Records keep their unique IDs but will
2180
* have new RecordSet position indexes.
2182
* @method sortRecords
2183
* @param fnSort {Function} Reference to a sort function.
2184
* @param desc {Boolean} True if sort direction is descending, false if sort
2185
* direction is ascending.
2186
* @return {YAHOO.widget.Record[]} Sorted array of Records.
2188
sortRecords : function(fnSort, desc) {
2189
return this._records.sort(function(a, b) {return fnSort(a, b, desc);});
2193
* Reverses all Records, so ["one", "two", "three"] becomes ["three", "two", "one"].
2195
* @method reverseRecords
2196
* @return {YAHOO.widget.Record[]} Reverse-sorted array of Records.
2198
reverseRecords : function() {
2199
return this._records.reverse();
2203
* Removes the Record at the given position index from the RecordSet. If a range
2204
* is also provided, removes that many Records, starting from the index. Length
2205
* of RecordSet is correspondingly shortened.
2207
* @method deleteRecord
2208
* @param index {Number} Record's RecordSet position index.
2209
* @param range {Number} (optional) How many Records to delete.
2210
* @return {Object} A copy of the data held by the deleted Record.
2212
deleteRecord : function(index) {
2213
if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
2214
// Copy data from the Record for the event that gets fired later
2215
var oData = widget.DataTable._cloneObject(this.getRecord(index).getData());
2217
this._deleteRecord(index);
2218
this.fireEvent("recordDeleteEvent",{data:oData,index:index});
2219
YAHOO.log("Record deleted at index " + index +
2220
" and containing data " + lang.dump(oData), "info", this.toString());
2224
YAHOO.log("Could not delete Record at index " + index, "error", this.toString());
2230
* Removes the Record at the given position index from the RecordSet. If a range
2231
* is also provided, removes that many Records, starting from the index. Length
2232
* of RecordSet is correspondingly shortened.
2234
* @method deleteRecords
2235
* @param index {Number} Record's RecordSet position index.
2236
* @param range {Number} (optional) How many Records to delete.
2237
* @return {Object[]} An array of copies of the data held by the deleted Records.
2239
deleteRecords : function(index, range) {
2240
if(!lang.isNumber(range)) {
2243
if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
2244
var recordsToDelete = this.getRecords(index, range);
2245
// Copy data from each Record for the event that gets fired later
2246
var deletedData = [];
2248
for(var i=0; i<recordsToDelete.length; i++) {
2249
deletedData[deletedData.length] = widget.DataTable._cloneObject(recordsToDelete[i]);
2251
this._deleteRecord(index, range);
2253
this.fireEvent("recordsDeleteEvent",{data:deletedData,index:index});
2254
YAHOO.log(range + "Record(s) deleted at index " + index +
2255
" and containing data " + lang.dump(deletedData), "info", this.toString());
2260
YAHOO.log("Could not delete Records at index " + index, "error", this.toString());
2266
* Deletes all Records from the RecordSet.
2270
reset : function() {
2273
this.fireEvent("resetEvent");
2274
YAHOO.log("All Records deleted from RecordSet", "info", this.toString());
2278
/////////////////////////////////////////////////////////////////////////////
2282
/////////////////////////////////////////////////////////////////////////////
2284
// RecordSet uses EventProvider
2285
lang.augmentProto(RS, util.EventProvider);
2288
* Fired when a new Record is added to the RecordSet.
2290
* @event recordAddEvent
2291
* @param oArgs.record {YAHOO.widget.Record} The Record instance.
2292
* @param oArgs.data {Object} Data added.
2296
* Fired when multiple Records are added to the RecordSet at once.
2298
* @event recordsAddEvent
2299
* @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
2300
* @param oArgs.data {Object[]} Data added.
2304
* Fired when a Record is set in the RecordSet.
2306
* @event recordSetEvent
2307
* @param oArgs.record {YAHOO.widget.Record} The Record instance.
2308
* @param oArgs.data {Object} Data added.
2312
* Fired when multiple Records are set in the RecordSet at once.
2314
* @event recordsSetEvent
2315
* @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
2316
* @param oArgs.data {Object[]} Data added.
2320
* Fired when a Record is updated with new data.
2322
* @event recordUpdateEvent
2323
* @param oArgs.record {YAHOO.widget.Record} The Record instance.
2324
* @param oArgs.newData {Object} New data.
2325
* @param oArgs.oldData {Object} Old data.
2329
* Fired when a Record is deleted from the RecordSet.
2331
* @event recordDeleteEvent
2332
* @param oArgs.data {Object} A copy of the data held by the Record,
2333
* or an array of data object literals if multiple Records were deleted at once.
2334
* @param oArgs.index {Object} Index of the deleted Record.
2338
* Fired when multiple Records are deleted from the RecordSet at once.
2340
* @event recordsDeleteEvent
2341
* @param oArgs.data {Object[]} An array of data object literals copied
2343
* @param oArgs.index {Object} Index of the first deleted Record.
2347
* Fired when all Records are deleted from the RecordSet at once.
2353
* @event keyUpdateEvent
2354
* @deprecated Use recordValueUpdateEvent
2358
* Fired when a Record value is updated with new data.
2360
* @event recordValueUpdateEvent
2361
* @param oArgs.record {YAHOO.widget.Record} The Record instance.
2362
* @param oArgs.key {String} The updated key.
2363
* @param oArgs.newData {Object} New data.
2364
* @param oArgs.oldData {Object} Old data.
2369
/****************************************************************************/
2370
/****************************************************************************/
2371
/****************************************************************************/
2374
* The Record class defines a DataTable record.
2376
* @namespace YAHOO.widget
2379
* @param oConfigs {Object} (optional) Object literal of key/value pairs.
2381
YAHOO.widget.Record = function(oLiteral) {
2382
this._nCount = widget.Record._nCount;
2383
this._sId = "yui-rec" + this._nCount;
2384
widget.Record._nCount++;
2386
if(lang.isObject(oLiteral)) {
2387
for(var sKey in oLiteral) {
2388
if(lang.hasOwnProperty(oLiteral, sKey)) {
2389
this._oData[sKey] = oLiteral[sKey];
2395
/////////////////////////////////////////////////////////////////////////////
2397
// Private member variables
2399
/////////////////////////////////////////////////////////////////////////////
2402
* Internal class variable to give unique IDs to Record instances.
2404
* @property Record._nCount
2408
YAHOO.widget.Record._nCount = 0;
2410
YAHOO.widget.Record.prototype = {
2412
* Immutable unique count assigned at instantiation. Remains constant while a
2413
* Record's position index can change from sorting.
2422
* Immutable unique ID assigned at instantiation. Remains constant while a
2423
* Record's position index can change from sorting.
2432
* Holds data for the Record in an object literal.
2440
/////////////////////////////////////////////////////////////////////////////
2442
// Public member variables
2444
/////////////////////////////////////////////////////////////////////////////
2446
/////////////////////////////////////////////////////////////////////////////
2450
/////////////////////////////////////////////////////////////////////////////
2453
* Returns unique count assigned at instantiation.
2458
getCount : function() {
2459
return this._nCount;
2463
* Returns unique ID assigned at instantiation.
2468
getId : function() {
2473
* Returns data for the Record for a field if given, or the entire object
2474
* literal otherwise.
2477
* @param sField {String} (Optional) The field from which to retrieve data value.
2480
getData : function(sField) {
2481
if(lang.isString(sField)) {
2482
return this._oData[sField];
2490
* Sets given data at the given key. Use the RecordSet method setValue to trigger
2494
* @param sKey {String} The key of the new value.
2495
* @param oData {MIXED} The new value.
2497
setData : function(sKey, oData) {
2498
this._oData[sKey] = oData;
2506
var lang = YAHOO.lang,
2508
widget = YAHOO.widget,
2513
DS = util.DataSourceBase;
2516
* The DataTable widget provides a progressively enhanced DHTML control for
2517
* displaying tabular data across A-grade browsers.
2520
* @requires yahoo, dom, event, element, datasource
2521
* @optional dragdrop, dragdrop
2522
* @title DataTable Widget
2525
/****************************************************************************/
2526
/****************************************************************************/
2527
/****************************************************************************/
2530
* DataTable class for the YUI DataTable widget.
2532
* @namespace YAHOO.widget
2534
* @extends YAHOO.util.Element
2536
* @param elContainer {HTMLElement} Container element for the TABLE.
2537
* @param aColumnDefs {Object[]} Array of object literal Column definitions.
2538
* @param oDataSource {YAHOO.util.DataSource} DataSource instance.
2539
* @param oConfigs {object} (optional) Object literal of configuration values.
2541
YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
2542
var DT = widget.DataTable;
2544
////////////////////////////////////////////////////////////////////////////
2545
// Backward compatibility for SDT, but prevent infinite loops
2547
if(oConfigs && oConfigs.scrollable) {
2548
return new YAHOO.widget.ScrollingDataTable(elContainer,aColumnDefs,oDataSource,oConfigs);
2551
////////////////////////////////////////////////////////////////////////////
2555
this._nIndex = DT._nCount;
2556
this._sId = "yui-dt"+this._nIndex;
2557
this._oChainRender = new YAHOO.util.Chain();
2558
this._oChainRender.subscribe("end",this._onRenderChainEnd, this, true);
2560
// Initialize configs
2561
this._initConfigs(oConfigs);
2563
// Initialize DataSource
2564
this._initDataSource(oDataSource);
2565
if(!this._oDataSource) {
2566
YAHOO.log("Could not instantiate DataTable due to an invalid DataSource", "error", this.toString());
2570
// Initialize ColumnSet
2571
this._initColumnSet(aColumnDefs);
2572
if(!this._oColumnSet) {
2573
YAHOO.log("Could not instantiate DataTable due to an invalid ColumnSet", "error", this.toString());
2577
// Initialize RecordSet
2578
this._initRecordSet();
2579
if(!this._oRecordSet) {
2582
// Initialize Attributes
2583
DT.superclass.constructor.call(this, elContainer, this.configs);
2585
// Initialize DOM elements
2586
var okDom = this._initDomElements(elContainer);
2588
YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
2592
// Show message as soon as config is available
2593
this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
2595
////////////////////////////////////////////////////////////////////////////
2596
// Once per instance
2600
DT._nCurrentCount++;
2602
////////////////////////////////////////////////////////////////////////////
2605
// Send a simple initial request
2607
success : this.onDataReturnSetRows,
2608
failure : this.onDataReturnSetRows,
2610
argument: this.getState()
2613
var initialLoad = this.get("initialLoad");
2614
if(initialLoad === true) {
2615
this._oDataSource.sendRequest(this.get("initialRequest"), oCallback);
2617
// Do not send an initial request at all
2618
else if(initialLoad === false) {
2619
this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
2621
// Send an initial request with a custom payload
2623
var oCustom = initialLoad || {};
2624
oCallback.argument = oCustom.argument || {};
2625
this._oDataSource.sendRequest(oCustom.request, oCallback);
2629
var DT = widget.DataTable;
2631
/////////////////////////////////////////////////////////////////////////////
2635
/////////////////////////////////////////////////////////////////////////////
2637
lang.augmentObject(DT, {
2640
* Class name assigned to outer DataTable container.
2642
* @property DataTable.CLASS_DATATABLE
2648
CLASS_DATATABLE : "yui-dt",
2651
* Class name assigned to liner DIV elements.
2653
* @property DataTable.CLASS_LINER
2657
* @default "yui-dt-liner"
2659
CLASS_LINER : "yui-dt-liner",
2662
* Class name assigned to display label elements.
2664
* @property DataTable.CLASS_LABEL
2668
* @default "yui-dt-label"
2670
CLASS_LABEL : "yui-dt-label",
2673
* Class name assigned to messaging elements.
2675
* @property DataTable.CLASS_MESSAGE
2679
* @default "yui-dt-message"
2681
CLASS_MESSAGE : "yui-dt-message",
2684
* Class name assigned to mask element when DataTable is disabled.
2686
* @property DataTable.CLASS_MASK
2690
* @default "yui-dt-mask"
2692
CLASS_MASK : "yui-dt-mask",
2695
* Class name assigned to data elements.
2697
* @property DataTable.CLASS_DATA
2701
* @default "yui-dt-data"
2703
CLASS_DATA : "yui-dt-data",
2706
* Class name assigned to Column drag target.
2708
* @property DataTable.CLASS_COLTARGET
2712
* @default "yui-dt-coltarget"
2714
CLASS_COLTARGET : "yui-dt-coltarget",
2717
* Class name assigned to resizer handle elements.
2719
* @property DataTable.CLASS_RESIZER
2723
* @default "yui-dt-resizer"
2725
CLASS_RESIZER : "yui-dt-resizer",
2728
* Class name assigned to resizer liner elements.
2730
* @property DataTable.CLASS_RESIZERLINER
2734
* @default "yui-dt-resizerliner"
2736
CLASS_RESIZERLINER : "yui-dt-resizerliner",
2739
* Class name assigned to resizer proxy elements.
2741
* @property DataTable.CLASS_RESIZERPROXY
2745
* @default "yui-dt-resizerproxy"
2747
CLASS_RESIZERPROXY : "yui-dt-resizerproxy",
2750
* Class name assigned to CellEditor container elements.
2752
* @property DataTable.CLASS_EDITOR
2756
* @default "yui-dt-editor"
2758
CLASS_EDITOR : "yui-dt-editor",
2761
* Class name assigned to paginator container elements.
2763
* @property DataTable.CLASS_PAGINATOR
2767
* @default "yui-dt-paginator"
2769
CLASS_PAGINATOR : "yui-dt-paginator",
2772
* Class name assigned to page number indicators.
2774
* @property DataTable.CLASS_PAGE
2778
* @default "yui-dt-page"
2780
CLASS_PAGE : "yui-dt-page",
2783
* Class name assigned to default indicators.
2785
* @property DataTable.CLASS_DEFAULT
2789
* @default "yui-dt-default"
2791
CLASS_DEFAULT : "yui-dt-default",
2794
* Class name assigned to previous indicators.
2796
* @property DataTable.CLASS_PREVIOUS
2800
* @default "yui-dt-previous"
2802
CLASS_PREVIOUS : "yui-dt-previous",
2805
* Class name assigned next indicators.
2807
* @property DataTable.CLASS_NEXT
2811
* @default "yui-dt-next"
2813
CLASS_NEXT : "yui-dt-next",
2816
* Class name assigned to first elements.
2818
* @property DataTable.CLASS_FIRST
2822
* @default "yui-dt-first"
2824
CLASS_FIRST : "yui-dt-first",
2827
* Class name assigned to last elements.
2829
* @property DataTable.CLASS_LAST
2833
* @default "yui-dt-last"
2835
CLASS_LAST : "yui-dt-last",
2838
* Class name assigned to even elements.
2840
* @property DataTable.CLASS_EVEN
2844
* @default "yui-dt-even"
2846
CLASS_EVEN : "yui-dt-even",
2849
* Class name assigned to odd elements.
2851
* @property DataTable.CLASS_ODD
2855
* @default "yui-dt-odd"
2857
CLASS_ODD : "yui-dt-odd",
2860
* Class name assigned to selected elements.
2862
* @property DataTable.CLASS_SELECTED
2866
* @default "yui-dt-selected"
2868
CLASS_SELECTED : "yui-dt-selected",
2871
* Class name assigned to highlighted elements.
2873
* @property DataTable.CLASS_HIGHLIGHTED
2877
* @default "yui-dt-highlighted"
2879
CLASS_HIGHLIGHTED : "yui-dt-highlighted",
2882
* Class name assigned to hidden elements.
2884
* @property DataTable.CLASS_HIDDEN
2888
* @default "yui-dt-hidden"
2890
CLASS_HIDDEN : "yui-dt-hidden",
2893
* Class name assigned to disabled elements.
2895
* @property DataTable.CLASS_DISABLED
2899
* @default "yui-dt-disabled"
2901
CLASS_DISABLED : "yui-dt-disabled",
2904
* Class name assigned to empty indicators.
2906
* @property DataTable.CLASS_EMPTY
2910
* @default "yui-dt-empty"
2912
CLASS_EMPTY : "yui-dt-empty",
2915
* Class name assigned to loading indicatorx.
2917
* @property DataTable.CLASS_LOADING
2921
* @default "yui-dt-loading"
2923
CLASS_LOADING : "yui-dt-loading",
2926
* Class name assigned to error indicators.
2928
* @property DataTable.CLASS_ERROR
2932
* @default "yui-dt-error"
2934
CLASS_ERROR : "yui-dt-error",
2937
* Class name assigned to editable elements.
2939
* @property DataTable.CLASS_EDITABLE
2943
* @default "yui-dt-editable"
2945
CLASS_EDITABLE : "yui-dt-editable",
2948
* Class name assigned to draggable elements.
2950
* @property DataTable.CLASS_DRAGGABLE
2954
* @default "yui-dt-draggable"
2956
CLASS_DRAGGABLE : "yui-dt-draggable",
2959
* Class name assigned to resizeable elements.
2961
* @property DataTable.CLASS_RESIZEABLE
2965
* @default "yui-dt-resizeable"
2967
CLASS_RESIZEABLE : "yui-dt-resizeable",
2970
* Class name assigned to scrollable elements.
2972
* @property DataTable.CLASS_SCROLLABLE
2976
* @default "yui-dt-scrollable"
2978
CLASS_SCROLLABLE : "yui-dt-scrollable",
2981
* Class name assigned to sortable elements.
2983
* @property DataTable.CLASS_SORTABLE
2987
* @default "yui-dt-sortable"
2989
CLASS_SORTABLE : "yui-dt-sortable",
2992
* Class name assigned to ascending elements.
2994
* @property DataTable.CLASS_ASC
2998
* @default "yui-dt-asc"
3000
CLASS_ASC : "yui-dt-asc",
3003
* Class name assigned to descending elements.
3005
* @property DataTable.CLASS_DESC
3009
* @default "yui-dt-desc"
3011
CLASS_DESC : "yui-dt-desc",
3014
* Class name assigned to BUTTON elements and/or container elements.
3016
* @property DataTable.CLASS_BUTTON
3020
* @default "yui-dt-button"
3022
CLASS_BUTTON : "yui-dt-button",
3025
* Class name assigned to INPUT TYPE=CHECKBOX elements and/or container elements.
3027
* @property DataTable.CLASS_CHECKBOX
3031
* @default "yui-dt-checkbox"
3033
CLASS_CHECKBOX : "yui-dt-checkbox",
3036
* Class name assigned to SELECT elements and/or container elements.
3038
* @property DataTable.CLASS_DROPDOWN
3042
* @default "yui-dt-dropdown"
3044
CLASS_DROPDOWN : "yui-dt-dropdown",
3047
* Class name assigned to INPUT TYPE=RADIO elements and/or container elements.
3049
* @property DataTable.CLASS_RADIO
3053
* @default "yui-dt-radio"
3055
CLASS_RADIO : "yui-dt-radio",
3057
/////////////////////////////////////////////////////////////////////////
3059
// Private static properties
3061
/////////////////////////////////////////////////////////////////////////
3064
* Internal class variable for indexing multiple DataTable instances.
3066
* @property DataTable._nCount
3074
* Internal class variable tracking current number of DataTable instances,
3075
* so that certain class values can be reset when all instances are destroyed.
3077
* @property DataTable._nCurrentCount
3085
* Reference to the STYLE node that is dynamically created and updated
3086
* in order to manage Column widths.
3088
* @property DataTable._elDynStyleNode
3093
_elDynStyleNode : null,
3096
* Set to true if _elDynStyleNode cannot be populated due to browser incompatibility.
3098
* @property DataTable._bDynStylesFallback
3103
_bDynStylesFallback : (ua.ie && (ua.ie<7)) ? true : false,
3106
* Object literal hash of Columns and their dynamically create style rules.
3108
* @property DataTable._oDynStyles
3116
* Element reference to shared Column drag target.
3118
* @property DataTable._elColumnDragTarget
3123
_elColumnDragTarget : null,
3126
* Element reference to shared Column resizer proxy.
3128
* @property DataTable._elColumnResizerProxy
3133
_elColumnResizerProxy : null,
3135
/////////////////////////////////////////////////////////////////////////
3137
// Private static methods
3139
/////////////////////////////////////////////////////////////////////////
3142
* Clones object literal or array of object literals.
3144
* @method DataTable._cloneObject
3145
* @param o {Object} Object.
3149
_cloneObject : function(o) {
3150
if(!lang.isValue(o)) {
3156
if(o instanceof YAHOO.widget.BaseCellEditor) {
3159
else if(lang.isFunction(o)) {
3162
else if(lang.isArray(o)) {
3164
for(var i=0,len=o.length;i<len;i++) {
3165
array[i] = DT._cloneObject(o[i]);
3169
else if(lang.isObject(o)) {
3171
if(lang.hasOwnProperty(o, x)) {
3172
if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
3173
copy[x] = DT._cloneObject(o[x]);
3189
* Destroys shared Column drag target.
3191
* @method DataTable._destroyColumnDragTargetEl
3195
_destroyColumnDragTargetEl : function() {
3196
if(DT._elColumnDragTarget) {
3197
var el = DT._elColumnDragTarget;
3198
YAHOO.util.Event.purgeElement(el);
3199
el.parentNode.removeChild(el);
3200
DT._elColumnDragTarget = null;
3206
* Creates HTML markup for shared Column drag target.
3208
* @method DataTable._initColumnDragTargetEl
3209
* @return {HTMLElement} Reference to Column drag target.
3213
_initColumnDragTargetEl : function() {
3214
if(!DT._elColumnDragTarget) {
3215
// Attach Column drag target element as first child of body
3216
var elColumnDragTarget = document.createElement('div');
3217
elColumnDragTarget.className = DT.CLASS_COLTARGET;
3218
elColumnDragTarget.style.display = "none";
3219
document.body.insertBefore(elColumnDragTarget, document.body.firstChild);
3221
// Internal tracker of Column drag target
3222
DT._elColumnDragTarget = elColumnDragTarget;
3225
return DT._elColumnDragTarget;
3229
* Destroys shared Column resizer proxy.
3231
* @method DataTable._destroyColumnResizerProxyEl
3232
* @return {HTMLElement} Reference to Column resizer proxy.
3236
_destroyColumnResizerProxyEl : function() {
3237
if(DT._elColumnResizerProxy) {
3238
var el = DT._elColumnResizerProxy;
3239
YAHOO.util.Event.purgeElement(el);
3240
el.parentNode.removeChild(el);
3241
DT._elColumnResizerProxy = null;
3246
* Creates HTML markup for shared Column resizer proxy.
3248
* @method DataTable._initColumnResizerProxyEl
3249
* @return {HTMLElement} Reference to Column resizer proxy.
3253
_initColumnResizerProxyEl : function() {
3254
if(!DT._elColumnResizerProxy) {
3255
// Attach Column resizer element as first child of body
3256
var elColumnResizerProxy = document.createElement("div");
3257
elColumnResizerProxy.id = "yui-dt-colresizerproxy"; // Needed for ColumnResizer
3258
elColumnResizerProxy.className = DT.CLASS_RESIZERPROXY;
3259
document.body.insertBefore(elColumnResizerProxy, document.body.firstChild);
3261
// Internal tracker of Column resizer proxy
3262
DT._elColumnResizerProxy = elColumnResizerProxy;
3264
return DT._elColumnResizerProxy;
3268
* Formats a BUTTON element.
3270
* @method DataTable.formatButton
3271
* @param el {HTMLElement} The element to format with markup.
3272
* @param oRecord {YAHOO.widget.Record} Record instance.
3273
* @param oColumn {YAHOO.widget.Column} Column instance.
3274
* @param oData {Object | Boolean} Data value for the cell. By default, the value
3275
* is what gets written to the BUTTON.
3278
formatButton : function(el, oRecord, oColumn, oData) {
3279
var sValue = lang.isValue(oData) ? oData : "Click";
3280
//TODO: support YAHOO.widget.Button
3281
//if(YAHOO.widget.Button) {
3285
el.innerHTML = "<button type=\"button\" class=\""+
3286
DT.CLASS_BUTTON + "\">" + sValue + "</button>";
3291
* Formats a CHECKBOX element.
3293
* @method DataTable.formatCheckbox
3294
* @param el {HTMLElement} The element to format with markup.
3295
* @param oRecord {YAHOO.widget.Record} Record instance.
3296
* @param oColumn {YAHOO.widget.Column} Column instance.
3297
* @param oData {Object | Boolean} Data value for the cell. Can be a simple
3298
* Boolean to indicate whether checkbox is checked or not. Can be object literal
3299
* {checked:bBoolean, label:sLabel}. Other forms of oData require a custom
3303
formatCheckbox : function(el, oRecord, oColumn, oData) {
3304
var bChecked = oData;
3305
bChecked = (bChecked) ? " checked=\"checked\"" : "";
3306
el.innerHTML = "<input type=\"checkbox\"" + bChecked +
3307
" class=\"" + DT.CLASS_CHECKBOX + "\" />";
3311
* Formats currency. Default unit is USD.
3313
* @method DataTable.formatCurrency
3314
* @param el {HTMLElement} The element to format with markup.
3315
* @param oRecord {YAHOO.widget.Record} Record instance.
3316
* @param oColumn {YAHOO.widget.Column} Column instance.
3317
* @param oData {Number} Data value for the cell.
3320
formatCurrency : function(el, oRecord, oColumn, oData) {
3321
el.innerHTML = util.Number.format(oData, oColumn.currencyOptions || this.get("currencyOptions"));
3325
* Formats JavaScript Dates.
3327
* @method DataTable.formatDate
3328
* @param el {HTMLElement} The element to format with markup.
3329
* @param oRecord {YAHOO.widget.Record} Record instance.
3330
* @param oColumn {YAHOO.widget.Column} Column instance.
3331
* @param oData {Object} Data value for the cell, or null.
3334
formatDate : function(el, oRecord, oColumn, oData) {
3335
var oConfig = oColumn.dateOptions || this.get("dateOptions");
3336
el.innerHTML = util.Date.format(oData, oConfig, oConfig.locale);
3340
* Formats SELECT elements.
3342
* @method DataTable.formatDropdown
3343
* @param el {HTMLElement} The element to format with markup.
3344
* @param oRecord {YAHOO.widget.Record} Record instance.
3345
* @param oColumn {YAHOO.widget.Column} Column instance.
3346
* @param oData {Object} Data value for the cell, or null.
3349
formatDropdown : function(el, oRecord, oColumn, oData) {
3350
var selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field),
3351
options = (lang.isArray(oColumn.dropdownOptions)) ?
3352
oColumn.dropdownOptions : null,
3355
collection = el.getElementsByTagName("select");
3357
// Create the form element only once, so we can attach the onChange listener
3358
if(collection.length === 0) {
3359
// Create SELECT element
3360
selectEl = document.createElement("select");
3361
selectEl.className = DT.CLASS_DROPDOWN;
3362
selectEl = el.appendChild(selectEl);
3364
// Add event listener
3365
Ev.addListener(selectEl,"change",this._onDropdownChange,this);
3368
selectEl = collection[0];
3370
// Update the form element
3372
// Clear out previous options
3373
selectEl.innerHTML = "";
3375
// We have options to populate
3377
// Create OPTION elements
3378
for(var i=0; i<options.length; i++) {
3379
var option = options[i];
3380
var optionEl = document.createElement("option");
3381
optionEl.value = (lang.isValue(option.value)) ?
3382
option.value : option;
3383
// Bug 2334323: Support legacy text, support label for consistency with DropdownCellEditor
3384
optionEl.innerHTML = (lang.isValue(option.text)) ?
3385
option.text : (lang.isValue(option.label)) ? option.label : option;
3386
optionEl = selectEl.appendChild(optionEl);
3387
if (optionEl.value == selectedValue) {
3388
optionEl.selected = true;
3392
// Selected value is our only option
3394
selectEl.innerHTML = "<option selected value=\"" + selectedValue + "\">" + selectedValue + "</option>";
3398
el.innerHTML = lang.isValue(oData) ? oData : "";
3405
* @method DataTable.formatEmail
3406
* @param el {HTMLElement} The element to format with markup.
3407
* @param oRecord {YAHOO.widget.Record} Record instance.
3408
* @param oColumn {YAHOO.widget.Column} Column instance.
3409
* @param oData {Object} Data value for the cell, or null.
3412
formatEmail : function(el, oRecord, oColumn, oData) {
3413
if(lang.isString(oData)) {
3414
el.innerHTML = "<a href=\"mailto:" + oData + "\">" + oData + "</a>";
3417
el.innerHTML = lang.isValue(oData) ? oData : "";
3424
* @method DataTable.formatLink
3425
* @param el {HTMLElement} The element to format with markup.
3426
* @param oRecord {YAHOO.widget.Record} Record instance.
3427
* @param oColumn {YAHOO.widget.Column} Column instance.
3428
* @param oData {Object} Data value for the cell, or null.
3431
formatLink : function(el, oRecord, oColumn, oData) {
3432
if(lang.isString(oData)) {
3433
el.innerHTML = "<a href=\"" + oData + "\">" + oData + "</a>";
3436
el.innerHTML = lang.isValue(oData) ? oData : "";
3443
* @method DataTable.formatNumber
3444
* @param el {HTMLElement} The element to format with markup.
3445
* @param oRecord {YAHOO.widget.Record} Record instance.
3446
* @param oColumn {YAHOO.widget.Column} Column instance.
3447
* @param oData {Object} Data value for the cell, or null.
3450
formatNumber : function(el, oRecord, oColumn, oData) {
3451
el.innerHTML = util.Number.format(oData, oColumn.numberOptions || this.get("numberOptions"));
3455
* Formats INPUT TYPE=RADIO elements.
3457
* @method DataTable.formatRadio
3458
* @param el {HTMLElement} The element to format with markup.
3459
* @param oRecord {YAHOO.widget.Record} Record instance.
3460
* @param oColumn {YAHOO.widget.Column} Column instance.
3461
* @param oData {Object} (Optional) Data value for the cell.
3464
formatRadio : function(el, oRecord, oColumn, oData) {
3465
var bChecked = oData;
3466
bChecked = (bChecked) ? " checked=\"checked\"" : "";
3467
el.innerHTML = "<input type=\"radio\"" + bChecked +
3468
" name=\""+this.getId()+"-col-" + oColumn.getSanitizedKey() + "\"" +
3469
" class=\"" + DT.CLASS_RADIO+ "\" />";
3473
* Formats text strings.
3475
* @method DataTable.formatText
3476
* @param el {HTMLElement} The element to format with markup.
3477
* @param oRecord {YAHOO.widget.Record} Record instance.
3478
* @param oColumn {YAHOO.widget.Column} Column instance.
3479
* @param oData {Object} (Optional) Data value for the cell.
3482
formatText : function(el, oRecord, oColumn, oData) {
3483
var value = (lang.isValue(oData)) ? oData : "";
3484
//TODO: move to util function
3485
el.innerHTML = value.toString().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
3489
* Formats TEXTAREA elements.
3491
* @method DataTable.formatTextarea
3492
* @param el {HTMLElement} The element to format with markup.
3493
* @param oRecord {YAHOO.widget.Record} Record instance.
3494
* @param oColumn {YAHOO.widget.Column} Column instance.
3495
* @param oData {Object} (Optional) Data value for the cell.
3498
formatTextarea : function(el, oRecord, oColumn, oData) {
3499
var value = (lang.isValue(oData)) ? oData : "",
3500
markup = "<textarea>" + value + "</textarea>";
3501
el.innerHTML = markup;
3505
* Formats INPUT TYPE=TEXT elements.
3507
* @method DataTable.formatTextbox
3508
* @param el {HTMLElement} The element to format with markup.
3509
* @param oRecord {YAHOO.widget.Record} Record instance.
3510
* @param oColumn {YAHOO.widget.Column} Column instance.
3511
* @param oData {Object} (Optional) Data value for the cell.
3514
formatTextbox : function(el, oRecord, oColumn, oData) {
3515
var value = (lang.isValue(oData)) ? oData : "",
3516
markup = "<input type=\"text\" value=\"" + value + "\" />";
3517
el.innerHTML = markup;
3521
* Default cell formatter
3523
* @method DataTable.formatDefault
3524
* @param el {HTMLElement} The element to format with markup.
3525
* @param oRecord {YAHOO.widget.Record} Record instance.
3526
* @param oColumn {YAHOO.widget.Column} Column instance.
3527
* @param oData {Object} (Optional) Data value for the cell.
3530
formatDefault : function(el, oRecord, oColumn, oData) {
3531
el.innerHTML = oData === undefined ||
3533
(typeof oData === 'number' && isNaN(oData)) ?
3534
" " : oData.toString();
3538
* Validates data value to type Number, doing type conversion as
3539
* necessary. A valid Number value is return, else null is returned
3540
* if input value does not validate.
3543
* @method DataTable.validateNumber
3544
* @param oData {Object} Data to validate.
3547
validateNumber : function(oData) {
3549
var number = oData * 1;
3552
if(lang.isNumber(number)) {
3556
YAHOO.log("Could not validate data " + lang.dump(oData) + " to type Number", "warn", this.toString());
3562
// Done in separate step so referenced functions are defined.
3564
* Cell formatting functions.
3565
* @property DataTable.Formatter
3570
button : DT.formatButton,
3571
checkbox : DT.formatCheckbox,
3572
currency : DT.formatCurrency,
3573
"date" : DT.formatDate,
3574
dropdown : DT.formatDropdown,
3575
email : DT.formatEmail,
3576
link : DT.formatLink,
3577
"number" : DT.formatNumber,
3578
radio : DT.formatRadio,
3579
text : DT.formatText,
3580
textarea : DT.formatTextarea,
3581
textbox : DT.formatTextbox,
3583
defaultFormatter : DT.formatDefault
3586
lang.extend(DT, util.Element, {
3588
/////////////////////////////////////////////////////////////////////////////
3590
// Superclass methods
3592
/////////////////////////////////////////////////////////////////////////////
3595
* Implementation of Element's abstract method. Sets up config values.
3597
* @method initAttributes
3598
* @param oConfigs {Object} (Optional) Object literal definition of configuration values.
3602
initAttributes : function(oConfigs) {
3603
oConfigs = oConfigs || {};
3604
DT.superclass.initAttributes.call(this, oConfigs);
3607
* @attribute summary
3608
* @description Value for the SUMMARY attribute.
3612
this.setAttributeConfig("summary", {
3614
validator: lang.isString,
3615
method: function(sSummary) {
3617
this._elTable.summary = sSummary;
3623
* @attribute selectionMode
3624
* @description Specifies row or cell selection mode. Accepts the following strings:
3626
* <dt>"standard"</dt>
3627
* <dd>Standard row selection with support for modifier keys to enable
3628
* multiple selections.</dd>
3631
* <dd>Row selection with modifier keys disabled to not allow
3632
* multiple selections.</dd>
3634
* <dt>"singlecell"</dt>
3635
* <dd>Cell selection with modifier keys disabled to not allow
3636
* multiple selections.</dd>
3638
* <dt>"cellblock"</dt>
3639
* <dd>Cell selection with support for modifier keys to enable multiple
3640
* selections in a block-fashion, like a spreadsheet.</dd>
3642
* <dt>"cellrange"</dt>
3643
* <dd>Cell selection with support for modifier keys to enable multiple
3644
* selections in a range-fashion, like a calendar.</dd>
3647
* @default "standard"
3650
this.setAttributeConfig("selectionMode", {
3652
validator: lang.isString
3656
* @attribute sortedBy
3657
* @description Object literal provides metadata for initial sort values if
3658
* data will arrive pre-sorted:
3660
* <dt>sortedBy.key</dt>
3661
* <dd>{String} Key of sorted Column</dd>
3662
* <dt>sortedBy.dir</dt>
3663
* <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
3665
* @type Object | null
3667
this.setAttributeConfig("sortedBy", {
3669
// TODO: accepted array for nested sorts
3670
validator: function(oNewSortedBy) {
3672
return (lang.isObject(oNewSortedBy) && oNewSortedBy.key);
3675
return (oNewSortedBy === null);
3678
method: function(oNewSortedBy) {
3679
// Stash the previous value
3680
var oOldSortedBy = this.get("sortedBy");
3682
// Workaround for bug 1827195
3683
this._configs.sortedBy.value = oNewSortedBy;
3685
// Remove ASC/DESC from TH
3692
if(oOldSortedBy && oOldSortedBy.key && oOldSortedBy.dir) {
3693
oOldColumn = this._oColumnSet.getColumn(oOldSortedBy.key);
3694
nOldColumnKeyIndex = oOldColumn.getKeyIndex();
3696
// Remove previous UI from THEAD
3697
var elOldTh = oOldColumn.getThEl();
3698
Dom.removeClass(elOldTh, oOldSortedBy.dir);
3699
this.formatTheadCell(oOldColumn.getThLinerEl().firstChild, oOldColumn, oNewSortedBy);
3702
oNewColumn = (oNewSortedBy.column) ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key);
3703
nNewColumnKeyIndex = oNewColumn.getKeyIndex();
3705
// Update THEAD with new UI
3706
var elNewTh = oNewColumn.getThEl();
3707
// Backward compatibility
3708
if(oNewSortedBy.dir && ((oNewSortedBy.dir == "asc") || (oNewSortedBy.dir == "desc"))) {
3709
var newClass = (oNewSortedBy.dir == "desc") ?
3712
Dom.addClass(elNewTh, newClass);
3715
var sortClass = oNewSortedBy.dir || DT.CLASS_ASC;
3716
Dom.addClass(elNewTh, sortClass);
3718
this.formatTheadCell(oNewColumn.getThLinerEl().firstChild, oNewColumn, oNewSortedBy);
3724
this._elTbody.style.display = "none";
3725
var allRows = this._elTbody.rows,
3727
for(var i=allRows.length-1; i>-1; i--) {
3728
allCells = allRows[i].childNodes;
3729
if(allCells[nOldColumnKeyIndex]) {
3730
Dom.removeClass(allCells[nOldColumnKeyIndex], oOldSortedBy.dir);
3732
if(allCells[nNewColumnKeyIndex]) {
3733
Dom.addClass(allCells[nNewColumnKeyIndex], oNewSortedBy.dir);
3736
this._elTbody.style.display = "";
3739
this._clearTrTemplateEl();
3744
* @attribute paginator
3745
* @description An instance of YAHOO.widget.Paginator.
3747
* @type {Object|YAHOO.widget.Paginator}
3749
this.setAttributeConfig("paginator", {
3751
validator : function (val) {
3752
return val === null || val instanceof widget.Paginator;
3754
method : function () { this._updatePaginator.apply(this,arguments); }
3758
* @attribute caption
3759
* @description Value for the CAPTION element. NB: Not supported in
3760
* ScrollingDataTable.
3763
this.setAttributeConfig("caption", {
3765
validator: lang.isString,
3766
method: function(sCaption) {
3767
this._initCaptionEl(sCaption);
3772
* @attribute draggableColumns
3773
* @description True if Columns are draggable to reorder, false otherwise.
3774
* The Drag & Drop Utility is required to enable this feature. Only top-level
3775
* and non-nested Columns are draggable. Write once.
3779
this.setAttributeConfig("draggableColumns", {
3781
validator: lang.isBoolean,
3782
method: function(oParam) {
3785
this._initDraggableColumns();
3788
this._destroyDraggableColumns();
3795
* @attribute renderLoopSize
3796
* @description A value greater than 0 enables DOM rendering of rows to be
3797
* executed from a non-blocking timeout queue and sets how many rows to be
3798
* rendered per timeout. Recommended for very large data sets.
3802
this.setAttributeConfig("renderLoopSize", {
3804
validator: lang.isNumber
3808
* @attribute formatRow
3809
* @description A function that accepts a TR element and its associated Record
3810
* for custom formatting. The function must return TRUE in order to automatically
3811
* continue formatting of child TD elements, else TD elements will not be
3812
* automatically formatted.
3816
this.setAttributeConfig("formatRow", {
3818
validator: lang.isFunction
3822
* @attribute generateRequest
3823
* @description A function that converts an object literal of desired DataTable
3824
* states into a request value which is then passed to the DataSource's
3825
* sendRequest method in order to retrieve data for those states. This
3826
* function is passed an object literal of state data and a reference to the
3827
* DataTable instance:
3830
* <dt>pagination<dt>
3832
* <dt>offsetRecord</dt>
3833
* <dd>{Number} Index of the first Record of the desired page</dd>
3834
* <dt>rowsPerPage</dt>
3835
* <dd>{Number} Number of rows per page</dd>
3840
* <dd>{String} Key of sorted Column</dd>
3842
* <dd>{String} Sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
3845
* <dd>The DataTable instance</dd>
3848
* and by default returns a String of syntax:
3849
* "sort={sortColumn}&dir={sortDir}&startIndex={pageStartIndex}&results={rowsPerPage}"
3851
* @default HTMLFunction
3853
this.setAttributeConfig("generateRequest", {
3854
value: function(oState, oSelf) {
3856
oState = oState || {pagination:null, sortedBy:null};
3857
var sort = (oState.sortedBy) ? oState.sortedBy.key : oSelf.getColumnSet().keys[0].getKey();
3858
var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
3859
var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
3860
var results = (oState.pagination) ? oState.pagination.rowsPerPage : null;
3862
// Build the request
3863
return "sort=" + sort +
3865
"&startIndex=" + startIndex +
3866
((results !== null) ? "&results=" + results : "");
3868
validator: lang.isFunction
3872
* @attribute initialRequest
3873
* @description Defines the initial request that gets sent to the DataSource
3874
* during initialization. Value is ignored if initialLoad is set to any value
3879
this.setAttributeConfig("initialRequest", {
3884
* @attribute initialLoad
3885
* @description Determines whether or not to load data at instantiation. By
3886
* default, will trigger a sendRequest() to the DataSource and pass in the
3887
* request defined by initialRequest. If set to false, data will not load
3888
* at instantiation. Alternatively, implementers who wish to work with a
3889
* custom payload may pass in an object literal with the following values:
3892
* <dt>request (MIXED)</dt>
3893
* <dd>Request value.</dd>
3895
* <dt>argument (MIXED)</dt>
3896
* <dd>Custom data that will be passed through to the callback function.</dd>
3900
* @type Boolean | Object
3903
this.setAttributeConfig("initialLoad", {
3908
* @attribute dynamicData
3909
* @description If true, sorting and pagination are relegated to the DataSource
3910
* for handling, using the request returned by the "generateRequest" function.
3911
* Each new DataSource response blows away all previous Records. False by default, so
3912
* sorting and pagination will be handled directly on the client side, without
3913
* causing any new requests for data from the DataSource.
3917
this.setAttributeConfig("dynamicData", {
3919
validator: lang.isBoolean
3923
* @attribute MSG_EMPTY
3924
* @description Message to display if DataTable has no data.
3926
* @default "No records found."
3928
this.setAttributeConfig("MSG_EMPTY", {
3929
value: "No records found.",
3930
validator: lang.isString
3934
* @attribute MSG_LOADING
3935
* @description Message to display while DataTable is loading data.
3937
* @default "Loading..."
3939
this.setAttributeConfig("MSG_LOADING", {
3940
value: "Loading...",
3941
validator: lang.isString
3945
* @attribute MSG_ERROR
3946
* @description Message to display while DataTable has data error.
3948
* @default "Data error."
3950
this.setAttributeConfig("MSG_ERROR", {
3951
value: "Data error.",
3952
validator: lang.isString
3956
* @attribute MSG_SORTASC
3957
* @description Message to display in tooltip to sort Column in ascending order.
3959
* @default "Click to sort ascending"
3961
this.setAttributeConfig("MSG_SORTASC", {
3962
value: "Click to sort ascending",
3963
validator: lang.isString,
3964
method: function(sParam) {
3966
for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
3967
if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_ASC) {
3968
allKeys[i]._elThLabel.firstChild.title = sParam;
3976
* @attribute MSG_SORTDESC
3977
* @description Message to display in tooltip to sort Column in descending order.
3979
* @default "Click to sort descending"
3981
this.setAttributeConfig("MSG_SORTDESC", {
3982
value: "Click to sort descending",
3983
validator: lang.isString,
3984
method: function(sParam) {
3986
for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
3987
if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_DESC) {
3988
allKeys[i]._elThLabel.firstChild.title = sParam;
3996
* @attribute currencySymbol
3999
this.setAttributeConfig("currencySymbol", {
4001
validator: lang.isString
4005
* Default config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
4006
* @attribute currencyOptions
4008
* @default {prefix: $, decimalPlaces:2, decimalSeparator:".", thousandsSeparator:","}
4010
this.setAttributeConfig("currencyOptions", {
4012
prefix: this.get("currencySymbol"), // TODO: deprecate currencySymbol
4014
decimalSeparator:".",
4015
thousandsSeparator:","
4020
* Default config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
4021
* @attribute dateOptions
4023
* @default {format:"%m/%d/%Y", locale:"en"}
4025
this.setAttributeConfig("dateOptions", {
4026
value: {format:"%m/%d/%Y", locale:"en"}
4030
* Default config passed to YAHOO.util.Number.format() by the 'number' Column formatter.
4031
* @attribute numberOptions
4033
* @default {decimalPlaces:0, thousandsSeparator:","}
4035
this.setAttributeConfig("numberOptions", {
4038
thousandsSeparator:","
4044
/////////////////////////////////////////////////////////////////////////////
4046
// Private member variables
4048
/////////////////////////////////////////////////////////////////////////////
4051
* True if instance is initialized, so as to fire the initEvent after render.
4061
* Index assigned to instance.
4070
* Counter for IDs assigned to TR elements.
4072
* @property _nTrCount
4079
* Counter for IDs assigned to TD elements.
4081
* @property _nTdCount
4088
* Unique id assigned to instance "yui-dtN", useful prefix for generating unique
4089
* DOM ID strings and log messages.
4100
* @property _oChainRender
4101
* @type YAHOO.util.Chain
4104
_oChainRender : null,
4107
* DOM reference to the container element for the DataTable instance into which
4108
* all other elements get created.
4110
* @property _elContainer
4114
_elContainer : null,
4117
* DOM reference to the mask element for the DataTable instance which disables it.
4126
* DOM reference to the TABLE element for the DataTable instance.
4128
* @property _elTable
4135
* DOM reference to the CAPTION element for the DataTable instance.
4137
* @property _elCaption
4144
* DOM reference to the COLGROUP element for the DataTable instance.
4146
* @property _elColgroup
4153
* DOM reference to the THEAD element for the DataTable instance.
4155
* @property _elThead
4162
* DOM reference to the primary TBODY element for the DataTable instance.
4164
* @property _elTbody
4171
* DOM reference to the secondary TBODY element used to display DataTable messages.
4173
* @property _elMsgTbody
4180
* DOM reference to the secondary TBODY element's single TR element used to display DataTable messages.
4182
* @property _elMsgTr
4189
* DOM reference to the secondary TBODY element's single TD element used to display DataTable messages.
4191
* @property _elMsgTd
4198
* DataSource instance for the DataTable instance.
4200
* @property _oDataSource
4201
* @type YAHOO.util.DataSource
4204
_oDataSource : null,
4207
* ColumnSet instance for the DataTable instance.
4209
* @property _oColumnSet
4210
* @type YAHOO.widget.ColumnSet
4216
* RecordSet instance for the DataTable instance.
4218
* @property _oRecordSet
4219
* @type YAHOO.widget.RecordSet
4225
* The active CellEditor instance for the DataTable instance.
4227
* @property _oCellEditor
4228
* @type YAHOO.widget.CellEditor
4231
_oCellEditor : null,
4234
* ID string of first TR element of the current DataTable page.
4236
* @property _sFirstTrId
4243
* ID string of the last TR element of the current DataTable page.
4245
* @property _sLastTrId
4252
* Template row to create all new rows from.
4253
* @property _elTrTemplate
4254
* @type {HTMLElement}
4257
_elTrTemplate : null,
4260
* Sparse array of custom functions to set column widths for browsers that don't
4261
* support dynamic CSS rules. Functions are added at the index representing
4262
* the number of rows they update.
4264
* @property _aDynFunctions
4268
_aDynFunctions : [],
4298
/////////////////////////////////////////////////////////////////////////////
4302
/////////////////////////////////////////////////////////////////////////////
4305
* Clears browser text selection. Useful to call on rowSelectEvent or
4306
* cellSelectEvent to prevent clicks or dblclicks from selecting text in the
4309
* @method clearTextSelection
4311
clearTextSelection : function() {
4313
if(window.getSelection) {
4314
sel = window.getSelection();
4316
else if(document.getSelection) {
4317
sel = document.getSelection();
4319
else if(document.selection) {
4320
sel = document.selection;
4326
else if (sel.removeAllRanges) {
4327
sel.removeAllRanges();
4329
else if(sel.collapse) {
4336
* Sets focus on the given element.
4339
* @param el {HTMLElement} Element.
4342
_focusEl : function(el) {
4343
el = el || this._elTbody;
4344
// http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
4345
// The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
4346
// strange unexpected things as the user clicks on buttons and other controls.
4347
setTimeout(function() {
4357
* Forces Gecko repaint.
4359
* @method _repaintGecko
4360
* @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
4363
_repaintGecko : (ua.gecko) ?
4365
el = el || this._elContainer;
4366
var parent = el.parentNode;
4367
var nextSibling = el.nextSibling;
4368
parent.insertBefore(parent.removeChild(el), nextSibling);
4372
* Forces Opera repaint.
4374
* @method _repaintOpera
4377
_repaintOpera : (ua.opera) ?
4380
document.documentElement.className += " ";
4381
document.documentElement.className.trim();
4386
* Forces Webkit repaint.
4388
* @method _repaintWebkit
4389
* @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
4392
_repaintWebkit : (ua.webkit) ?
4394
el = el || this._elContainer;
4395
var parent = el.parentNode;
4396
var nextSibling = el.nextSibling;
4397
parent.insertBefore(parent.removeChild(el), nextSibling);
4424
* Initializes object literal of config values.
4426
* @method _initConfigs
4427
* @param oConfig {Object} Object literal of config values.
4430
_initConfigs : function(oConfigs) {
4431
if(!oConfigs || !lang.isObject(oConfigs)) {
4434
this.configs = oConfigs;
4438
* Initializes ColumnSet.
4440
* @method _initColumnSet
4441
* @param aColumnDefs {Object[]} Array of object literal Column definitions.
4444
_initColumnSet : function(aColumnDefs) {
4445
var oColumn, i, len;
4447
if(this._oColumnSet) {
4448
// First clear _oDynStyles for existing ColumnSet and
4449
// uregister CellEditor Custom Events
4450
for(i=0, len=this._oColumnSet.keys.length; i<len; i++) {
4451
oColumn = this._oColumnSet.keys[i];
4452
DT._oDynStyles["."+this.getId()+"-col-"+oColumn.getSanitizedKey()+" ."+DT.CLASS_LINER] = undefined;
4453
if(oColumn.editor && oColumn.editor.unsubscribeAll) { // Backward compatibility
4454
oColumn.editor.unsubscribeAll();
4458
this._oColumnSet = null;
4459
this._clearTrTemplateEl();
4462
if(lang.isArray(aColumnDefs)) {
4463
this._oColumnSet = new YAHOO.widget.ColumnSet(aColumnDefs);
4465
// Backward compatibility
4466
else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) {
4467
this._oColumnSet = aColumnDefs;
4468
YAHOO.log("DataTable's constructor now requires an array" +
4469
" of object literal Column definitions instead of a ColumnSet instance",
4470
"warn", this.toString());
4473
// Register CellEditor Custom Events
4474
var allKeys = this._oColumnSet.keys;
4475
for(i=0, len=allKeys.length; i<len; i++) {
4476
oColumn = allKeys[i];
4477
if(oColumn.editor && oColumn.editor.subscribe) { // Backward incompatibility
4478
oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
4479
oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
4480
oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
4481
oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
4482
oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
4483
oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
4484
oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
4485
oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
4491
* Initializes DataSource.
4493
* @method _initDataSource
4494
* @param oDataSource {YAHOO.util.DataSource} DataSource instance.
4497
_initDataSource : function(oDataSource) {
4498
this._oDataSource = null;
4499
if(oDataSource && (oDataSource instanceof DS)) {
4500
this._oDataSource = oDataSource;
4502
// Backward compatibility
4504
var tmpTable = null;
4505
var tmpContainer = this._elContainer;
4507
//TODO: this will break if re-initing DS at runtime for SDT
4508
// Peek in container child nodes to see if TABLE already exists
4509
if(tmpContainer.hasChildNodes()) {
4510
var tmpChildren = tmpContainer.childNodes;
4511
for(i=0; i<tmpChildren.length; i++) {
4512
if(tmpChildren[i].nodeName && tmpChildren[i].nodeName.toLowerCase() == "table") {
4513
tmpTable = tmpChildren[i];
4518
var tmpFieldsArray = [];
4519
for(; i<this._oColumnSet.keys.length; i++) {
4520
tmpFieldsArray.push({key:this._oColumnSet.keys[i].key});
4523
this._oDataSource = new DS(tmpTable);
4524
this._oDataSource.responseType = DS.TYPE_HTMLTABLE;
4525
this._oDataSource.responseSchema = {fields: tmpFieldsArray};
4526
YAHOO.log("Null DataSource for progressive enhancement from" +
4527
" markup has been deprecated", "warn", this.toString());
4534
* Initializes RecordSet.
4536
* @method _initRecordSet
4539
_initRecordSet : function() {
4540
if(this._oRecordSet) {
4541
this._oRecordSet.reset();
4544
this._oRecordSet = new YAHOO.widget.RecordSet();
4549
* Initializes DOM elements.
4551
* @method _initDomElements
4552
* @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
4553
* return {Boolean} False in case of error, otherwise true
4556
_initDomElements : function(elContainer) {
4558
this._initContainerEl(elContainer);
4560
this._initTableEl(this._elContainer);
4562
this._initColgroupEl(this._elTable);
4564
this._initTheadEl(this._elTable);
4567
this._initMsgTbodyEl(this._elTable);
4570
this._initTbodyEl(this._elTable);
4572
if(!this._elContainer || !this._elTable || !this._elColgroup || !this._elThead || !this._elTbody || !this._elMsgTbody) {
4573
YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
4582
* Destroy's the DataTable outer container element, if available.
4584
* @method _destroyContainerEl
4585
* @param elContainer {HTMLElement} Reference to the container element.
4588
_destroyContainerEl : function(elContainer) {
4589
Dom.removeClass(elContainer, DT.CLASS_DATATABLE);
4590
Ev.purgeElement(elContainer, true);
4591
elContainer.innerHTML = "";
4593
this._elContainer = null;
4594
this._elColgroup = null;
4595
this._elThead = null;
4596
this._elTbody = null;
4600
* Initializes the DataTable outer container element, including a mask.
4602
* @method _initContainerEl
4603
* @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
4606
_initContainerEl : function(elContainer) {
4607
// Validate container
4608
elContainer = Dom.get(elContainer);
4610
if(elContainer && elContainer.nodeName && (elContainer.nodeName.toLowerCase() == "div")) {
4612
this._destroyContainerEl(elContainer);
4614
Dom.addClass(elContainer, DT.CLASS_DATATABLE);
4615
Ev.addListener(elContainer, "focus", this._onTableFocus, this);
4616
Ev.addListener(elContainer, "dblclick", this._onTableDblclick, this);
4617
this._elContainer = elContainer;
4619
var elMask = document.createElement("div");
4620
elMask.className = DT.CLASS_MASK;
4621
elMask.style.display = "none";
4622
this._elMask = elContainer.appendChild(elMask);
4627
* Destroy's the DataTable TABLE element, if available.
4629
* @method _destroyTableEl
4632
_destroyTableEl : function() {
4633
var elTable = this._elTable;
4635
Ev.purgeElement(elTable, true);
4636
elTable.parentNode.removeChild(elTable);
4637
this._elCaption = null;
4638
this._elColgroup = null;
4639
this._elThead = null;
4640
this._elTbody = null;
4645
* Creates HTML markup CAPTION element.
4647
* @method _initCaptionEl
4648
* @param sCaption {String} Text for caption.
4651
_initCaptionEl : function(sCaption) {
4652
if(this._elTable && sCaption) {
4653
// Create CAPTION element
4654
if(!this._elCaption) {
4655
this._elCaption = this._elTable.createCaption();
4657
// Set CAPTION value
4658
this._elCaption.innerHTML = sCaption;
4660
else if(this._elCaption) {
4661
this._elCaption.parentNode.removeChild(this._elCaption);
4666
* Creates HTML markup for TABLE, COLGROUP, THEAD and TBODY elements in outer
4667
* container element.
4669
* @method _initTableEl
4670
* @param elContainer {HTMLElement} Container element into which to create TABLE.
4673
_initTableEl : function(elContainer) {
4676
this._destroyTableEl();
4679
this._elTable = elContainer.appendChild(document.createElement("table"));
4681
// Set SUMMARY attribute
4682
this._elTable.summary = this.get("summary");
4684
// Create CAPTION element
4685
if(this.get("caption")) {
4686
this._initCaptionEl(this.get("caption"));
4692
* Destroy's the DataTable COLGROUP element, if available.
4694
* @method _destroyColgroupEl
4697
_destroyColgroupEl : function() {
4698
var elColgroup = this._elColgroup;
4700
var elTable = elColgroup.parentNode;
4701
Ev.purgeElement(elColgroup, true);
4702
elTable.removeChild(elColgroup);
4703
this._elColgroup = null;
4708
* Initializes COLGROUP and COL elements for managing minWidth.
4710
* @method _initColgroupEl
4711
* @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
4714
_initColgroupEl : function(elTable) {
4717
this._destroyColgroupEl();
4719
// Add COLs to DOCUMENT FRAGMENT
4720
var allCols = this._aColIds || [],
4721
allKeys = this._oColumnSet.keys,
4722
i = 0, len = allCols.length,
4724
elFragment = document.createDocumentFragment(),
4725
elColTemplate = document.createElement("col");
4727
for(i=0,len=allKeys.length; i<len; i++) {
4728
oColumn = allKeys[i];
4729
elCol = elFragment.appendChild(elColTemplate.cloneNode(false));
4733
var elColgroup = elTable.insertBefore(document.createElement("colgroup"), elTable.firstChild);
4734
elColgroup.appendChild(elFragment);
4735
this._elColgroup = elColgroup;
4740
* Adds a COL element to COLGROUP at given index.
4742
* @method _insertColgroupColEl
4743
* @param index {Number} Index of new COL element.
4746
_insertColgroupColEl : function(index) {
4747
if(lang.isNumber(index)&& this._elColgroup) {
4748
var nextSibling = this._elColgroup.childNodes[index] || null;
4749
this._elColgroup.insertBefore(document.createElement("col"), nextSibling);
4754
* Removes a COL element to COLGROUP at given index.
4756
* @method _removeColgroupColEl
4757
* @param index {Number} Index of removed COL element.
4760
_removeColgroupColEl : function(index) {
4761
if(lang.isNumber(index) && this._elColgroup && this._elColgroup.childNodes[index]) {
4762
this._elColgroup.removeChild(this._elColgroup.childNodes[index]);
4767
* Reorders a COL element from old index(es) to new index.
4769
* @method _reorderColgroupColEl
4770
* @param aKeyIndexes {Number[]} Array of indexes of removed COL element.
4771
* @param newIndex {Number} New index.
4774
_reorderColgroupColEl : function(aKeyIndexes, newIndex) {
4775
if(lang.isArray(aKeyIndexes) && lang.isNumber(newIndex) && this._elColgroup && (this._elColgroup.childNodes.length > aKeyIndexes[aKeyIndexes.length-1])) {
4779
for(i=aKeyIndexes.length-1; i>-1; i--) {
4780
tmpCols.push(this._elColgroup.removeChild(this._elColgroup.childNodes[aKeyIndexes[i]]));
4783
var nextSibling = this._elColgroup.childNodes[newIndex] || null;
4784
for(i=tmpCols.length-1; i>-1; i--) {
4785
this._elColgroup.insertBefore(tmpCols[i], nextSibling);
4791
* Destroy's the DataTable THEAD element, if available.
4793
* @method _destroyTheadEl
4796
_destroyTheadEl : function() {
4797
var elThead = this._elThead;
4799
var elTable = elThead.parentNode;
4800
Ev.purgeElement(elThead, true);
4801
this._destroyColumnHelpers();
4802
elTable.removeChild(elThead);
4803
this._elThead = null;
4808
* Initializes THEAD element.
4810
* @method _initTheadEl
4811
* @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
4812
* @param {HTMLElement} Initialized THEAD element.
4815
_initTheadEl : function(elTable) {
4816
elTable = elTable || this._elTable;
4820
this._destroyTheadEl();
4822
//TODO: append to DOM later for performance
4823
var elThead = (this._elColgroup) ?
4824
elTable.insertBefore(document.createElement("thead"), this._elColgroup.nextSibling) :
4825
elTable.appendChild(document.createElement("thead"));
4827
// Set up DOM events for THEAD
4828
Ev.addListener(elThead, "focus", this._onTheadFocus, this);
4829
Ev.addListener(elThead, "keydown", this._onTheadKeydown, this);
4830
Ev.addListener(elThead, "mouseover", this._onTableMouseover, this);
4831
Ev.addListener(elThead, "mouseout", this._onTableMouseout, this);
4832
Ev.addListener(elThead, "mousedown", this._onTableMousedown, this);
4833
Ev.addListener(elThead, "mouseup", this._onTableMouseup, this);
4834
Ev.addListener(elThead, "click", this._onTheadClick, this);
4836
// Since we can't listen for click and dblclick on the same element...
4837
// Attach separately to THEAD and TBODY
4838
///Ev.addListener(elThead, "dblclick", this._onTableDblclick, this);
4840
var oColumnSet = this._oColumnSet,
4843
// Add TRs to the THEAD
4844
var colTree = oColumnSet.tree;
4846
for(i=0; i<colTree.length; i++) {
4847
var elTheadTr = elThead.appendChild(document.createElement("tr"));
4849
// ...and create TH cells
4850
for(j=0; j<colTree[i].length; j++) {
4851
oColumn = colTree[i][j];
4852
elTh = elTheadTr.appendChild(document.createElement("th"));
4853
this._initThEl(elTh,oColumn);
4856
// Set FIRST/LAST on THEAD rows
4858
Dom.addClass(elTheadTr, DT.CLASS_FIRST);
4860
if(i === (colTree.length-1)) {
4861
Dom.addClass(elTheadTr, DT.CLASS_LAST);
4866
// Set FIRST/LAST on edge TH elements using the values in ColumnSet headers array
4867
var aFirstHeaders = oColumnSet.headers[0] || [];
4868
for(i=0; i<aFirstHeaders.length; i++) {
4869
Dom.addClass(Dom.get(this.getId() +"-th-"+aFirstHeaders[i]), DT.CLASS_FIRST);
4871
var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1] || [];
4872
for(i=0; i<aLastHeaders.length; i++) {
4873
Dom.addClass(Dom.get(this.getId() +"-th-"+aLastHeaders[i]), DT.CLASS_LAST);
4876
YAHOO.log("TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
4878
///TODO: try _repaintGecko(this._elContainer) instead
4880
if(ua.webkit && ua.webkit < 420) {
4882
setTimeout(function() {
4883
elThead.style.display = "";
4885
elThead.style.display = 'none';
4888
this._elThead = elThead;
4890
// Column helpers needs _elThead to exist
4891
this._initColumnHelpers();
4896
* Populates TH element as defined by Column.
4899
* @param elTh {HTMLElement} TH element reference.
4900
* @param oColumn {YAHOO.widget.Column} Column object.
4903
_initThEl : function(elTh, oColumn) {
4904
elTh.id = this.getId() + "-th-" + oColumn.getSanitizedKey(); // Needed for accessibility, getColumn by TH, and ColumnDD
4905
elTh.innerHTML = "";
4906
elTh.rowSpan = oColumn.getRowspan();
4907
elTh.colSpan = oColumn.getColspan();
4908
oColumn._elTh = elTh;
4910
var elThLiner = elTh.appendChild(document.createElement("div"));
4911
elThLiner.id = elTh.id + "-liner"; // Needed for resizer
4912
elThLiner.className = DT.CLASS_LINER;
4913
oColumn._elThLiner = elThLiner;
4915
var elThLabel = elThLiner.appendChild(document.createElement("span"));
4916
elThLabel.className = DT.CLASS_LABEL;
4918
// Assign abbr attribute
4920
elTh.abbr = oColumn.abbr;
4922
// Clear minWidth on hidden Columns
4923
if(oColumn.hidden) {
4924
this._clearMinWidth(oColumn);
4927
elTh.className = this._getColumnClassNames(oColumn);
4929
// Set Column width...
4931
// Validate minWidth
4932
var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
4933
oColumn.minWidth : oColumn.width;
4934
// ...for fallback cases
4935
if(DT._bDynStylesFallback) {
4936
elTh.firstChild.style.overflow = 'hidden';
4937
elTh.firstChild.style.width = nWidth + 'px';
4939
// ...for non fallback cases
4941
this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden');
4945
this.formatTheadCell(elThLabel, oColumn, this.get("sortedBy"));
4946
oColumn._elThLabel = elThLabel;
4950
* Outputs markup into the given TH based on given Column.
4952
* @method DataTable.formatTheadCell
4953
* @param elCellLabel {HTMLElement} The label SPAN element within the TH liner,
4954
* not the liner DIV element.
4955
* @param oColumn {YAHOO.widget.Column} Column instance.
4956
* @param oSortedBy {Object} Sort state object literal.
4958
formatTheadCell : function(elCellLabel, oColumn, oSortedBy) {
4959
var sKey = oColumn.getKey();
4960
var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
4962
// Add accessibility link for sortable Columns
4963
if(oColumn.sortable) {
4964
// Calculate the direction
4965
var sSortClass = this.getColumnSortDir(oColumn, oSortedBy);
4966
var bDesc = (sSortClass === DT.CLASS_DESC);
4968
// This is the sorted Column
4969
if(oSortedBy && (oColumn.key === oSortedBy.key)) {
4970
bDesc = !(oSortedBy.dir === DT.CLASS_DESC);
4973
// Generate a unique HREF for visited status
4974
var sHref = this.getId() + "-href-" + oColumn.getSanitizedKey();
4976
// Generate a dynamic TITLE for sort status
4977
var sTitle = (bDesc) ? this.get("MSG_SORTDESC") : this.get("MSG_SORTASC");
4979
// Format the element
4980
elCellLabel.innerHTML = "<a href=\"" + sHref + "\" title=\"" + sTitle + "\" class=\"" + DT.CLASS_SORTABLE + "\">" + sLabel + "</a>";
4982
// Just display the label for non-sortable Columns
4984
elCellLabel.innerHTML = sLabel;
4989
* Disables DD from top-level Column TH elements.
4991
* @method _destroyDraggableColumns
4994
_destroyDraggableColumns : function() {
4996
for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
4997
oColumn = this._oColumnSet.tree[0][i];
4999
oColumn._dd = oColumn._dd.unreg();
5000
Dom.removeClass(oColumn.getThEl(), DT.CLASS_DRAGGABLE);
5006
* Initializes top-level Column TH elements into DD instances.
5008
* @method _initDraggableColumns
5011
_initDraggableColumns : function() {
5012
this._destroyDraggableColumns();
5014
var oColumn, elTh, elDragTarget;
5015
for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
5016
oColumn = this._oColumnSet.tree[0][i];
5017
elTh = oColumn.getThEl();
5018
Dom.addClass(elTh, DT.CLASS_DRAGGABLE);
5019
elDragTarget = DT._initColumnDragTargetEl();
5020
oColumn._dd = new YAHOO.widget.ColumnDD(this, oColumn, elTh, elDragTarget);
5024
YAHOO.log("Could not find DragDrop for draggable Columns", "warn", this.toString());
5029
* Disables resizeability on key Column TH elements.
5031
* @method _destroyResizeableColumns
5034
_destroyResizeableColumns : function() {
5035
var aKeys = this._oColumnSet.keys;
5036
for(var i=0, len=aKeys.length; i<len; i++) {
5037
if(aKeys[i]._ddResizer) {
5038
aKeys[i]._ddResizer = aKeys[i]._ddResizer.unreg();
5039
Dom.removeClass(aKeys[i].getThEl(), DT.CLASS_RESIZEABLE);
5045
* Initializes resizeability on key Column TH elements.
5047
* @method _initResizeableColumns
5050
_initResizeableColumns : function() {
5051
this._destroyResizeableColumns();
5053
var oColumn, elTh, elThLiner, elThResizerLiner, elThResizer, elResizerProxy, cancelClick;
5054
for(var i=0, len=this._oColumnSet.keys.length; i<len; i++) {
5055
oColumn = this._oColumnSet.keys[i];
5056
if(oColumn.resizeable) {
5057
elTh = oColumn.getThEl();
5058
Dom.addClass(elTh, DT.CLASS_RESIZEABLE);
5059
elThLiner = oColumn.getThLinerEl();
5061
// Bug 1915349: So resizer is as tall as TH when rowspan > 1
5062
// Create a separate resizer liner with position:relative
5063
elThResizerLiner = elTh.appendChild(document.createElement("div"));
5064
elThResizerLiner.className = DT.CLASS_RESIZERLINER;
5066
// Move TH contents into the new resizer liner
5067
elThResizerLiner.appendChild(elThLiner);
5069
// Create the resizer
5070
elThResizer = elThResizerLiner.appendChild(document.createElement("div"));
5071
elThResizer.id = elTh.id + "-resizer"; // Needed for ColumnResizer
5072
elThResizer.className = DT.CLASS_RESIZER;
5073
oColumn._elResizer = elThResizer;
5075
// Create the resizer proxy, once globally
5076
elResizerProxy = DT._initColumnResizerProxyEl();
5077
oColumn._ddResizer = new YAHOO.util.ColumnResizer(
5078
this, oColumn, elTh, elThResizer, elResizerProxy);
5079
cancelClick = function(e) {
5080
Ev.stopPropagation(e);
5082
Ev.addListener(elThResizer,"click",cancelClick);
5087
YAHOO.log("Could not find DragDrop for resizeable Columns", "warn", this.toString());
5092
* Destroys elements associated with Column functionality: ColumnDD and ColumnResizers.
5094
* @method _destroyColumnHelpers
5097
_destroyColumnHelpers : function() {
5098
this._destroyDraggableColumns();
5099
this._destroyResizeableColumns();
5103
* Initializes elements associated with Column functionality: ColumnDD and ColumnResizers.
5105
* @method _initColumnHelpers
5108
_initColumnHelpers : function() {
5109
if(this.get("draggableColumns")) {
5110
this._initDraggableColumns();
5112
this._initResizeableColumns();
5116
* Destroy's the DataTable TBODY element, if available.
5118
* @method _destroyTbodyEl
5121
_destroyTbodyEl : function() {
5122
var elTbody = this._elTbody;
5124
var elTable = elTbody.parentNode;
5125
Ev.purgeElement(elTbody, true);
5126
elTable.removeChild(elTbody);
5127
this._elTbody = null;
5132
* Initializes TBODY element for data.
5134
* @method _initTbodyEl
5135
* @param elTable {HTMLElement} TABLE element into which to create TBODY .
5138
_initTbodyEl : function(elTable) {
5141
this._destroyTbodyEl();
5144
var elTbody = elTable.appendChild(document.createElement("tbody"));
5145
elTbody.tabIndex = 0;
5146
elTbody.className = DT.CLASS_DATA;
5148
// Set up DOM events for TBODY
5149
Ev.addListener(elTbody, "focus", this._onTbodyFocus, this);
5150
Ev.addListener(elTbody, "mouseover", this._onTableMouseover, this);
5151
Ev.addListener(elTbody, "mouseout", this._onTableMouseout, this);
5152
Ev.addListener(elTbody, "mousedown", this._onTableMousedown, this);
5153
Ev.addListener(elTbody, "mouseup", this._onTableMouseup, this);
5154
Ev.addListener(elTbody, "keydown", this._onTbodyKeydown, this);
5155
Ev.addListener(elTbody, "keypress", this._onTableKeypress, this);
5156
Ev.addListener(elTbody, "click", this._onTbodyClick, this);
5158
// Since we can't listen for click and dblclick on the same element...
5159
// Attach separately to THEAD and TBODY
5160
///Ev.addListener(elTbody, "dblclick", this._onTableDblclick, this);
5163
// IE puts focus outline in the wrong place
5165
elTbody.hideFocus=true;
5168
this._elTbody = elTbody;
5173
* Destroy's the DataTable message TBODY element, if available.
5175
* @method _destroyMsgTbodyEl
5178
_destroyMsgTbodyEl : function() {
5179
var elMsgTbody = this._elMsgTbody;
5181
var elTable = elMsgTbody.parentNode;
5182
Ev.purgeElement(elMsgTbody, true);
5183
elTable.removeChild(elMsgTbody);
5184
this._elTbody = null;
5189
* Initializes TBODY element for messaging.
5191
* @method _initMsgTbodyEl
5192
* @param elTable {HTMLElement} TABLE element into which to create TBODY
5195
_initMsgTbodyEl : function(elTable) {
5197
var elMsgTbody = document.createElement("tbody");
5198
elMsgTbody.className = DT.CLASS_MESSAGE;
5199
var elMsgTr = elMsgTbody.appendChild(document.createElement("tr"));
5200
elMsgTr.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
5201
this._elMsgTr = elMsgTr;
5202
var elMsgTd = elMsgTr.appendChild(document.createElement("td"));
5203
elMsgTd.colSpan = this._oColumnSet.keys.length || 1;
5204
elMsgTd.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
5205
this._elMsgTd = elMsgTd;
5206
elMsgTbody = elTable.insertBefore(elMsgTbody, this._elTbody);
5207
var elMsgLiner = elMsgTd.appendChild(document.createElement("div"));
5208
elMsgLiner.className = DT.CLASS_LINER;
5209
this._elMsgTbody = elMsgTbody;
5214
* Initialize internal event listeners
5216
* @method _initEvents
5219
_initEvents : function () {
5220
// Initialize Column sort
5221
this._initColumnSort();
5223
// Add the document level click listener
5224
YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this);
5226
// Paginator integration
5227
this.subscribe("paginatorChange",function () {
5228
this._handlePaginatorChange.apply(this,arguments);
5231
this.subscribe("initEvent",function () {
5232
this.renderPaginator();
5235
// Initialize CellEditor integration
5236
this._initCellEditing();
5240
* Initializes Column sorting.
5242
* @method _initColumnSort
5245
_initColumnSort : function() {
5246
this.subscribe("theadCellClickEvent", this.onEventSortColumn);
5248
// Backward compatibility
5249
var oSortedBy = this.get("sortedBy");
5251
if(oSortedBy.dir == "desc") {
5252
this._configs.sortedBy.value.dir = DT.CLASS_DESC;
5254
else if(oSortedBy.dir == "asc") {
5255
this._configs.sortedBy.value.dir = DT.CLASS_ASC;
5261
* Initializes CellEditor integration.
5263
* @method _initCellEditing
5266
_initCellEditing : function() {
5267
this.subscribe("editorBlurEvent",function () {
5268
this.onEditorBlurEvent.apply(this,arguments);
5270
this.subscribe("editorBlockEvent",function () {
5271
this.onEditorBlockEvent.apply(this,arguments);
5273
this.subscribe("editorUnblockEvent",function () {
5274
this.onEditorUnblockEvent.apply(this,arguments);
5310
// DOM MUTATION FUNCTIONS
5313
* Retruns classnames to represent current Column states.
5314
* @method _getColumnClassnames
5315
* @param oColumn {YAHOO.widget.Column} Column instance.
5316
* @param aAddClasses {String[]} An array of additional classnames to add to the
5318
* @return {String} A String of classnames to be assigned to TH or TD elements
5322
_getColumnClassNames : function (oColumn, aAddClasses) {
5326
if(lang.isString(oColumn.className)) {
5327
// Single custom class
5328
allClasses = [oColumn.className];
5330
else if(lang.isArray(oColumn.className)) {
5331
// Array of custom classes
5332
allClasses = oColumn.className;
5335
// no custom classes
5339
// Hook for setting width with via dynamic style uses key since ID is too disposable
5340
allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey();
5342
// Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
5343
allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
5345
var isSortedBy = this.get("sortedBy") || {};
5347
if(oColumn.key === isSortedBy.key) {
5348
allClasses[allClasses.length] = isSortedBy.dir || '';
5351
if(oColumn.hidden) {
5352
allClasses[allClasses.length] = DT.CLASS_HIDDEN;
5355
if(oColumn.selected) {
5356
allClasses[allClasses.length] = DT.CLASS_SELECTED;
5359
if(oColumn.sortable) {
5360
allClasses[allClasses.length] = DT.CLASS_SORTABLE;
5363
if(oColumn.resizeable) {
5364
allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
5367
if(oColumn.editor) {
5368
allClasses[allClasses.length] = DT.CLASS_EDITABLE;
5371
// Addtnl classes, including First/Last
5373
allClasses = allClasses.concat(aAddClasses);
5376
return allClasses.join(' ');
5380
* Clears TR element template in response to any Column state change.
5381
* @method _clearTrTemplateEl
5384
_clearTrTemplateEl : function () {
5385
this._elTrTemplate = null;
5389
* Returns a new TR element template with TD elements classed with current
5391
* @method _getTrTemplateEl
5392
* @return {HTMLElement} A TR element to be cloned and added to the DOM.
5395
_getTrTemplateEl : function (oRecord, index) {
5396
// Template is already available
5397
if(this._elTrTemplate) {
5398
return this._elTrTemplate;
5400
// Template needs to be created
5403
tr = d.createElement('tr'),
5404
td = d.createElement('td'),
5405
div = d.createElement('div');
5407
// Append the liner element
5408
td.appendChild(div);
5410
// Create TD elements into DOCUMENT FRAGMENT
5411
var df = document.createDocumentFragment(),
5412
allKeys = this._oColumnSet.keys,
5415
// Set state for each TD;
5417
for(var i=0, keysLen=allKeys.length; i<keysLen; i++) {
5418
// Clone the TD template
5419
elTd = td.cloneNode(true);
5421
// Format the base TD
5422
elTd = this._formatTdEl(allKeys[i], elTd, i, (i===keysLen-1));
5424
df.appendChild(elTd);
5427
this._elTrTemplate = tr;
5433
* Formats a basic TD element.
5434
* @method _formatTdEl
5435
* @param oColumn {YAHOO.widget.Column} Associated Column instance.
5436
* @param elTd {HTMLElement} An unformatted TD element.
5437
* @param index {Number} Column key index.
5438
* @param isLast {Boolean} True if Column is last key of the ColumnSet.
5439
* @return {HTMLElement} A formatted TD element.
5442
_formatTdEl : function (oColumn, elTd, index, isLast) {
5443
var oColumnSet = this._oColumnSet;
5445
// Set the TD's accessibility headers
5446
var allHeaders = oColumnSet.headers,
5447
allColHeaders = allHeaders[index],
5450
for(var j=0, headersLen=allColHeaders.length; j < headersLen; j++) {
5451
sHeader = this._sId + "-th-" + allColHeaders[j] + ' ';
5452
sTdHeaders += sHeader;
5454
elTd.headers = sTdHeaders;
5456
// Class the TD element
5457
var aAddClasses = [];
5459
aAddClasses[aAddClasses.length] = DT.CLASS_FIRST;
5462
aAddClasses[aAddClasses.length] = DT.CLASS_LAST;
5464
elTd.className = this._getColumnClassNames(oColumn, aAddClasses);
5466
// Class the liner element
5467
elTd.firstChild.className = DT.CLASS_LINER;
5469
// Set Column width for fallback cases
5470
if(oColumn.width && DT._bDynStylesFallback) {
5471
// Validate minWidth
5472
var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
5473
oColumn.minWidth : oColumn.width;
5474
elTd.firstChild.style.overflow = 'hidden';
5475
elTd.firstChild.style.width = nWidth + 'px';
5483
* Create a new TR element for a given Record and appends it with the correct
5484
* number of Column-state-classed TD elements. Striping is the responsibility of
5485
* the calling function, which may decide to stripe the single row, a subset of
5486
* rows, or all the rows.
5487
* @method _createTrEl
5488
* @param oRecord {YAHOO.widget.Record} Record instance
5489
* @return {HTMLElement} The new TR element. This must be added to the DOM.
5492
_addTrEl : function (oRecord) {
5493
var elTrTemplate = this._getTrTemplateEl();
5495
// Clone the TR template.
5496
var elTr = elTrTemplate.cloneNode(true);
5499
return this._updateTrEl(elTr,oRecord);
5503
* Formats the contents of the given TR's TD elements with data from the given
5504
* Record. Only innerHTML should change, nothing structural.
5506
* @method _updateTrEl
5507
* @param elTr {HTMLElement} The TR element to update.
5508
* @param oRecord {YAHOO.widget.Record} The associated Record instance.
5509
* @return {HTMLElement} DOM reference to the new TR element.
5512
_updateTrEl : function(elTr, oRecord) {
5513
var ok = this.get("formatRow") ? this.get("formatRow").call(this, elTr, oRecord) : true;
5515
// Hide the row to prevent constant reflows
5516
elTr.style.display = 'none';
5518
// Update TD elements with new data
5519
var allTds = elTr.childNodes,
5521
for(var i=0,len=allTds.length; i<len; ++i) {
5524
// Set the cell content
5525
this.formatCell(allTds[i].firstChild, oRecord, this._oColumnSet.keys[i]);
5528
// Redisplay the row for reflow
5529
elTr.style.display = '';
5532
elTr.id = oRecord.getId(); // Needed for Record association and tracking of FIRST/LAST
5538
* Deletes TR element by DOM reference or by DataTable page row index.
5540
* @method _deleteTrEl
5541
* @param row {HTMLElement | Number} TR element reference or Datatable page row index.
5542
* @return {Boolean} Returns true if successful, else returns false.
5545
_deleteTrEl : function(row) {
5548
// Get page row index for the element
5549
if(!lang.isNumber(row)) {
5550
rowIndex = Dom.get(row).sectionRowIndex;
5555
if(lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
5556
// Cannot use tbody.deleteRow due to IE6 instability
5557
//return this._elTbody.deleteRow(rowIndex);
5558
return this._elTbody.removeChild(this.getTrEl(row));
5591
// CSS/STATE FUNCTIONS
5597
* Removes the class YAHOO.widget.DataTable.CLASS_FIRST from the first TR element
5598
* of the DataTable page and updates internal tracker.
5600
* @method _unsetFirstRow
5603
_unsetFirstRow : function() {
5605
if(this._sFirstTrId) {
5606
Dom.removeClass(this._sFirstTrId, DT.CLASS_FIRST);
5607
this._sFirstTrId = null;
5612
* Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element
5613
* of the DataTable page and updates internal tracker.
5615
* @method _setFirstRow
5618
_setFirstRow : function() {
5619
this._unsetFirstRow();
5620
var elTr = this.getFirstTrEl();
5623
Dom.addClass(elTr, DT.CLASS_FIRST);
5624
this._sFirstTrId = elTr.id;
5629
* Removes the class YAHOO.widget.DataTable.CLASS_LAST from the last TR element
5630
* of the DataTable page and updates internal tracker.
5632
* @method _unsetLastRow
5635
_unsetLastRow : function() {
5636
// Unassign previous class
5637
if(this._sLastTrId) {
5638
Dom.removeClass(this._sLastTrId, DT.CLASS_LAST);
5639
this._sLastTrId = null;
5644
* Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element
5645
* of the DataTable page and updates internal tracker.
5647
* @method _setLastRow
5650
_setLastRow : function() {
5651
this._unsetLastRow();
5652
var elTr = this.getLastTrEl();
5655
Dom.addClass(elTr, DT.CLASS_LAST);
5656
this._sLastTrId = elTr.id;
5661
* Assigns the classes DT.CLASS_EVEN and DT.CLASS_ODD to one, many, or all TR elements.
5663
* @method _setRowStripes
5664
* @param row {HTMLElement | String | Number} (optional) HTML TR element reference
5665
* or string ID, or page row index of where to start striping.
5666
* @param range {Number} (optional) If given, how many rows to stripe, otherwise
5667
* stripe all the rows until the end.
5670
_setRowStripes : function(row, range) {
5671
// Default values stripe all rows
5672
var allRows = this._elTbody.rows,
5674
nEndIndex = allRows.length,
5675
aOdds = [], nOddIdx = 0,
5676
aEvens = [], nEvenIdx = 0;
5679
if((row !== null) && (row !== undefined)) {
5680
// Validate given start row
5681
var elStartRow = this.getTrEl(row);
5683
nStartIndex = elStartRow.sectionRowIndex;
5685
// Validate given range
5686
if(lang.isNumber(range) && (range > 1)) {
5687
nEndIndex = nStartIndex + range;
5692
for(var i=nStartIndex; i<nEndIndex; i++) {
5694
aOdds[nOddIdx++] = allRows[i];
5696
aEvens[nEvenIdx++] = allRows[i];
5701
Dom.replaceClass(aOdds, DT.CLASS_EVEN, DT.CLASS_ODD);
5704
if (aEvens.length) {
5705
Dom.replaceClass(aEvens, DT.CLASS_ODD, DT.CLASS_EVEN);
5710
* Assigns the class DT.CLASS_SELECTED to TR and TD elements.
5712
* @method _setSelections
5715
_setSelections : function() {
5716
// Keep track of selected rows
5717
var allSelectedRows = this.getSelectedRows();
5718
// Keep track of selected cells
5719
var allSelectedCells = this.getSelectedCells();
5720
// Anything to select?
5721
if((allSelectedRows.length>0) || (allSelectedCells.length > 0)) {
5722
var oColumnSet = this._oColumnSet,
5724
// Loop over each row
5725
for(var i=0; i<allSelectedRows.length; i++) {
5726
el = Dom.get(allSelectedRows[i]);
5728
Dom.addClass(el, DT.CLASS_SELECTED);
5731
// Loop over each cell
5732
for(i=0; i<allSelectedCells.length; i++) {
5733
el = Dom.get(allSelectedCells[i].recordId);
5735
Dom.addClass(el.childNodes[oColumnSet.getColumn(allSelectedCells[i].columnKey).getKeyIndex()], DT.CLASS_SELECTED);
5783
/////////////////////////////////////////////////////////////////////////////
5785
// Private DOM Event Handlers
5787
/////////////////////////////////////////////////////////////////////////////
5790
* Validates minWidths whenever the render chain ends.
5792
* @method _onRenderChainEnd
5795
_onRenderChainEnd : function() {
5796
// Hide loading message
5797
this.hideTableMessage();
5799
// Show empty message
5800
if(this._elTbody.rows.length === 0) {
5801
this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
5804
// Execute in timeout thread to give implementers a chance
5805
// to subscribe after the constructor
5807
setTimeout(function() {
5808
if((oSelf instanceof DT) && oSelf._sId) {
5811
oSelf._bInit = false;
5812
oSelf.fireEvent("initEvent");
5816
oSelf.fireEvent("renderEvent");
5817
// Backward compatibility
5818
oSelf.fireEvent("refreshEvent");
5819
YAHOO.log("DataTable rendered", "info", oSelf.toString());
5821
// Post-render routine
5822
oSelf.validateColumnWidths();
5824
// Post-render event
5825
oSelf.fireEvent("postRenderEvent");
5827
/*if(YAHOO.example.Performance.trialStart) {
5828
YAHOO.log((new Date()).getTime() - YAHOO.example.Performance.trialStart.getTime() + " ms", "time");
5829
YAHOO.example.Performance.trialStart = null;
5832
YAHOO.log("Post-render routine executed", "info", oSelf.toString());
5838
* Handles click events on the DOCUMENT.
5840
* @method _onDocumentClick
5841
* @param e {HTMLEvent} The click event.
5842
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5845
_onDocumentClick : function(e, oSelf) {
5846
var elTarget = Ev.getTarget(e);
5847
var elTag = elTarget.nodeName.toLowerCase();
5849
if(!Dom.isAncestor(oSelf._elContainer, elTarget)) {
5850
oSelf.fireEvent("tableBlurEvent");
5852
// Fires editorBlurEvent when click is not within the TABLE.
5853
// For cases when click is within the TABLE, due to timing issues,
5854
// the editorBlurEvent needs to get fired by the lower-level DOM click
5855
// handlers below rather than by the TABLE click handler directly.
5856
if(oSelf._oCellEditor) {
5857
if(oSelf._oCellEditor.getContainerEl) {
5858
var elContainer = oSelf._oCellEditor.getContainerEl();
5859
// Only if the click was not within the CellEditor container
5860
if(!Dom.isAncestor(elContainer, elTarget) &&
5861
(elContainer.id !== elTarget.id)) {
5862
oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
5865
// Backward Compatibility
5866
else if(oSelf._oCellEditor.isActive) {
5867
// Only if the click was not within the Cell Editor container
5868
if(!Dom.isAncestor(oSelf._oCellEditor.container, elTarget) &&
5869
(oSelf._oCellEditor.container.id !== elTarget.id)) {
5870
oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
5878
* Handles focus events on the DataTable instance.
5880
* @method _onTableFocus
5881
* @param e {HTMLEvent} The focus event.
5882
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5885
_onTableFocus : function(e, oSelf) {
5886
oSelf.fireEvent("tableFocusEvent");
5890
* Handles focus events on the THEAD element.
5892
* @method _onTheadFocus
5893
* @param e {HTMLEvent} The focus event.
5894
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5897
_onTheadFocus : function(e, oSelf) {
5898
oSelf.fireEvent("theadFocusEvent");
5899
oSelf.fireEvent("tableFocusEvent");
5903
* Handles focus events on the TBODY element.
5905
* @method _onTbodyFocus
5906
* @param e {HTMLEvent} The focus event.
5907
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5910
_onTbodyFocus : function(e, oSelf) {
5911
oSelf.fireEvent("tbodyFocusEvent");
5912
oSelf.fireEvent("tableFocusEvent");
5916
* Handles mouseover events on the DataTable instance.
5918
* @method _onTableMouseover
5919
* @param e {HTMLEvent} The mouseover event.
5920
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5923
_onTableMouseover : function(e, oSelf) {
5924
var elTarget = Ev.getTarget(e);
5925
var elTag = elTarget.nodeName.toLowerCase();
5926
var bKeepBubbling = true;
5927
while(elTarget && (elTag != "table")) {
5934
bKeepBubbling = oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
5937
if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
5938
bKeepBubbling = oSelf.fireEvent("theadLabelMouseoverEvent",{target:elTarget,event:e});
5939
// Backward compatibility
5940
bKeepBubbling = oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
5944
bKeepBubbling = oSelf.fireEvent("theadCellMouseoverEvent",{target:elTarget,event:e});
5945
// Backward compatibility
5946
bKeepBubbling = oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
5949
if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
5950
bKeepBubbling = oSelf.fireEvent("theadRowMouseoverEvent",{target:elTarget,event:e});
5951
// Backward compatibility
5952
bKeepBubbling = oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
5955
bKeepBubbling = oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
5961
if(bKeepBubbling === false) {
5965
elTarget = elTarget.parentNode;
5967
elTag = elTarget.nodeName.toLowerCase();
5971
oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elContainer),event:e});
5975
* Handles mouseout events on the DataTable instance.
5977
* @method _onTableMouseout
5978
* @param e {HTMLEvent} The mouseout event.
5979
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
5982
_onTableMouseout : function(e, oSelf) {
5983
var elTarget = Ev.getTarget(e);
5984
var elTag = elTarget.nodeName.toLowerCase();
5985
var bKeepBubbling = true;
5986
while(elTarget && (elTag != "table")) {
5993
bKeepBubbling = oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
5996
if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
5997
bKeepBubbling = oSelf.fireEvent("theadLabelMouseoutEvent",{target:elTarget,event:e});
5998
// Backward compatibility
5999
bKeepBubbling = oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
6003
bKeepBubbling = oSelf.fireEvent("theadCellMouseoutEvent",{target:elTarget,event:e});
6004
// Backward compatibility
6005
bKeepBubbling = oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
6008
if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6009
bKeepBubbling = oSelf.fireEvent("theadRowMouseoutEvent",{target:elTarget,event:e});
6010
// Backward compatibility
6011
bKeepBubbling = oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
6014
bKeepBubbling = oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
6020
if(bKeepBubbling === false) {
6024
elTarget = elTarget.parentNode;
6026
elTag = elTarget.nodeName.toLowerCase();
6030
oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elContainer),event:e});
6034
* Handles mousedown events on the DataTable instance.
6036
* @method _onTableMousedown
6037
* @param e {HTMLEvent} The mousedown event.
6038
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6041
_onTableMousedown : function(e, oSelf) {
6042
var elTarget = Ev.getTarget(e);
6043
var elTag = elTarget.nodeName.toLowerCase();
6044
var bKeepBubbling = true;
6045
while(elTarget && (elTag != "table")) {
6052
bKeepBubbling = oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
6055
if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6056
bKeepBubbling = oSelf.fireEvent("theadLabelMousedownEvent",{target:elTarget,event:e});
6057
// Backward compatibility
6058
bKeepBubbling = oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
6062
bKeepBubbling = oSelf.fireEvent("theadCellMousedownEvent",{target:elTarget,event:e});
6063
// Backward compatibility
6064
bKeepBubbling = oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
6067
if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6068
bKeepBubbling = oSelf.fireEvent("theadRowMousedownEvent",{target:elTarget,event:e});
6069
// Backward compatibility
6070
bKeepBubbling = oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
6073
bKeepBubbling = oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
6079
if(bKeepBubbling === false) {
6083
elTarget = elTarget.parentNode;
6085
elTag = elTarget.nodeName.toLowerCase();
6089
oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elContainer),event:e});
6093
* Handles mouseup events on the DataTable instance.
6095
* @method _onTableMouseup
6096
* @param e {HTMLEvent} The mouseup event.
6097
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6100
_onTableMouseup : function(e, oSelf) {
6101
var elTarget = Ev.getTarget(e);
6102
var elTag = elTarget.nodeName.toLowerCase();
6103
var bKeepBubbling = true;
6104
while(elTarget && (elTag != "table")) {
6111
bKeepBubbling = oSelf.fireEvent("cellMouseupEvent",{target:elTarget,event:e});
6114
if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6115
bKeepBubbling = oSelf.fireEvent("theadLabelMouseupEvent",{target:elTarget,event:e});
6116
// Backward compatibility
6117
bKeepBubbling = oSelf.fireEvent("headerLabelMouseupEvent",{target:elTarget,event:e});
6121
bKeepBubbling = oSelf.fireEvent("theadCellMouseupEvent",{target:elTarget,event:e});
6122
// Backward compatibility
6123
bKeepBubbling = oSelf.fireEvent("headerCellMouseupEvent",{target:elTarget,event:e});
6126
if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6127
bKeepBubbling = oSelf.fireEvent("theadRowMouseupEvent",{target:elTarget,event:e});
6128
// Backward compatibility
6129
bKeepBubbling = oSelf.fireEvent("headerRowMouseupEvent",{target:elTarget,event:e});
6132
bKeepBubbling = oSelf.fireEvent("rowMouseupEvent",{target:elTarget,event:e});
6138
if(bKeepBubbling === false) {
6142
elTarget = elTarget.parentNode;
6144
elTag = elTarget.nodeName.toLowerCase();
6148
oSelf.fireEvent("tableMouseupEvent",{target:(elTarget || oSelf._elContainer),event:e});
6152
* Handles dblclick events on the DataTable instance.
6154
* @method _onTableDblclick
6155
* @param e {HTMLEvent} The dblclick event.
6156
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6159
_onTableDblclick : function(e, oSelf) {
6160
var elTarget = Ev.getTarget(e);
6161
var elTag = elTarget.nodeName.toLowerCase();
6162
var bKeepBubbling = true;
6163
while(elTarget && (elTag != "table")) {
6168
bKeepBubbling = oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
6171
if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6172
bKeepBubbling = oSelf.fireEvent("theadLabelDblclickEvent",{target:elTarget,event:e});
6173
// Backward compatibility
6174
bKeepBubbling = oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
6178
bKeepBubbling = oSelf.fireEvent("theadCellDblclickEvent",{target:elTarget,event:e});
6179
// Backward compatibility
6180
bKeepBubbling = oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
6183
if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
6184
bKeepBubbling = oSelf.fireEvent("theadRowDblclickEvent",{target:elTarget,event:e});
6185
// Backward compatibility
6186
bKeepBubbling = oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
6189
bKeepBubbling = oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
6195
if(bKeepBubbling === false) {
6199
elTarget = elTarget.parentNode;
6201
elTag = elTarget.nodeName.toLowerCase();
6205
oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elContainer),event:e});
6208
* Handles keydown events on the THEAD element.
6210
* @method _onTheadKeydown
6211
* @param e {HTMLEvent} The key event.
6212
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6215
_onTheadKeydown : function(e, oSelf) {
6216
var elTarget = Ev.getTarget(e);
6217
var elTag = elTarget.nodeName.toLowerCase();
6218
var bKeepBubbling = true;
6219
while(elTarget && (elTag != "table")) {
6225
// TODO: implement textareaKeyEvent
6228
bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
6233
if(bKeepBubbling === false) {
6237
elTarget = elTarget.parentNode;
6239
elTag = elTarget.nodeName.toLowerCase();
6243
oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
6247
* Handles keydown events on the TBODY element. Handles selection behavior,
6248
* provides hooks for ENTER to edit functionality.
6250
* @method _onTbodyKeydown
6251
* @param e {HTMLEvent} The key event.
6252
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6255
_onTbodyKeydown : function(e, oSelf) {
6256
var sMode = oSelf.get("selectionMode");
6258
if(sMode == "standard") {
6259
oSelf._handleStandardSelectionByKey(e);
6261
else if(sMode == "single") {
6262
oSelf._handleSingleSelectionByKey(e);
6264
else if(sMode == "cellblock") {
6265
oSelf._handleCellBlockSelectionByKey(e);
6267
else if(sMode == "cellrange") {
6268
oSelf._handleCellRangeSelectionByKey(e);
6270
else if(sMode == "singlecell") {
6271
oSelf._handleSingleCellSelectionByKey(e);
6274
if(oSelf._oCellEditor) {
6275
if(oSelf._oCellEditor.fireEvent) {
6276
oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
6278
else if(oSelf._oCellEditor.isActive) {
6279
oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
6283
var elTarget = Ev.getTarget(e);
6284
var elTag = elTarget.nodeName.toLowerCase();
6285
var bKeepBubbling = true;
6286
while(elTarget && (elTag != "table")) {
6291
bKeepBubbling = oSelf.fireEvent("tbodyKeyEvent",{target:elTarget,event:e});
6296
if(bKeepBubbling === false) {
6300
elTarget = elTarget.parentNode;
6302
elTag = elTarget.nodeName.toLowerCase();
6306
oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
6310
* Handles keypress events on the TABLE. Mainly to support stopEvent on Mac.
6312
* @method _onTableKeypress
6313
* @param e {HTMLEvent} The key event.
6314
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6317
_onTableKeypress : function(e, oSelf) {
6318
if(ua.opera || (navigator.userAgent.toLowerCase().indexOf("mac") !== -1) && (ua.webkit < 420)) {
6319
var nKey = Ev.getCharCode(e);
6325
else if(nKey == 38) {
6332
* Handles click events on the THEAD element.
6334
* @method _onTheadClick
6335
* @param e {HTMLEvent} The click event.
6336
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6339
_onTheadClick : function(e, oSelf) {
6340
// This blurs the CellEditor
6341
if(oSelf._oCellEditor) {
6342
if(oSelf._oCellEditor.fireEvent) {
6343
oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
6345
// Backward compatibility
6346
else if(oSelf._oCellEditor.isActive) {
6347
oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
6351
var elTarget = Ev.getTarget(e),
6352
elTag = elTarget.nodeName.toLowerCase(),
6353
bKeepBubbling = true;
6354
while(elTarget && (elTag != "table")) {
6359
var sType = elTarget.type.toLowerCase();
6360
if(sType == "checkbox") {
6361
bKeepBubbling = oSelf.fireEvent("theadCheckboxClickEvent",{target:elTarget,event:e});
6363
else if(sType == "radio") {
6364
bKeepBubbling = oSelf.fireEvent("theadRadioClickEvent",{target:elTarget,event:e});
6366
else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
6367
bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
6371
bKeepBubbling = oSelf.fireEvent("theadLinkClickEvent",{target:elTarget,event:e});
6374
bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
6377
if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
6378
bKeepBubbling = oSelf.fireEvent("theadLabelClickEvent",{target:elTarget,event:e});
6379
// Backward compatibility
6380
bKeepBubbling = oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
6384
bKeepBubbling = oSelf.fireEvent("theadCellClickEvent",{target:elTarget,event:e});
6385
// Backward compatibility
6386
bKeepBubbling = oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
6389
bKeepBubbling = oSelf.fireEvent("theadRowClickEvent",{target:elTarget,event:e});
6390
// Backward compatibility
6391
bKeepBubbling = oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
6396
if(bKeepBubbling === false) {
6400
elTarget = elTarget.parentNode;
6402
elTag = elTarget.nodeName.toLowerCase();
6406
oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
6410
* Handles click events on the primary TBODY element.
6412
* @method _onTbodyClick
6413
* @param e {HTMLEvent} The click event.
6414
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6417
_onTbodyClick : function(e, oSelf) {
6418
// This blurs the CellEditor
6419
if(oSelf._oCellEditor) {
6420
if(oSelf._oCellEditor.fireEvent) {
6421
oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
6423
else if(oSelf._oCellEditor.isActive) {
6424
oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
6428
// Fire Custom Events
6429
var elTarget = Ev.getTarget(e),
6430
elTag = elTarget.nodeName.toLowerCase(),
6431
bKeepBubbling = true;
6432
while(elTarget && (elTag != "table")) {
6437
var sType = elTarget.type.toLowerCase();
6438
if(sType == "checkbox") {
6439
bKeepBubbling = oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e});
6441
else if(sType == "radio") {
6442
bKeepBubbling = oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e});
6444
else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
6445
bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
6449
bKeepBubbling = oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
6452
bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
6455
bKeepBubbling = oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
6458
bKeepBubbling = oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
6463
if(bKeepBubbling === false) {
6467
elTarget = elTarget.parentNode;
6469
elTag = elTarget.nodeName.toLowerCase();
6473
oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
6477
* Handles change events on SELECT elements within DataTable.
6479
* @method _onDropdownChange
6480
* @param e {HTMLEvent} The change event.
6481
* @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
6484
_onDropdownChange : function(e, oSelf) {
6485
var elTarget = Ev.getTarget(e);
6486
oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget});
6520
/////////////////////////////////////////////////////////////////////////////
6522
// Public member variables
6524
/////////////////////////////////////////////////////////////////////////////
6526
* Returns object literal of initial configs.
6535
/////////////////////////////////////////////////////////////////////////////
6539
/////////////////////////////////////////////////////////////////////////////
6542
* Returns unique id assigned to instance, which is a useful prefix for
6543
* generating unique DOM ID strings.
6546
* @return {String} Unique ID of the DataSource instance.
6548
getId : function() {
6553
* DataSource instance name, for logging.
6556
* @return {String} Unique name of the DataSource instance.
6559
toString : function() {
6560
return "DataTable instance " + this._sId;
6564
* Returns the DataTable instance's DataSource instance.
6566
* @method getDataSource
6567
* @return {YAHOO.util.DataSource} DataSource instance.
6569
getDataSource : function() {
6570
return this._oDataSource;
6574
* Returns the DataTable instance's ColumnSet instance.
6576
* @method getColumnSet
6577
* @return {YAHOO.widget.ColumnSet} ColumnSet instance.
6579
getColumnSet : function() {
6580
return this._oColumnSet;
6584
* Returns the DataTable instance's RecordSet instance.
6586
* @method getRecordSet
6587
* @return {YAHOO.widget.RecordSet} RecordSet instance.
6589
getRecordSet : function() {
6590
return this._oRecordSet;
6594
* Returns on object literal representing the DataTable instance's current
6595
* state with the following properties:
6597
* <dt>pagination</dt>
6598
* <dd>Instance of YAHOO.widget.Paginator</dd>
6603
* <dt>sortedBy.key</dt>
6604
* <dd>{String} Key of sorted Column</dd>
6605
* <dt>sortedBy.dir</dt>
6606
* <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
6610
* <dt>selectedRows</dt>
6611
* <dd>Array of selected rows by Record ID.</dd>
6613
* <dt>selectedCells</dt>
6614
* <dd>Selected cells as an array of object literals:
6615
* {recordId:sRecordId, columnKey:sColumnKey}</dd>
6619
* @return {Object} DataTable instance state object literal values.
6621
getState : function() {
6623
totalRecords: this.get('paginator') ? this.get('paginator').get("totalRecords") : this._oRecordSet.getLength(),
6624
pagination: this.get("paginator") ? this.get("paginator").getState() : null,
6625
sortedBy: this.get("sortedBy"),
6626
selectedRows: this.getSelectedRows(),
6627
selectedCells: this.getSelectedCells()
6676
* Returns DOM reference to the DataTable's container element.
6678
* @method getContainerEl
6679
* @return {HTMLElement} Reference to DIV element.
6681
getContainerEl : function() {
6682
return this._elContainer;
6686
* Returns DOM reference to the DataTable's TABLE element.
6688
* @method getTableEl
6689
* @return {HTMLElement} Reference to TABLE element.
6691
getTableEl : function() {
6692
return this._elTable;
6696
* Returns DOM reference to the DataTable's THEAD element.
6698
* @method getTheadEl
6699
* @return {HTMLElement} Reference to THEAD element.
6701
getTheadEl : function() {
6702
return this._elThead;
6706
* Returns DOM reference to the DataTable's primary TBODY element.
6708
* @method getTbodyEl
6709
* @return {HTMLElement} Reference to TBODY element.
6711
getTbodyEl : function() {
6712
return this._elTbody;
6716
* Returns DOM reference to the DataTable's secondary TBODY element that is
6717
* used to display messages.
6719
* @method getMsgTbodyEl
6720
* @return {HTMLElement} Reference to TBODY element.
6722
getMsgTbodyEl : function() {
6723
return this._elMsgTbody;
6727
* Returns DOM reference to the TD element within the secondary TBODY that is
6728
* used to display messages.
6730
* @method getMsgTdEl
6731
* @return {HTMLElement} Reference to TD element.
6733
getMsgTdEl : function() {
6734
return this._elMsgTd;
6738
* Returns the corresponding TR reference for a given DOM element, ID string or
6739
* directly page row index. If the given identifier is a child of a TR element,
6740
* then DOM tree is traversed until a parent TR element is returned, otherwise
6744
* @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to
6745
* get: by element reference, ID string, page row index, or Record.
6746
* @return {HTMLElement} Reference to TR element, or null.
6748
getTrEl : function(row) {
6750
if(row instanceof YAHOO.widget.Record) {
6751
return document.getElementById(row.getId());
6753
// By page row index
6754
else if(lang.isNumber(row)) {
6755
var allRows = this._elTbody.rows;
6756
return ((row > -1) && (row < allRows.length)) ? allRows[row] : null;
6758
// By ID string or element reference
6760
var elRow = (lang.isString(row)) ? document.getElementById(row) : row;
6762
// Validate HTML element
6763
if(elRow && (elRow.ownerDocument == document)) {
6764
// Validate TR element
6765
if(elRow.nodeName.toLowerCase() != "tr") {
6766
// Traverse up the DOM to find the corresponding TR element
6767
elRow = Dom.getAncestorByTagName(elRow,"tr");
6778
* Returns DOM reference to the first TR element in the DataTable page, or null.
6780
* @method getFirstTrEl
6781
* @return {HTMLElement} Reference to TR element.
6783
getFirstTrEl : function() {
6784
return this._elTbody.rows[0] || null;
6788
* Returns DOM reference to the last TR element in the DataTable page, or null.
6790
* @method getLastTrEl
6791
* @return {HTMLElement} Reference to last TR element.
6793
getLastTrEl : function() {
6794
var allRows = this._elTbody.rows;
6795
if(allRows.length > 0) {
6796
return allRows[allRows.length-1] || null;
6801
* Returns DOM reference to the next TR element from the given TR element, or null.
6803
* @method getNextTrEl
6804
* @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
6805
* reference, ID string, page row index, or Record from which to get next TR element.
6806
* @return {HTMLElement} Reference to next TR element.
6808
getNextTrEl : function(row) {
6809
var nThisTrIndex = this.getTrIndex(row);
6810
if(nThisTrIndex !== null) {
6811
var allRows = this._elTbody.rows;
6812
if(nThisTrIndex < allRows.length-1) {
6813
return allRows[nThisTrIndex+1];
6817
YAHOO.log("Could not get next TR element for row " + row, "info", this.toString());
6822
* Returns DOM reference to the previous TR element from the given TR element, or null.
6824
* @method getPreviousTrEl
6825
* @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
6826
* reference, ID string, page row index, or Record from which to get previous TR element.
6827
* @return {HTMLElement} Reference to previous TR element.
6829
getPreviousTrEl : function(row) {
6830
var nThisTrIndex = this.getTrIndex(row);
6831
if(nThisTrIndex !== null) {
6832
var allRows = this._elTbody.rows;
6833
if(nThisTrIndex > 0) {
6834
return allRows[nThisTrIndex-1];
6838
YAHOO.log("Could not get previous TR element for row " + row, "info", this.toString());
6843
* Returns DOM reference to a TD liner element.
6845
* @method getTdLinerEl
6846
* @param cell {HTMLElement | Object} TD element or child of a TD element, or
6847
* object literal of syntax {record:oRecord, column:oColumn}.
6848
* @return {HTMLElement} Reference to TD liner element.
6850
getTdLinerEl : function(cell) {
6851
var elCell = this.getTdEl(cell);
6852
return elCell.firstChild || null;
6856
* Returns DOM reference to a TD element.
6859
* @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
6860
* object literal of syntax {record:oRecord, column:oColumn}.
6861
* @return {HTMLElement} Reference to TD element.
6863
getTdEl : function(cell) {
6865
var el = Dom.get(cell);
6867
// Validate HTML element
6868
if(el && (el.ownerDocument == document)) {
6869
// Validate TD element
6870
if(el.nodeName.toLowerCase() != "td") {
6871
// Traverse up the DOM to find the corresponding TR element
6872
elCell = Dom.getAncestorByTagName(el, "td");
6881
var oRecord, nColKeyIndex;
6883
if(lang.isString(cell.columnKey) && lang.isString(cell.recordId)) {
6884
oRecord = this.getRecord(cell.recordId);
6885
var oColumn = this.getColumn(cell.columnKey);
6887
nColKeyIndex = oColumn.getKeyIndex();
6891
if(cell.record && cell.column && cell.column.getKeyIndex) {
6892
oRecord = cell.record;
6893
nColKeyIndex = cell.column.getKeyIndex();
6895
var elRow = this.getTrEl(oRecord);
6896
if((nColKeyIndex !== null) && elRow && elRow.cells && elRow.cells.length > 0) {
6897
return elRow.cells[nColKeyIndex] || null;
6905
* Returns DOM reference to the first TD element in the DataTable page (by default),
6906
* the first TD element of the optionally given row, or null.
6908
* @method getFirstTdEl
6909
* @param row {HTMLElement} (optional) row from which to get first TD
6910
* @return {HTMLElement} Reference to TD element.
6912
getFirstTdEl : function(row) {
6913
var elRow = this.getTrEl(row) || this.getFirstTrEl();
6914
if(elRow && (elRow.cells.length > 0)) {
6915
return elRow.cells[0];
6917
YAHOO.log("Could not get first TD element for row " + elRow, "info", this.toString());
6922
* Returns DOM reference to the last TD element in the DataTable page (by default),
6923
* the first TD element of the optionally given row, or null.
6925
* @method getLastTdEl
6926
* @return {HTMLElement} Reference to last TD element.
6928
getLastTdEl : function(row) {
6929
var elRow = this.getTrEl(row) || this.getLastTrEl();
6930
if(elRow && (elRow.cells.length > 0)) {
6931
return elRow.cells[elRow.cells.length-1];
6933
YAHOO.log("Could not get last TD element for row " + elRow, "info", this.toString());
6938
* Returns DOM reference to the next TD element from the given cell, or null.
6940
* @method getNextTdEl
6941
* @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
6942
* object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
6943
* @return {HTMLElement} Reference to next TD element, or null.
6945
getNextTdEl : function(cell) {
6946
var elCell = this.getTdEl(cell);
6948
var nThisTdIndex = elCell.cellIndex;
6949
var elRow = this.getTrEl(elCell);
6950
if(nThisTdIndex < elRow.cells.length-1) {
6951
return elRow.cells[nThisTdIndex+1];
6954
var elNextRow = this.getNextTrEl(elRow);
6956
return elNextRow.cells[0];
6960
YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
6965
* Returns DOM reference to the previous TD element from the given cell, or null.
6967
* @method getPreviousTdEl
6968
* @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
6969
* object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
6970
* @return {HTMLElement} Reference to previous TD element, or null.
6972
getPreviousTdEl : function(cell) {
6973
var elCell = this.getTdEl(cell);
6975
var nThisTdIndex = elCell.cellIndex;
6976
var elRow = this.getTrEl(elCell);
6977
if(nThisTdIndex > 0) {
6978
return elRow.cells[nThisTdIndex-1];
6981
var elPreviousRow = this.getPreviousTrEl(elRow);
6983
return this.getLastTdEl(elPreviousRow);
6987
YAHOO.log("Could not get next TD element for cell " + cell, "info", this.toString());
6992
* Returns DOM reference to the above TD element from the given cell, or null.
6994
* @method getAboveTdEl
6995
* @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
6996
* object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
6997
* @return {HTMLElement} Reference to next TD element, or null.
6999
getAboveTdEl : function(cell) {
7000
var elCell = this.getTdEl(cell);
7002
var elPreviousRow = this.getPreviousTrEl(elCell);
7004
return elPreviousRow.cells[elCell.cellIndex];
7007
YAHOO.log("Could not get above TD element for cell " + cell, "info", this.toString());
7012
* Returns DOM reference to the below TD element from the given cell, or null.
7014
* @method getBelowTdEl
7015
* @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
7016
* object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
7017
* @return {HTMLElement} Reference to previous TD element, or null.
7019
getBelowTdEl : function(cell) {
7020
var elCell = this.getTdEl(cell);
7022
var elNextRow = this.getNextTrEl(elCell);
7024
return elNextRow.cells[elCell.cellIndex];
7027
YAHOO.log("Could not get below TD element for cell " + cell, "info", this.toString());
7032
* Returns DOM reference to a TH liner element. Needed to normalize for resizeable
7033
* Columns, which have an additional resizer liner DIV element between the TH
7034
* element and the liner DIV element.
7036
* @method getThLinerEl
7037
* @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
7038
* DOM element reference, or string ID.
7039
* @return {HTMLElement} Reference to TH liner element.
7041
getThLinerEl : function(theadCell) {
7042
var oColumn = this.getColumn(theadCell);
7043
return (oColumn) ? oColumn.getThLinerEl() : null;
7047
* Returns DOM reference to a TH element.
7050
* @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
7051
* DOM element reference, or string ID.
7052
* @return {HTMLElement} Reference to TH element.
7054
getThEl : function(theadCell) {
7057
// Validate Column instance
7058
if(theadCell instanceof YAHOO.widget.Column) {
7059
var oColumn = theadCell;
7060
elTh = oColumn.getThEl();
7065
// Validate HTML element
7067
var el = Dom.get(theadCell);
7069
if(el && (el.ownerDocument == document)) {
7070
// Validate TH element
7071
if(el.nodeName.toLowerCase() != "th") {
7072
// Traverse up the DOM to find the corresponding TR element
7073
elTh = Dom.getAncestorByTagName(el,"th");
7087
* Returns the page row index of given row. Returns null if the row is not on the
7088
* current DataTable page.
7090
* @method getTrIndex
7091
* @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID
7092
* string reference to an element within the DataTable page, a Record instance,
7093
* or a Record's RecordSet index.
7094
* @return {Number} Page row index, or null if row does not exist or is not on current page.
7096
getTrIndex : function(row) {
7100
if(row instanceof YAHOO.widget.Record) {
7101
nRecordIndex = this._oRecordSet.getRecordIndex(row);
7102
if(nRecordIndex === null) {
7103
// Not a valid Record
7107
// Calculate page row index from Record index
7108
else if(lang.isNumber(row)) {
7111
if(lang.isNumber(nRecordIndex)) {
7112
// Validate the number
7113
if((nRecordIndex > -1) && (nRecordIndex < this._oRecordSet.getLength())) {
7114
// DataTable is paginated
7115
var oPaginator = this.get('paginator');
7117
// Check the record index is within the indices of the
7119
var rng = oPaginator.getPageRecords();
7120
if (rng && nRecordIndex >= rng[0] && nRecordIndex <= rng[1]) {
7121
// This Record is on current page
7122
return nRecordIndex - rng[0];
7124
// This Record is not on current page
7129
// Not paginated, just return the Record index
7131
return nRecordIndex;
7134
// RecordSet index is out of range
7139
// By element reference or ID string
7141
// Validate TR element
7142
var elRow = this.getTrEl(row);
7143
if(elRow && (elRow.ownerDocument == document) &&
7144
(elRow.parentNode == this._elTbody)) {
7145
return elRow.sectionRowIndex;
7149
YAHOO.log("Could not get page row index for row " + row, "info", this.toString());
7201
* Resets a RecordSet with the given data and populates the page view
7202
* with the new data. Any previous data, and selection and sort states are
7203
* cleared. New data should be added as a separate step.
7205
* @method initializeTable
7207
initializeTable : function() {
7211
// Clear the RecordSet
7212
this._oRecordSet.reset();
7214
// Clear the Paginator's totalRecords if paginating
7215
var pag = this.get('paginator');
7217
pag.set('totalRecords',0);
7221
this._unselectAllTrEls();
7222
this._unselectAllTdEls();
7223
this._aSelections = null;
7224
this._oAnchorRecord = null;
7225
this._oAnchorCell = null;
7228
this.set("sortedBy", null);
7232
* Internal wrapper calls run() on render Chain instance.
7234
* @method _runRenderChain
7237
_runRenderChain : function() {
7238
this._oChainRender.run();
7242
* Renders the view with existing Records from the RecordSet while
7243
* maintaining sort, pagination, and selection states. For performance, reuses
7244
* existing DOM elements when possible while deleting extraneous elements.
7248
render : function() {
7249
//YAHOO.example.Performance.trialStart = new Date();
7251
this._oChainRender.stop();
7252
YAHOO.log("DataTable rendering...", "info", this.toString());
7254
var i, j, k, len, allRecords;
7256
var oPaginator = this.get('paginator');
7257
// Paginator is enabled, show a subset of Records and update Paginator UI
7259
allRecords = this._oRecordSet.getRecords(
7260
oPaginator.getStartIndex(),
7261
oPaginator.getRowsPerPage());
7263
// Not paginated, show all records
7265
allRecords = this._oRecordSet.getRecords();
7268
// From the top, update in-place existing rows, so as to reuse DOM elements
7269
var elTbody = this._elTbody,
7270
loopN = this.get("renderLoopSize"),
7271
nRecordsLength = allRecords.length;
7274
if(nRecordsLength > 0) {
7275
elTbody.style.display = "none";
7276
while(elTbody.lastChild) {
7277
elTbody.removeChild(elTbody.lastChild);
7279
elTbody.style.display = "";
7281
// Set up the loop Chain to render rows
7282
this._oChainRender.add({
7283
method: function(oArg) {
7284
if((this instanceof DT) && this._sId) {
7285
var i = oArg.nCurrentRecord,
7286
endRecordIndex = ((oArg.nCurrentRecord+oArg.nLoopLength) > nRecordsLength) ?
7287
nRecordsLength : (oArg.nCurrentRecord+oArg.nLoopLength),
7290
elTbody.style.display = "none";
7292
for(; i<endRecordIndex; i++) {
7293
elRow = Dom.get(allRecords[i].getId());
7294
elRow = elRow || this._addTrEl(allRecords[i]);
7295
nextSibling = elTbody.childNodes[i] || null;
7296
elTbody.insertBefore(elRow, nextSibling);
7298
elTbody.style.display = "";
7300
// Set up for the next loop
7301
oArg.nCurrentRecord = i;
7305
iterations: (loopN > 0) ? Math.ceil(nRecordsLength/loopN) : 1,
7307
nCurrentRecord: 0,//nRecordsLength-1, // Start at first Record
7308
nLoopLength: (loopN > 0) ? loopN : nRecordsLength
7310
timeout: (loopN > 0) ? 0 : -1
7313
// Post-render tasks
7314
this._oChainRender.add({
7315
method: function(oArg) {
7316
if((this instanceof DT) && this._sId) {
7317
while(elTbody.rows.length > nRecordsLength) {
7318
elTbody.removeChild(elTbody.lastChild);
7320
this._setFirstRow();
7322
this._setRowStripes();
7323
this._setSelections();
7327
timeout: (loopN > 0) ? 0 : -1
7331
// Table has no rows
7333
// Set up the loop Chain to delete rows
7334
var nTotal = elTbody.rows.length;
7336
this._oChainRender.add({
7337
method: function(oArg) {
7338
if((this instanceof DT) && this._sId) {
7339
var i = oArg.nCurrent,
7340
loopN = oArg.nLoopLength,
7341
nIterEnd = (i - loopN < 0) ? -1 : i - loopN;
7343
elTbody.style.display = "none";
7345
for(; i>nIterEnd; i--) {
7346
elTbody.deleteRow(-1);
7348
elTbody.style.display = "";
7350
// Set up for the next loop
7355
iterations: (loopN > 0) ? Math.ceil(nTotal/loopN) : 1,
7358
nLoopLength: (loopN > 0) ? loopN : nTotal
7360
timeout: (loopN > 0) ? 0 : -1
7364
this._runRenderChain();
7368
* Disables DataTable UI.
7372
disable : function() {
7373
var elTable = this._elTable;
7374
var elMask = this._elMask;
7375
elMask.style.width = elTable.offsetWidth + "px";
7376
elMask.style.height = elTable.offsetHeight + "px";
7377
elMask.style.display = "";
7378
this.fireEvent("disableEvent");
7382
* Undisables DataTable UI.
7386
undisable : function() {
7387
this._elMask.style.display = "none";
7388
this.fireEvent("undisableEvent");
7392
* Nulls out the entire DataTable instance and related objects, removes attached
7393
* event listeners, and clears out DOM elements inside the container. After
7394
* calling this method, the instance reference should be expliclitly nulled by
7395
* implementer, as in myDataTable = null. Use with caution!
7399
destroy : function() {
7401
var instanceName = this.toString();
7403
this._oChainRender.stop();
7405
// Destroy static resizer proxy and column proxy
7406
DT._destroyColumnDragTargetEl();
7407
DT._destroyColumnResizerProxyEl();
7409
// Destroy ColumnDD and ColumnResizers
7410
this._destroyColumnHelpers();
7412
// Destroy all CellEditors
7414
for(var i=0, len=this._oColumnSet.flat.length; i<len; i++) {
7415
oCellEditor = this._oColumnSet.flat[i].editor;
7416
if(oCellEditor && oCellEditor.destroy) {
7417
oCellEditor.destroy();
7418
this._oColumnSet.flat[i].editor = null;
7422
// Unhook custom events
7423
this._oRecordSet.unsubscribeAll();
7424
this.unsubscribeAll();
7426
// Unhook DOM events
7427
Ev.removeListener(document, "click", this._onDocumentClick);
7429
// Clear out the container
7430
this._destroyContainerEl(this._elContainer);
7433
for(var param in this) {
7434
if(lang.hasOwnProperty(this, param)) {
7439
// Clean up static values
7440
DT._nCurrentCount--;
7442
if(DT._nCurrentCount < 1) {
7443
if(DT._elDynStyleNode) {
7444
document.getElementsByTagName('head')[0].removeChild(DT._elDynStyleNode);
7445
DT._elDynStyleNode = null;
7449
YAHOO.log("DataTable instance destroyed: " + instanceName);
7453
* Displays message within secondary TBODY.
7455
* @method showTableMessage
7456
* @param sHTML {String} (optional) Value for innerHTMlang.
7457
* @param sClassName {String} (optional) Classname.
7459
showTableMessage : function(sHTML, sClassName) {
7460
var elCell = this._elMsgTd;
7461
if(lang.isString(sHTML)) {
7462
elCell.firstChild.innerHTML = sHTML;
7464
if(lang.isString(sClassName)) {
7465
elCell.className = sClassName;
7468
this._elMsgTbody.style.display = "";
7470
this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
7471
YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
7475
* Hides secondary TBODY.
7477
* @method hideTableMessage
7479
hideTableMessage : function() {
7480
if(this._elMsgTbody.style.display != "none") {
7481
this._elMsgTbody.style.display = "none";
7482
this._elMsgTbody.parentNode.style.width = "";
7483
this.fireEvent("tableMsgHideEvent");
7484
YAHOO.log("DataTable message hidden", "info", this.toString());
7489
* Brings focus to the TBODY element. Alias to focusTbodyEl.
7493
focus : function() {
7494
this.focusTbodyEl();
7498
* Brings focus to the THEAD element.
7500
* @method focusTheadEl
7502
focusTheadEl : function() {
7503
this._focusEl(this._elThead);
7507
* Brings focus to the TBODY element.
7509
* @method focusTbodyEl
7511
focusTbodyEl : function() {
7512
this._focusEl(this._elTbody);
7516
* Setting display:none on DataTable or any parent may impact width validations.
7517
* After setting display back to "", implementers should call this method to
7518
* manually perform those validations.
7522
onShow : function() {
7523
this.validateColumnWidths();
7525
for(var allKeys = this._oColumnSet.keys, i=0, len=allKeys.length, col; i<len; i++) {
7527
if(col._ddResizer) {
7528
col._ddResizer.resetResizerEl();
7599
// RECORDSET FUNCTIONS
7602
* Returns Record index for given TR element or page row index.
7604
* @method getRecordIndex
7605
* @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR
7606
* element reference or page row index.
7607
* @return {Number} Record's RecordSet index, or null.
7609
getRecordIndex : function(row) {
7612
if(!lang.isNumber(row)) {
7614
if(row instanceof YAHOO.widget.Record) {
7615
return this._oRecordSet.getRecordIndex(row);
7617
// By element reference
7619
// Find the TR element
7620
var el = this.getTrEl(row);
7622
nTrIndex = el.sectionRowIndex;
7626
// By page row index
7631
if(lang.isNumber(nTrIndex)) {
7632
var oPaginator = this.get("paginator");
7634
return oPaginator.get('recordOffset') + nTrIndex;
7641
YAHOO.log("Could not get Record index for row " + row, "info", this.toString());
7646
* For the given identifier, returns the associated Record instance.
7649
* @param row {HTMLElement | Number | String} DOM reference to a TR element (or
7650
* child of a TR element), RecordSet position index, or Record ID.
7651
* @return {YAHOO.widget.Record} Record instance.
7653
getRecord : function(row) {
7654
var oRecord = this._oRecordSet.getRecord(row);
7657
// Validate TR element
7658
var elRow = this.getTrEl(row);
7660
oRecord = this._oRecordSet.getRecord(this.getRecordIndex(elRow.sectionRowIndex));
7664
if(oRecord instanceof YAHOO.widget.Record) {
7665
return this._oRecordSet.getRecord(oRecord);
7668
YAHOO.log("Could not get Record for row at " + row, "info", this.toString());
7721
* For the given identifier, returns the associated Column instance. Note: For
7722
* getting Columns by Column ID string, please use the method getColumnById().
7725
* @param column {HTMLElement | String | Number} TH/TD element (or child of a
7726
* TH/TD element), a Column key, or a ColumnSet key index.
7727
* @return {YAHOO.widget.Column} Column instance.
7729
getColumn : function(column) {
7730
var oColumn = this._oColumnSet.getColumn(column);
7733
// Validate TD element
7734
var elCell = this.getTdEl(column);
7736
oColumn = this._oColumnSet.getColumn(elCell.cellIndex);
7738
// Validate TH element
7740
elCell = this.getThEl(column);
7743
var allColumns = this._oColumnSet.flat;
7744
for(var i=0, len=allColumns.length; i<len; i++) {
7745
if(allColumns[i].getThEl().id === elCell.id) {
7746
oColumn = allColumns[i];
7753
YAHOO.log("Could not get Column for column at " + column, "info", this.toString());
7759
* For the given Column ID, returns the associated Column instance. Note: For
7760
* getting Columns by key, please use the method getColumn().
7762
* @method getColumnById
7763
* @param column {String} Column ID string.
7764
* @return {YAHOO.widget.Column} Column instance.
7766
getColumnById : function(column) {
7767
return this._oColumnSet.getColumnById(column);
7771
* For the given Column instance, returns next direction to sort.
7773
* @method getColumnSortDir
7774
* @param oColumn {YAHOO.widget.Column} Column instance.
7775
* @param oSortedBy {Object} (optional) Specify the state, or use current state.
7776
* @return {String} YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTableCLASS_DESC.
7778
getColumnSortDir : function(oColumn, oSortedBy) {
7779
// Backward compatibility
7780
if(oColumn.sortOptions && oColumn.sortOptions.defaultOrder) {
7781
if(oColumn.sortOptions.defaultOrder == "asc") {
7782
oColumn.sortOptions.defaultDir = DT.CLASS_ASC;
7784
else if (oColumn.sortOptions.defaultOrder == "desc") {
7785
oColumn.sortOptions.defaultDir = DT.CLASS_DESC;
7789
// What is the Column's default sort direction?
7790
var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultDir) ? oColumn.sortOptions.defaultDir : DT.CLASS_ASC;
7792
// Is the Column currently sorted?
7793
var bSorted = false;
7794
oSortedBy = oSortedBy || this.get("sortedBy");
7795
if(oSortedBy && (oSortedBy.key === oColumn.key)) {
7798
sortDir = (oSortedBy.dir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
7801
sortDir = (sortDir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
7808
* Overridable method gives implementers a hook to show loading message before
7811
* @method doBeforeSortColumn
7812
* @param oColumn {YAHOO.widget.Column} Column instance.
7813
* @param sSortDir {String} YAHOO.widget.DataTable.CLASS_ASC or
7814
* YAHOO.widget.DataTable.CLASS_DESC.
7815
* @return {Boolean} Return true to continue sorting Column.
7817
doBeforeSortColumn : function(oColumn, sSortDir) {
7818
this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
7823
* Sorts given Column. If "dynamicData" is true, current selections are purged before
7824
* a request is sent to the DataSource for data for the new state (using the
7825
* request returned by "generateRequest()").
7827
* @method sortColumn
7828
* @param oColumn {YAHOO.widget.Column} Column instance.
7829
* @param sDir {String} (Optional) YAHOO.widget.DataTable.CLASS_ASC or
7830
* YAHOO.widget.DataTable.CLASS_DESC
7832
sortColumn : function(oColumn, sDir) {
7833
if(oColumn && (oColumn instanceof YAHOO.widget.Column)) {
7834
if(!oColumn.sortable) {
7835
Dom.addClass(this.getThEl(oColumn), DT.CLASS_SORTABLE);
7838
// Validate given direction
7839
if(sDir && (sDir !== DT.CLASS_ASC) && (sDir !== DT.CLASS_DESC)) {
7844
var sSortDir = sDir || this.getColumnSortDir(oColumn);
7846
// Is the Column currently sorted?
7847
var oSortedBy = this.get("sortedBy") || {};
7848
var bSorted = (oSortedBy.key === oColumn.key) ? true : false;
7850
var ok = this.doBeforeSortColumn(oColumn, sSortDir);
7853
if(this.get("dynamicData")) {
7854
// Get current state
7855
var oState = this.getState();
7857
// Reset record offset, if paginated
7858
if(oState.pagination) {
7859
oState.pagination.recordOffset = 0;
7862
// Update sortedBy to new values
7868
// Get the request for the new state
7869
var request = this.get("generateRequest")(oState, this);
7872
this.unselectAllRows();
7873
this.unselectAllCells();
7875
// Send request for new data
7877
success : this.onDataReturnSetRows,
7878
failure : this.onDataReturnSetRows,
7879
argument : oState, // Pass along the new state to the callback
7882
this._oDataSource.sendRequest(request, callback);
7886
// Is there a custom sort handler function defined?
7887
var sortFnc = (oColumn.sortOptions && lang.isFunction(oColumn.sortOptions.sortFunction)) ?
7888
// Custom sort function
7889
oColumn.sortOptions.sortFunction : null;
7892
if(!bSorted || sDir || sortFnc) {
7893
// Get the field to sort
7894
var sField = (oColumn.sortOptions && oColumn.sortOptions.field) ? oColumn.sortOptions.field : oColumn.field;
7896
// Default sort function if necessary
7897
sortFnc = sortFnc ||
7898
function(a, b, desc) {
7899
var sorted = YAHOO.util.Sort.compare(a.getData(sField),b.getData(sField), desc);
7901
return YAHOO.util.Sort.compare(a.getCount(),b.getCount(), desc); // Bug 1932978
7908
this._oRecordSet.sortRecords(sortFnc, ((sSortDir == DT.CLASS_DESC) ? true : false));
7910
// Just reverse the Records
7912
this._oRecordSet.reverseRecords();
7915
// Reset to first page if paginated
7916
var oPaginator = this.get('paginator');
7918
// Set page silently, so as not to fire change event.
7919
oPaginator.setPage(1,true);
7922
// Update UI via sortedBy
7924
this.set("sortedBy", {key:oColumn.key, dir:sSortDir, column:oColumn});
7927
this.fireEvent("columnSortEvent",{column:oColumn,dir:sSortDir});
7928
YAHOO.log("Column \"" + oColumn.key + "\" sorted \"" + sSortDir + "\"", "info", this.toString());
7932
YAHOO.log("Could not sort Column \"" + oColumn.key + "\"", "warn", this.toString());
7936
* Sets given Column to given pixel width. If new width is less than minimum
7937
* width, sets to minimum width. Updates oColumn.width value.
7939
* @method setColumnWidth
7940
* @param oColumn {YAHOO.widget.Column} Column instance.
7941
* @param nWidth {Number} New width in pixels. A null value auto-sizes Column,
7942
* subject to minWidth and maxAutoWidth validations.
7944
setColumnWidth : function(oColumn, nWidth) {
7945
if(!(oColumn instanceof YAHOO.widget.Column)) {
7946
oColumn = this.getColumn(oColumn);
7949
// Validate new width against minimum width
7950
if(lang.isNumber(nWidth)) {
7951
// This is why we must require a Number... :-|
7952
nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
7955
oColumn.width = nWidth;
7957
// Resize the DOM elements
7958
this._setColumnWidth(oColumn, nWidth+"px");
7960
this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
7961
YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
7963
// Unsets a width to auto-size
7964
else if(nWidth === null) {
7966
oColumn.width = nWidth;
7968
// Resize the DOM elements
7969
this._setColumnWidth(oColumn, "auto");
7970
this.validateColumnWidths(oColumn);
7971
this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
7972
YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
7975
// Bug 2339454: resize then sort misaligment
7976
this._clearTrTemplateEl();
7979
YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
7984
* Sets liner DIV elements of given Column to given width. When value should be
7985
* auto-calculated to fit content overflow is set to visible, otherwise overflow
7986
* is set to hidden. No validations against minimum width and no updating
7987
* Column.width value.
7989
* @method _setColumnWidth
7990
* @param oColumn {YAHOO.widget.Column} Column instance.
7991
* @param sWidth {String} New width value.
7992
* @param sOverflow {String} Should be "hidden" when Column width is explicitly
7993
* being set to a value, but should be "visible" when Column is meant to auto-fit content.
7996
_setColumnWidth : function(oColumn, sWidth, sOverflow) {
7997
if(oColumn && (oColumn.getKeyIndex() !== null)) {
7998
sOverflow = sOverflow || (((sWidth === '') || (sWidth === 'auto')) ? 'visible' : 'hidden');
8000
// Dynamic style algorithm
8001
if(!DT._bDynStylesFallback) {
8002
this._setColumnWidthDynStyles(oColumn, sWidth, sOverflow);
8004
// Dynamic function algorithm
8006
this._setColumnWidthDynFunction(oColumn, sWidth, sOverflow);
8010
YAHOO.log("Could not set width of unknown Column " + oColumn + " to " + sWidth, "warn", this.toString());
8015
* Updates width of a Column's liner DIV elements by dynamically creating a
8016
* STYLE node and writing and updating CSS style rules to it. If this fails during
8017
* runtime, the fallback method _setColumnWidthDynFunction() will be called.
8018
* Notes: This technique is not performant in IE6. IE7 crashes if DataTable is
8019
* nested within another TABLE element. For these cases, it is recommended to
8020
* use the method _setColumnWidthDynFunction by setting _bDynStylesFallback to TRUE.
8022
* @method _setColumnWidthDynStyles
8023
* @param oColumn {YAHOO.widget.Column} Column instance.
8024
* @param sWidth {String} New width value.
8027
_setColumnWidthDynStyles : function(oColumn, sWidth, sOverflow) {
8028
var s = DT._elDynStyleNode,
8031
// Create a new STYLE node
8033
s = document.createElement('style');
8034
s.type = 'text/css';
8035
s = document.getElementsByTagName('head').item(0).appendChild(s);
8036
DT._elDynStyleNode = s;
8039
// We have a STYLE node to update
8041
// Use unique classname for this Column instance as a hook for resizing
8042
var sClassname = "." + this.getId() + "-col-" + oColumn.getSanitizedKey() + " ." + DT.CLASS_LINER;
8044
// Hide for performance
8046
this._elTbody.style.display = 'none';
8049
rule = DT._oDynStyles[sClassname];
8051
// The Column does not yet have a rule
8053
if(s.styleSheet && s.styleSheet.addRule) {
8054
s.styleSheet.addRule(sClassname,"overflow:"+sOverflow);
8055
s.styleSheet.addRule(sClassname,'width:'+sWidth);
8056
rule = s.styleSheet.rules[s.styleSheet.rules.length-1];
8057
DT._oDynStyles[sClassname] = rule;
8059
else if(s.sheet && s.sheet.insertRule) {
8060
s.sheet.insertRule(sClassname+" {overflow:"+sOverflow+";width:"+sWidth+";}",s.sheet.cssRules.length);
8061
rule = s.sheet.cssRules[s.sheet.cssRules.length-1];
8062
DT._oDynStyles[sClassname] = rule;
8065
// We have a rule to update
8067
rule.style.overflow = sOverflow;
8068
rule.style.width = sWidth;
8073
this._elTbody.style.display = '';
8077
// That was not a success, we must call the fallback routine
8079
DT._bDynStylesFallback = true;
8080
this._setColumnWidthDynFunction(oColumn, sWidth);
8085
* Updates width of a Column's liner DIV elements by dynamically creating a
8086
* function to update all element style properties in one pass. Note: This
8087
* technique is not supported in sandboxed environments that prohibit EVALs.
8089
* @method _setColumnWidthDynFunction
8090
* @param oColumn {YAHOO.widget.Column} Column instance.
8091
* @param sWidth {String} New width value.
8094
_setColumnWidthDynFunction : function(oColumn, sWidth, sOverflow) {
8095
// TODO: why is this here?
8096
if(sWidth == 'auto') {
8100
// Create one function for each value of rows.length
8101
var rowslen = this._elTbody ? this._elTbody.rows.length : 0;
8103
// Dynamically create the function
8104
if (!this._aDynFunctions[rowslen]) {
8106
//Compile a custom function to do all the liner div width
8107
//assignments at the same time. A unique function is required
8108
//for each unique number of rows in _elTbody. This will
8109
//result in a function declaration like:
8110
//function (oColumn,sWidth,sOverflow) {
8111
// var colIdx = oColumn.getKeyIndex();
8112
// oColumn.getThLinerEl().style.overflow =
8113
// this._elTbody.rows[0].cells[colIdx].firstChild.style.overflow =
8114
// this._elTbody.rows[1].cells[colIdx].firstChild.style.overflow =
8115
// ... (for all row indices in this._elTbody.rows.length - 1)
8116
// this._elTbody.rows[99].cells[colIdx].firstChild.style.overflow =
8118
// oColumn.getThLinerEl().style.width =
8119
// this._elTbody.rows[0].cells[colIdx].firstChild.style.width =
8120
// this._elTbody.rows[1].cells[colIdx].firstChild.style.width =
8121
// ... (for all row indices in this._elTbody.rows.length - 1)
8122
// this._elTbody.rows[99].cells[colIdx].firstChild.style.width =
8128
'var colIdx=oColumn.getKeyIndex();',
8129
'oColumn.getThLinerEl().style.overflow='
8131
for (i=rowslen-1, j=2; i >= 0; --i) {
8132
resizerDef[j++] = 'this._elTbody.rows[';
8133
resizerDef[j++] = i;
8134
resizerDef[j++] = '].cells[colIdx].firstChild.style.overflow=';
8136
resizerDef[j] = 'sOverflow;';
8137
resizerDef[j+1] = 'oColumn.getThLinerEl().style.width=';
8138
for (i=rowslen-1, k=j+2; i >= 0; --i) {
8139
resizerDef[k++] = 'this._elTbody.rows[';
8140
resizerDef[k++] = i;
8141
resizerDef[k++] = '].cells[colIdx].firstChild.style.width=';
8143
resizerDef[k] = 'sWidth;';
8144
this._aDynFunctions[rowslen] =
8145
new Function('oColumn','sWidth','sOverflow',resizerDef.join(''));
8148
// Get the function to execute
8149
var resizerFn = this._aDynFunctions[rowslen];
8151
// TODO: Hide TBODY for performance in _setColumnWidthDynFunction?
8153
resizerFn.call(this,oColumn,sWidth,sOverflow);
8158
* For one or all Columns, when Column is not hidden, width is not set, and minWidth
8159
* and/or maxAutoWidth is set, validates auto-width against minWidth and maxAutoWidth.
8161
* @method validateColumnWidths
8162
* @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
8164
validateColumnWidths : function(oColumn) {
8165
var elColgroup = this._elColgroup;
8166
var elColgroupClone = elColgroup.cloneNode(true);
8167
var bNeedsValidation = false;
8168
var allKeys = this._oColumnSet.keys;
8170
// Validate just one Column's minWidth and/or maxAutoWidth
8171
if(oColumn && !oColumn.hidden && !oColumn.width && (oColumn.getKeyIndex() !== null)) {
8172
elThLiner = oColumn.getThLinerEl();
8173
if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
8174
elColgroupClone.childNodes[oColumn.getKeyIndex()].style.width =
8176
(parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
8177
(parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
8178
bNeedsValidation = true;
8180
else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
8181
this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
8184
// Validate all Columns
8186
for(var i=0, len=allKeys.length; i<len; i++) {
8187
oColumn = allKeys[i];
8188
if(!oColumn.hidden && !oColumn.width) {
8189
elThLiner = oColumn.getThLinerEl();
8190
if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
8191
elColgroupClone.childNodes[i].style.width =
8193
(parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
8194
(parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
8195
bNeedsValidation = true;
8197
else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
8198
this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
8203
if(bNeedsValidation) {
8204
elColgroup.parentNode.replaceChild(elColgroupClone, elColgroup);
8205
this._elColgroup = elColgroupClone;
8212
* @method _clearMinWidth
8213
* @param oColumn {YAHOO.widget.Column} Which Column.
8216
_clearMinWidth : function(oColumn) {
8217
if(oColumn.getKeyIndex() !== null) {
8218
this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = '';
8223
* Restores minWidth.
8225
* @method _restoreMinWidth
8226
* @param oColumn {YAHOO.widget.Column} Which Column.
8229
_restoreMinWidth : function(oColumn) {
8230
if(oColumn.minWidth && (oColumn.getKeyIndex() !== null)) {
8231
this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = oColumn.minWidth + 'px';
8236
* Hides given Column. NOTE: You cannot hide/show nested Columns. You can only
8237
* hide/show non-nested Columns, and top-level parent Columns (which will
8238
* hide/show all children Columns).
8240
* @method hideColumn
8241
* @param oColumn {YAHOO.widget.Column} Column instance.
8243
hideColumn : function(oColumn) {
8244
if(!(oColumn instanceof YAHOO.widget.Column)) {
8245
oColumn = this.getColumn(oColumn);
8247
// Only top-level Columns can get hidden due to issues in FF2 and SF3
8248
if(oColumn && !oColumn.hidden && oColumn.getTreeIndex() !== null) {
8250
var allrows = this.getTbodyEl().rows;
8251
var l = allrows.length;
8252
var allDescendants = this._oColumnSet.getDescendants(oColumn);
8254
// Hide each nested Column
8255
for(var i=0; i<allDescendants.length; i++) {
8256
var thisColumn = allDescendants[i];
8257
thisColumn.hidden = true;
8259
// Style the head cell
8260
Dom.addClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
8262
// Does this Column have body cells?
8263
var thisKeyIndex = thisColumn.getKeyIndex();
8264
if(thisKeyIndex !== null) {
8266
this._clearMinWidth(oColumn);
8268
// Style the body cells
8269
for(var j=0;j<l;j++) {
8270
Dom.addClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
8274
this.fireEvent("columnHideEvent",{column:thisColumn});
8275
YAHOO.log("Column \"" + oColumn.key + "\" hidden", "info", this.toString());
8278
this._repaintOpera();
8279
this._clearTrTemplateEl();
8282
YAHOO.log("Could not hide Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be hidden", "warn", this.toString());
8287
* Shows given Column. NOTE: You cannot hide/show nested Columns. You can only
8288
* hide/show non-nested Columns, and top-level parent Columns (which will
8289
* hide/show all children Columns).
8291
* @method showColumn
8292
* @param oColumn {YAHOO.widget.Column} Column instance.
8294
showColumn : function(oColumn) {
8295
if(!(oColumn instanceof YAHOO.widget.Column)) {
8296
oColumn = this.getColumn(oColumn);
8298
// Only top-level Columns can get hidden
8299
if(oColumn && oColumn.hidden && (oColumn.getTreeIndex() !== null)) {
8300
var allrows = this.getTbodyEl().rows;
8301
var l = allrows.length;
8302
var allDescendants = this._oColumnSet.getDescendants(oColumn);
8304
// Show each nested Column
8305
for(var i=0; i<allDescendants.length; i++) {
8306
var thisColumn = allDescendants[i];
8307
thisColumn.hidden = false;
8309
// Unstyle the head cell
8310
Dom.removeClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
8312
// Does this Column have body cells?
8313
var thisKeyIndex = thisColumn.getKeyIndex();
8314
if(thisKeyIndex !== null) {
8316
this._restoreMinWidth(oColumn);
8319
// Unstyle the body cells
8320
for(var j=0;j<l;j++) {
8321
Dom.removeClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
8325
this.fireEvent("columnShowEvent",{column:thisColumn});
8326
YAHOO.log("Column \"" + oColumn.key + "\" shown", "info", this.toString());
8328
this._clearTrTemplateEl();
8331
YAHOO.log("Could not show Column \"" + lang.dump(oColumn) + "\". Only non-nested Columns can be shown", "warn", this.toString());
8336
* Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
8337
* non-nested Columns, and top-level parent Columns (which will remove all
8338
* children Columns).
8340
* @method removeColumn
8341
* @param oColumn {YAHOO.widget.Column} Column instance.
8342
* @return oColumn {YAHOO.widget.Column} Removed Column instance.
8344
removeColumn : function(oColumn) {
8346
if(!(oColumn instanceof YAHOO.widget.Column)) {
8347
oColumn = this.getColumn(oColumn);
8350
var nColTreeIndex = oColumn.getTreeIndex();
8351
if(nColTreeIndex !== null) {
8352
// Which key index(es)
8354
aKeyIndexes = oColumn.getKeyIndex();
8355
// Must be a parent Column
8356
if(aKeyIndexes === null) {
8357
var descKeyIndexes = [];
8358
var allDescendants = this._oColumnSet.getDescendants(oColumn);
8359
for(i=0, len=allDescendants.length; i<len; i++) {
8360
// Is this descendant a key Column?
8361
var thisKey = allDescendants[i].getKeyIndex();
8362
if(thisKey !== null) {
8363
descKeyIndexes[descKeyIndexes.length] = thisKey;
8366
if(descKeyIndexes.length > 0) {
8367
aKeyIndexes = descKeyIndexes;
8370
// Must be a key Column
8372
aKeyIndexes = [aKeyIndexes];
8375
if(aKeyIndexes !== null) {
8376
// Sort the indexes so we can remove from the right
8377
aKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
8379
// Destroy previous THEAD
8380
this._destroyTheadEl();
8383
var aOrigColumnDefs = this._oColumnSet.getDefinitions();
8384
oColumn = aOrigColumnDefs.splice(nColTreeIndex,1)[0];
8385
this._initColumnSet(aOrigColumnDefs);
8386
this._initTheadEl();
8389
for(i=aKeyIndexes.length-1; i>-1; i--) {
8390
this._removeColgroupColEl(aKeyIndexes[i]);
8394
var allRows = this._elTbody.rows;
8395
if(allRows.length > 0) {
8396
var loopN = this.get("renderLoopSize"),
8397
loopEnd = allRows.length;
8398
this._oChainRender.add({
8399
method: function(oArg) {
8400
if((this instanceof DT) && this._sId) {
8401
var i = oArg.nCurrentRow,
8402
len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
8403
aIndexes = oArg.aIndexes,
8405
for(; i < len; ++i) {
8406
for(j = aIndexes.length-1; j>-1; j--) {
8407
allRows[i].removeChild(allRows[i].childNodes[aIndexes[j]]);
8410
oArg.nCurrentRow = i;
8413
iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
8414
argument: {nCurrentRow:0, aIndexes:aKeyIndexes},
8416
timeout: (loopN > 0) ? 0 : -1
8418
this._runRenderChain();
8421
this.fireEvent("columnRemoveEvent",{column:oColumn});
8422
YAHOO.log("Column \"" + oColumn.key + "\" removed", "info", this.toString());
8427
YAHOO.log("Could not remove Column \"" + oColumn.key + "\". Only non-nested Columns can be removed", "warn", this.toString());
8431
* Inserts given Column at the index if given, otherwise at the end. NOTE: You
8432
* can only add non-nested Columns and top-level parent Columns. You cannot add
8433
* a nested Column to an existing parent.
8435
* @method insertColumn
8436
* @param oColumn {Object | YAHOO.widget.Column} Object literal Column
8437
* definition or a Column instance.
8438
* @param index {Number} (optional) New tree index.
8439
* @return oColumn {YAHOO.widget.Column} Inserted Column instance.
8441
insertColumn : function(oColumn, index) {
8443
if(oColumn instanceof YAHOO.widget.Column) {
8444
oColumn = oColumn.getDefinition();
8446
else if(oColumn.constructor !== Object) {
8447
YAHOO.log("Could not insert Column \"" + oColumn + "\" due to invalid argument", "warn", this.toString());
8451
// Validate index or append new Column to the end of the ColumnSet
8452
var oColumnSet = this._oColumnSet;
8453
if(!lang.isValue(index) || !lang.isNumber(index)) {
8454
index = oColumnSet.tree[0].length;
8457
// Destroy previous THEAD
8458
this._destroyTheadEl();
8461
var aNewColumnDefs = this._oColumnSet.getDefinitions();
8462
aNewColumnDefs.splice(index, 0, oColumn);
8463
this._initColumnSet(aNewColumnDefs);
8464
this._initTheadEl();
8466
// Need to refresh the reference
8467
oColumnSet = this._oColumnSet;
8468
var oNewColumn = oColumnSet.tree[0][index];
8470
// Get key index(es) for new Column
8472
descKeyIndexes = [];
8473
var allDescendants = oColumnSet.getDescendants(oNewColumn);
8474
for(i=0, len=allDescendants.length; i<len; i++) {
8475
// Is this descendant a key Column?
8476
var thisKey = allDescendants[i].getKeyIndex();
8477
if(thisKey !== null) {
8478
descKeyIndexes[descKeyIndexes.length] = thisKey;
8482
if(descKeyIndexes.length > 0) {
8484
var newIndex = descKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
8487
for(i=descKeyIndexes.length-1; i>-1; i--) {
8488
this._insertColgroupColEl(descKeyIndexes[i]);
8492
var allRows = this._elTbody.rows;
8493
if(allRows.length > 0) {
8494
var loopN = this.get("renderLoopSize"),
8495
loopEnd = allRows.length;
8497
// Get templates for each new TD
8498
var aTdTemplates = [],
8500
for(i=0, len=descKeyIndexes.length; i<len; i++) {
8501
var thisKeyIndex = descKeyIndexes[i];
8502
elTdTemplate = this._getTrTemplateEl().childNodes[i].cloneNode(true);
8503
elTdTemplate = this._formatTdEl(this._oColumnSet.keys[thisKeyIndex], elTdTemplate, thisKeyIndex, (thisKeyIndex===this._oColumnSet.keys.length-1));
8504
aTdTemplates[thisKeyIndex] = elTdTemplate;
8507
this._oChainRender.add({
8508
method: function(oArg) {
8509
if((this instanceof DT) && this._sId) {
8510
var i = oArg.nCurrentRow, j,
8511
descKeyIndexes = oArg.descKeyIndexes,
8512
len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
8514
for(; i < len; ++i) {
8515
nextSibling = allRows[i].childNodes[newIndex] || null;
8516
for(j=descKeyIndexes.length-1; j>-1; j--) {
8517
allRows[i].insertBefore(oArg.aTdTemplates[descKeyIndexes[j]].cloneNode(true), nextSibling);
8520
oArg.nCurrentRow = i;
8523
iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
8524
argument: {nCurrentRow:0,aTdTemplates:aTdTemplates,descKeyIndexes:descKeyIndexes},
8526
timeout: (loopN > 0) ? 0 : -1
8528
this._runRenderChain();
8531
this.fireEvent("columnInsertEvent",{column:oColumn,index:index});
8532
YAHOO.log("Column \"" + oColumn.key + "\" inserted into index " + index, "info", this.toString());
8538
* Removes given Column and inserts into given tree index. NOTE: You
8539
* can only reorder non-nested Columns and top-level parent Columns. You cannot
8540
* reorder a nested Column to an existing parent.
8542
* @method reorderColumn
8543
* @param oColumn {YAHOO.widget.Column} Column instance.
8544
* @param index {Number} New tree index.
8545
* @return oColumn {YAHOO.widget.Column} Reordered Column instance.
8547
reorderColumn : function(oColumn, index) {
8548
// Validate Column and new index
8549
if(!(oColumn instanceof YAHOO.widget.Column)) {
8550
oColumn = this.getColumn(oColumn);
8552
if(oColumn && YAHOO.lang.isNumber(index)) {
8553
var nOrigTreeIndex = oColumn.getTreeIndex();
8554
if((nOrigTreeIndex !== null) && (nOrigTreeIndex !== index)) {
8555
// Which key index(es)
8557
aOrigKeyIndexes = oColumn.getKeyIndex(),
8559
descKeyIndexes = [],
8561
// Must be a parent Column...
8562
if(aOrigKeyIndexes === null) {
8563
allDescendants = this._oColumnSet.getDescendants(oColumn);
8564
for(i=0, len=allDescendants.length; i<len; i++) {
8565
// Is this descendant a key Column?
8566
thisKey = allDescendants[i].getKeyIndex();
8567
if(thisKey !== null) {
8568
descKeyIndexes[descKeyIndexes.length] = thisKey;
8571
if(descKeyIndexes.length > 0) {
8572
aOrigKeyIndexes = descKeyIndexes;
8575
// ...or else must be a key Column
8577
aOrigKeyIndexes = [aOrigKeyIndexes];
8580
if(aOrigKeyIndexes !== null) {
8582
aOrigKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
8584
// Destroy previous THEAD
8585
this._destroyTheadEl();
8588
var aColumnDefs = this._oColumnSet.getDefinitions();
8589
var oColumnDef = aColumnDefs.splice(nOrigTreeIndex,1)[0];
8590
aColumnDefs.splice(index, 0, oColumnDef);
8591
this._initColumnSet(aColumnDefs);
8592
this._initTheadEl();
8594
// Need to refresh the reference
8595
var oNewColumn = this._oColumnSet.tree[0][index];
8597
// What are new key index(es)
8598
var aNewKeyIndexes = oNewColumn.getKeyIndex();
8599
// Must be a parent Column
8600
if(aNewKeyIndexes === null) {
8601
descKeyIndexes = [];
8602
allDescendants = this._oColumnSet.getDescendants(oNewColumn);
8603
for(i=0, len=allDescendants.length; i<len; i++) {
8604
// Is this descendant a key Column?
8605
thisKey = allDescendants[i].getKeyIndex();
8606
if(thisKey !== null) {
8607
descKeyIndexes[descKeyIndexes.length] = thisKey;
8610
if(descKeyIndexes.length > 0) {
8611
aNewKeyIndexes = descKeyIndexes;
8614
// Must be a key Column
8616
aNewKeyIndexes = [aNewKeyIndexes];
8619
// Sort the new indexes and grab the first one for the new location
8620
var newIndex = aNewKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
8623
this._reorderColgroupColEl(aOrigKeyIndexes, newIndex);
8626
var allRows = this._elTbody.rows;
8627
if(allRows.length > 0) {
8628
var loopN = this.get("renderLoopSize"),
8629
loopEnd = allRows.length;
8630
this._oChainRender.add({
8631
method: function(oArg) {
8632
if((this instanceof DT) && this._sId) {
8633
var i = oArg.nCurrentRow, j, tmpTds, nextSibling,
8634
len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
8635
aIndexes = oArg.aIndexes, thisTr;
8637
for(; i < len; ++i) {
8639
thisTr = allRows[i];
8642
for(j=aIndexes.length-1; j>-1; j--) {
8643
tmpTds.push(thisTr.removeChild(thisTr.childNodes[aIndexes[j]]));
8647
nextSibling = thisTr.childNodes[newIndex] || null;
8648
for(j=tmpTds.length-1; j>-1; j--) {
8649
thisTr.insertBefore(tmpTds[j], nextSibling);
8652
oArg.nCurrentRow = i;
8655
iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
8656
argument: {nCurrentRow:0, aIndexes:aOrigKeyIndexes},
8658
timeout: (loopN > 0) ? 0 : -1
8660
this._runRenderChain();
8663
this.fireEvent("columnReorderEvent",{column:oNewColumn});
8664
YAHOO.log("Column \"" + oNewColumn.key + "\" reordered", "info", this.toString());
8669
YAHOO.log("Could not reorder Column \"" + oColumn.key + "\". Only non-nested Columns can be reordered", "warn", this.toString());
8673
* Selects given Column. NOTE: You cannot select/unselect nested Columns. You can only
8674
* select/unselect non-nested Columns, and bottom-level key Columns.
8676
* @method selectColumn
8677
* @param column {HTMLElement | String | Number} DOM reference or ID string to a
8678
* TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8680
selectColumn : function(oColumn) {
8681
oColumn = this.getColumn(oColumn);
8682
if(oColumn && !oColumn.selected) {
8683
// Only bottom-level Columns can get hidden
8684
if(oColumn.getKeyIndex() !== null) {
8685
oColumn.selected = true;
8688
var elTh = oColumn.getThEl();
8689
Dom.addClass(elTh,DT.CLASS_SELECTED);
8691
// Update body cells
8692
var allRows = this.getTbodyEl().rows;
8693
var oChainRender = this._oChainRender;
8695
method: function(oArg) {
8696
if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8697
Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
8702
iterations: allRows.length,
8703
argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
8706
this._clearTrTemplateEl();
8708
this._elTbody.style.display = "none";
8709
this._runRenderChain();
8710
this._elTbody.style.display = "";
8712
this.fireEvent("columnSelectEvent",{column:oColumn});
8713
YAHOO.log("Column \"" + oColumn.key + "\" selected", "info", this.toString());
8716
YAHOO.log("Could not select Column \"" + oColumn.key + "\". Only non-nested Columns can be selected", "warn", this.toString());
8722
* Unselects given Column. NOTE: You cannot select/unselect nested Columns. You can only
8723
* select/unselect non-nested Columns, and bottom-level key Columns.
8725
* @method unselectColumn
8726
* @param column {HTMLElement | String | Number} DOM reference or ID string to a
8727
* TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8729
unselectColumn : function(oColumn) {
8730
oColumn = this.getColumn(oColumn);
8731
if(oColumn && oColumn.selected) {
8732
// Only bottom-level Columns can get hidden
8733
if(oColumn.getKeyIndex() !== null) {
8734
oColumn.selected = false;
8737
var elTh = oColumn.getThEl();
8738
Dom.removeClass(elTh,DT.CLASS_SELECTED);
8740
// Update body cells
8741
var allRows = this.getTbodyEl().rows;
8742
var oChainRender = this._oChainRender;
8744
method: function(oArg) {
8745
if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8746
Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);
8751
iterations:allRows.length,
8752
argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
8755
this._clearTrTemplateEl();
8757
this._elTbody.style.display = "none";
8758
this._runRenderChain();
8759
this._elTbody.style.display = "";
8761
this.fireEvent("columnUnselectEvent",{column:oColumn});
8762
YAHOO.log("Column \"" + oColumn.key + "\" unselected", "info", this.toString());
8765
YAHOO.log("Could not unselect Column \"" + oColumn.key + "\". Only non-nested Columns can be unselected", "warn", this.toString());
8771
* Returns an array selected Column instances.
8773
* @method getSelectedColumns
8774
* @return {YAHOO.widget.Column[]} Array of Column instances.
8776
getSelectedColumns : function(oColumn) {
8777
var selectedColumns = [];
8778
var aKeys = this._oColumnSet.keys;
8779
for(var i=0,len=aKeys.length; i<len; i++) {
8780
if(aKeys[i].selected) {
8781
selectedColumns[selectedColumns.length] = aKeys[i];
8784
return selectedColumns;
8788
* Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
8789
* NOTE: You cannot highlight/unhighlight nested Columns. You can only
8790
* highlight/unhighlight non-nested Columns, and bottom-level key Columns.
8792
* @method highlightColumn
8793
* @param column {HTMLElement | String | Number} DOM reference or ID string to a
8794
* TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8796
highlightColumn : function(column) {
8797
var oColumn = this.getColumn(column);
8798
// Only bottom-level Columns can get highlighted
8799
if(oColumn && (oColumn.getKeyIndex() !== null)) {
8801
var elTh = oColumn.getThEl();
8802
Dom.addClass(elTh,DT.CLASS_HIGHLIGHTED);
8804
// Update body cells
8805
var allRows = this.getTbodyEl().rows;
8806
var oChainRender = this._oChainRender;
8808
method: function(oArg) {
8809
if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8810
Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
8815
iterations:allRows.length,
8816
argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
8819
this._elTbody.style.display = "none";
8820
this._runRenderChain();
8821
this._elTbody.style.display = "";
8823
this.fireEvent("columnHighlightEvent",{column:oColumn});
8824
YAHOO.log("Column \"" + oColumn.key + "\" highlighed", "info", this.toString());
8827
YAHOO.log("Could not highlight Column \"" + oColumn.key + "\". Only non-nested Columns can be highlighted", "warn", this.toString());
8832
* Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
8833
* NOTE: You cannot highlight/unhighlight nested Columns. You can only
8834
* highlight/unhighlight non-nested Columns, and bottom-level key Columns.
8836
* @method unhighlightColumn
8837
* @param column {HTMLElement | String | Number} DOM reference or ID string to a
8838
* TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
8840
unhighlightColumn : function(column) {
8841
var oColumn = this.getColumn(column);
8842
// Only bottom-level Columns can get highlighted
8843
if(oColumn && (oColumn.getKeyIndex() !== null)) {
8845
var elTh = oColumn.getThEl();
8846
Dom.removeClass(elTh,DT.CLASS_HIGHLIGHTED);
8848
// Update body cells
8849
var allRows = this.getTbodyEl().rows;
8850
var oChainRender = this._oChainRender;
8852
method: function(oArg) {
8853
if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
8854
Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
8859
iterations:allRows.length,
8860
argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
8863
this._elTbody.style.display = "none";
8864
this._runRenderChain();
8865
this._elTbody.style.display = "";
8867
this.fireEvent("columnUnhighlightEvent",{column:oColumn});
8868
YAHOO.log("Column \"" + oColumn.key + "\" unhighlighted", "info", this.toString());
8871
YAHOO.log("Could not unhighlight Column \"" + oColumn.key + "\". Only non-nested Columns can be unhighlighted", "warn", this.toString());
8921
* Adds one new Record of data into the RecordSet at the index if given,
8922
* otherwise at the end. If the new Record is in page view, the
8923
* corresponding DOM elements are also updated.
8926
* @param oData {Object} Object literal of data for the row.
8927
* @param index {Number} (optional) RecordSet position index at which to add data.
8929
addRow : function(oData, index) {
8930
if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
8931
YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
8935
if(oData && lang.isObject(oData)) {
8936
var oRecord = this._oRecordSet.addRecord(oData, index);
8939
var oPaginator = this.get('paginator');
8943
// Update the paginator's totalRecords
8944
var totalRecords = oPaginator.get('totalRecords');
8945
if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
8946
oPaginator.set('totalRecords',totalRecords + 1);
8949
recIndex = this.getRecordIndex(oRecord);
8950
var endRecIndex = (oPaginator.getPageRecords())[1];
8952
// New record affects the view
8953
if (recIndex <= endRecIndex) {
8954
// Defer UI updates to the render method
8958
this.fireEvent("rowAddEvent", {record:oRecord});
8959
YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString());
8964
recIndex = this.getTrIndex(oRecord);
8965
if(lang.isNumber(recIndex)) {
8966
// Add the TR element
8967
this._oChainRender.add({
8968
method: function(oArg) {
8969
if((this instanceof DT) && this._sId) {
8970
var oRecord = oArg.record;
8971
var recIndex = oArg.recIndex;
8972
var elNewTr = this._addTrEl(oRecord);
8974
var elNext = (this._elTbody.rows[recIndex]) ? this._elTbody.rows[recIndex] : null;
8975
this._elTbody.insertBefore(elNewTr, elNext);
8978
if(recIndex === 0) {
8979
this._setFirstRow();
8981
if(elNext === null) {
8985
this._setRowStripes();
8987
this.hideTableMessage();
8989
this.fireEvent("rowAddEvent", {record:oRecord});
8990
YAHOO.log("Added a row for Record " + YAHOO.lang.dump(oRecord) + " at RecordSet index " + recIndex, "info", this.toString());
8994
argument: {record: oRecord, recIndex: recIndex},
8996
timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
8998
this._runRenderChain();
9004
YAHOO.log("Could not add row at index " + index + " with " + lang.dump(oData), "warn", this.toString());
9008
* Convenience method to add multiple rows.
9011
* @param aData {Object[]} Array of object literal data for the rows.
9012
* @param index {Number} (optional) RecordSet position index at which to add data.
9014
addRows : function(aData, index) {
9015
if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
9016
YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());
9020
if(lang.isArray(aData)) {
9021
var aRecords = this._oRecordSet.addRecords(aData, index);
9023
var recIndex = this.getRecordIndex(aRecords[0]);
9026
var oPaginator = this.get('paginator');
9028
// Update the paginator's totalRecords
9029
var totalRecords = oPaginator.get('totalRecords');
9030
if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
9031
oPaginator.set('totalRecords',totalRecords + aRecords.length);
9034
var endRecIndex = (oPaginator.getPageRecords())[1];
9036
// At least one of the new records affects the view
9037
if (recIndex <= endRecIndex) {
9041
this.fireEvent("rowsAddEvent", {records:aRecords});
9042
YAHOO.log("Added " + aRecords.length +
9043
" rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
9044
" with data " + lang.dump(aData), "info", this.toString());
9049
// Add the TR elements
9050
var loopN = this.get("renderLoopSize");
9051
var loopEnd = recIndex + aData.length;
9052
var nRowsNeeded = (loopEnd - recIndex); // how many needed
9053
var isLast = (recIndex >= this._elTbody.rows.length);
9054
this._oChainRender.add({
9055
method: function(oArg) {
9056
if((this instanceof DT) && this._sId) {
9057
var aRecords = oArg.aRecords,
9058
i = oArg.nCurrentRow,
9059
j = oArg.nCurrentRecord,
9060
len = loopN > 0 ? Math.min(i + loopN,loopEnd) : loopEnd,
9061
df = document.createDocumentFragment(),
9062
elNext = (this._elTbody.rows[i]) ? this._elTbody.rows[i] : null;
9063
for(; i < len; i++, j++) {
9064
df.appendChild(this._addTrEl(aRecords[j]));
9066
this._elTbody.insertBefore(df, elNext);
9067
oArg.nCurrentRow = i;
9068
oArg.nCurrentRecord = j;
9071
iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
9072
argument: {nCurrentRow:recIndex,nCurrentRecord:0,aRecords:aRecords},
9074
timeout: (loopN > 0) ? 0 : -1
9076
this._oChainRender.add({
9077
method: function(oArg) {
9078
var recIndex = oArg.recIndex;
9080
if(recIndex === 0) {
9081
this._setFirstRow();
9087
this._setRowStripes();
9089
this.fireEvent("rowsAddEvent", {records:aRecords});
9090
YAHOO.log("Added " + aRecords.length +
9091
" rows at index " + this._oRecordSet.getRecordIndex(aRecords[0]) +
9092
" with data " + lang.dump(aData), "info", this.toString());
9094
argument: {recIndex: recIndex, isLast: isLast},
9096
timeout: -1 // Needs to run immediately after the DOM insertions above
9098
this._runRenderChain();
9099
this.hideTableMessage();
9104
YAHOO.log("Could not add rows at index " + index + " with " + lang.dump(aData), "warn", this.toString());
9108
* For the given row, updates the associated Record with the given data. If the
9109
* row is on current page, the corresponding DOM elements are also updated.
9112
* @param row {YAHOO.widget.Record | Number | HTMLElement | String}
9113
* Which row to update: By Record instance, by Record's RecordSet
9114
* position index, by HTMLElement reference to the TR element, or by ID string
9115
* of the TR element.
9116
* @param oData {Object} Object literal of data for the row.
9118
updateRow : function(row, oData) {
9120
if (!lang.isNumber(index)) {
9121
index = this.getRecordIndex(row);
9124
// Update the Record
9125
if(lang.isNumber(index) && (index >= 0)) {
9126
var oRecordSet = this._oRecordSet,
9127
oldRecord = oRecordSet.getRecord(index);
9131
var updatedRecord = this._oRecordSet.setRecord(oData, index),
9132
elRow = this.getTrEl(oldRecord),
9133
// Copy data from the Record for the event that gets fired later
9134
oldData = oldRecord ? oldRecord.getData() : null;
9137
// Update selected rows as necessary
9138
var tracker = this._aSelections || [],
9140
oldId = oldRecord.getId(),
9141
newId = updatedRecord.getId();
9142
for(; i<tracker.length; i++) {
9143
if((tracker[i] === oldId)) {
9146
else if(tracker[i].recordId === oldId) {
9147
tracker[i].recordId = newId;
9151
// Update the TR only if row is on current page
9152
this._oChainRender.add({
9153
method: function() {
9154
if((this instanceof DT) && this._sId) {
9156
var oPaginator = this.get('paginator');
9158
var pageStartIndex = (oPaginator.getPageRecords())[0],
9159
pageLastIndex = (oPaginator.getPageRecords())[1];
9161
// At least one of the new records affects the view
9162
if ((index >= pageStartIndex) || (index <= pageLastIndex)) {
9168
this._updateTrEl(elRow, updatedRecord);
9171
this.getTbodyEl().appendChild(this._addTrEl(updatedRecord));
9174
this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData});
9175
YAHOO.log("DataTable row updated: Record ID = " + updatedRecord.getId() +
9176
", Record index = " + this.getRecordIndex(updatedRecord) +
9177
", page row index = " + this.getTrIndex(updatedRecord), "info", this.toString());
9181
timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9183
this._runRenderChain();
9188
YAHOO.log("Could not update row " + row + " with the data : " + lang.dump(oData), "warn", this.toString());
9193
* Starting with the given row, updates associated Records with the given data.
9194
* The number of rows to update are determined by the array of data provided.
9195
* Undefined data (i.e., not an object literal) causes a row to be skipped. If
9196
* any of the rows are on current page, the corresponding DOM elements are also
9199
* @method updateRows
9200
* @param startrow {YAHOO.widget.Record | Number | HTMLElement | String}
9201
* Starting row to update: By Record instance, by Record's RecordSet
9202
* position index, by HTMLElement reference to the TR element, or by ID string
9203
* of the TR element.
9204
* @param aData {Object[]} Array of object literal of data for the rows.
9206
updateRows : function(startrow, aData) {
9207
if(lang.isArray(aData)) {
9208
var startIndex = startrow,
9209
oRecordSet = this._oRecordSet;
9211
if (!lang.isNumber(startrow)) {
9212
startIndex = this.getRecordIndex(startrow);
9215
if(lang.isNumber(startIndex) && (startIndex >= 0) && (startIndex < oRecordSet.getLength())) {
9216
var lastIndex = startIndex + aData.length,
9217
aOldRecords = oRecordSet.getRecords(startIndex, aData.length),
9218
aNewRecords = oRecordSet.setRecords(aData, startIndex);
9220
// Update selected rows as necessary
9221
var tracker = this._aSelections || [],
9222
i=0, j, newId, oldId;
9223
for(; i<tracker.length; i++) {
9224
for(j=0; j<aOldRecords.length; j++) {
9225
oldId = aOldRecords[j].getId();
9226
if((tracker[i] === oldId)) {
9227
tracker[i] = aNewRecords[j].getId();
9229
else if(tracker[i].recordId === oldId) {
9230
tracker[i].recordId = aNewRecords[j].getId();
9236
var oPaginator = this.get('paginator');
9238
var pageStartIndex = (oPaginator.getPageRecords())[0],
9239
pageLastIndex = (oPaginator.getPageRecords())[1];
9241
// At least one of the new records affects the view
9242
if ((startIndex >= pageStartIndex) || (lastIndex <= pageLastIndex)) {
9246
this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
9247
YAHOO.log("Added " + aNewRecords.length +
9248
" rows starting at index " + startIndex +
9249
" with data " + lang.dump(aData), "info", this.toString());
9254
// Update the TR elements
9255
var loopN = this.get("renderLoopSize"),
9256
rowCount = aData.length, // how many needed
9257
lastRowIndex = this._elTbody.rows.length,
9258
isLast = (lastIndex >= lastRowIndex),
9259
isAdding = (lastIndex > lastRowIndex);
9261
this._oChainRender.add({
9262
method: function(oArg) {
9263
if((this instanceof DT) && this._sId) {
9264
var aRecords = oArg.aRecords,
9265
i = oArg.nCurrentRow,
9266
j = oArg.nDataPointer,
9267
len = loopN > 0 ? Math.min(i+loopN, startIndex+aRecords.length) : startIndex+aRecords.length;
9269
for(; i < len; i++,j++) {
9270
if(isAdding && (i>=lastRowIndex)) {
9271
this._elTbody.appendChild(this._addTrEl(aRecords[j]));
9274
this._updateTrEl(this._elTbody.rows[i], aRecords[j]);
9277
oArg.nCurrentRow = i;
9278
oArg.nDataPointer = j;
9281
iterations: (loopN > 0) ? Math.ceil(rowCount/loopN) : 1,
9282
argument: {nCurrentRow:startIndex,aRecords:aNewRecords,nDataPointer:0,isAdding:isAdding},
9284
timeout: (loopN > 0) ? 0 : -1
9286
this._oChainRender.add({
9287
method: function(oArg) {
9288
var recIndex = oArg.recIndex;
9290
if(recIndex === 0) {
9291
this._setFirstRow();
9297
this._setRowStripes();
9299
this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
9300
YAHOO.log("Added " + aNewRecords.length +
9301
" rows starting at index " + startIndex +
9302
" with data " + lang.dump(aData), "info", this.toString());
9304
argument: {recIndex: startIndex, isLast: isLast},
9306
timeout: -1 // Needs to run immediately after the DOM insertions above
9308
this._runRenderChain();
9309
this.hideTableMessage();
9315
YAHOO.log("Could not update rows at " + startrow + " with " + lang.dump(aData), "warn", this.toString());
9319
* Deletes the given row's Record from the RecordSet. If the row is on current page,
9320
* the corresponding DOM elements are also deleted.
9323
* @param row {HTMLElement | String | Number} DOM element reference or ID string
9324
* to DataTable page element or RecordSet index.
9326
deleteRow : function(row) {
9327
var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
9328
if(lang.isNumber(nRecordIndex)) {
9329
var oRecord = this.getRecord(nRecordIndex);
9331
var nTrIndex = this.getTrIndex(nRecordIndex);
9333
// Remove from selection tracker if there
9334
var sRecordId = oRecord.getId();
9335
var tracker = this._aSelections || [];
9336
for(var j=tracker.length-1; j>-1; j--) {
9337
if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
9338
(lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
9339
tracker.splice(j,1);
9343
// Delete Record from RecordSet
9344
var oData = this._oRecordSet.deleteRecord(nRecordIndex);
9348
// If paginated and the deleted row was on this or a prior page, just
9350
var oPaginator = this.get('paginator');
9352
// Update the paginator's totalRecords
9353
var totalRecords = oPaginator.get('totalRecords'),
9354
// must capture before the totalRecords change because
9355
// Paginator shifts to previous page automatically
9356
rng = oPaginator.getPageRecords();
9358
if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
9359
oPaginator.set('totalRecords',totalRecords - 1);
9362
// The deleted record was on this or a prior page, re-render
9363
if (!rng || nRecordIndex <= rng[1]) {
9367
this._oChainRender.add({
9368
method: function() {
9369
if((this instanceof DT) && this._sId) {
9370
this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex, oldData:oData, trElIndex:nTrIndex});
9371
YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());
9375
timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9377
this._runRenderChain();
9381
if(lang.isNumber(nTrIndex)) {
9382
this._oChainRender.add({
9383
method: function() {
9384
if((this instanceof DT) && this._sId) {
9385
var isLast = (nTrIndex == this.getLastTrEl().sectionRowIndex);
9386
this._deleteTrEl(nTrIndex);
9388
// Post-delete tasks
9389
if(this._elTbody.rows.length > 0) {
9391
if(nTrIndex === 0) {
9392
this._setFirstRow();
9398
if(nTrIndex != this._elTbody.rows.length) {
9399
this._setRowStripes(nTrIndex);
9403
this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,oldData:oData, trElIndex:nTrIndex});
9404
YAHOO.log("Deleted row with data " + YAHOO.lang.dump(oData) + " at RecordSet index " + nRecordIndex + " and page row index " + nTrIndex, "info", this.toString());
9408
timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9410
this._runRenderChain();
9417
YAHOO.log("Could not delete row: " + row, "warn", this.toString());
9422
* Convenience method to delete multiple rows.
9424
* @method deleteRows
9425
* @param row {HTMLElement | String | Number} DOM element reference or ID string
9426
* to DataTable page element or RecordSet index.
9427
* @param count {Number} (optional) How many rows to delete. A negative value
9428
* will delete towards the beginning.
9430
deleteRows : function(row, count) {
9431
var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
9432
if(lang.isNumber(nRecordIndex)) {
9433
var oRecord = this.getRecord(nRecordIndex);
9435
var nTrIndex = this.getTrIndex(nRecordIndex);
9437
// Remove from selection tracker if there
9438
var sRecordId = oRecord.getId();
9439
var tracker = this._aSelections || [];
9440
for(var j=tracker.length-1; j>-1; j--) {
9441
if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
9442
(lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
9443
tracker.splice(j,1);
9447
// Delete Record from RecordSet
9448
var highIndex = nRecordIndex;
9449
var lowIndex = nRecordIndex;
9451
// Validate count and account for negative value
9452
if(count && lang.isNumber(count)) {
9453
highIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex;
9454
lowIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1;
9455
count = (count > 0) ? count : count*-1;
9458
count = highIndex - lowIndex + 1;
9465
var aData = this._oRecordSet.deleteRecords(lowIndex, count);
9469
var oPaginator = this.get('paginator'),
9470
loopN = this.get("renderLoopSize");
9471
// If paginated and the deleted row was on this or a prior page, just
9474
// Update the paginator's totalRecords
9475
var totalRecords = oPaginator.get('totalRecords'),
9476
// must capture before the totalRecords change because
9477
// Paginator shifts to previous page automatically
9478
rng = oPaginator.getPageRecords();
9480
if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
9481
oPaginator.set('totalRecords',totalRecords - aData.length);
9484
// The records were on this or a prior page, re-render
9485
if (!rng || lowIndex <= rng[1]) {
9489
this._oChainRender.add({
9490
method: function(oArg) {
9491
if((this instanceof DT) && this._sId) {
9492
this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
9493
YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
9497
timeout: (loopN > 0) ? 0 : -1
9499
this._runRenderChain();
9504
if(lang.isNumber(nTrIndex)) {
9505
// Delete the TR elements starting with highest index
9506
var loopEnd = lowIndex;
9507
var nRowsNeeded = count; // how many needed
9508
this._oChainRender.add({
9509
method: function(oArg) {
9510
if((this instanceof DT) && this._sId) {
9511
var i = oArg.nCurrentRow,
9512
len = (loopN > 0) ? (Math.max(i - loopN,loopEnd)-1) : loopEnd-1;
9514
this._deleteTrEl(i);
9516
oArg.nCurrentRow = i;
9519
iterations: (loopN > 0) ? Math.ceil(count/loopN) : 1,
9520
argument: {nCurrentRow:highIndex},
9522
timeout: (loopN > 0) ? 0 : -1
9524
this._oChainRender.add({
9525
method: function() {
9526
// Post-delete tasks
9527
if(this._elTbody.rows.length > 0) {
9528
this._setFirstRow();
9530
this._setRowStripes();
9533
this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
9534
YAHOO.log("DataTable " + count + " rows deleted starting at index " + lowIndex, "info", this.toString());
9537
timeout: -1 // Needs to run immediately after the DOM deletions above
9539
this._runRenderChain();
9546
YAHOO.log("Could not delete " + count + " rows at row " + row, "warn", this.toString());
9598
* Outputs markup into the given TD based on given Record.
9600
* @method formatCell
9601
* @param elCell {HTMLElement} The liner DIV element within the TD.
9602
* @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
9603
* @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
9605
formatCell : function(elCell, oRecord, oColumn) {
9607
oRecord = this.getRecord(elCell);
9610
oColumn = this.getColumn(elCell.parentNode.cellIndex);
9613
if(oRecord && oColumn) {
9614
var sField = oColumn.field;
9615
var oData = oRecord.getData(sField);
9617
var fnFormatter = typeof oColumn.formatter === 'function' ?
9619
DT.Formatter[oColumn.formatter+''] ||
9620
DT.Formatter.defaultFormatter;
9622
// Apply special formatter
9624
fnFormatter.call(this, elCell, oRecord, oColumn, oData);
9627
elCell.innerHTML = oData;
9630
this.fireEvent("cellFormatEvent", {record:oRecord, column:oColumn, key:oColumn.key, el:elCell});
9633
YAHOO.log("Could not format cell " + elCell, "error", this.toString());
9638
* For the given row and column, updates the Record with the given data. If the
9639
* cell is on current page, the corresponding DOM elements are also updated.
9641
* @method updateCell
9642
* @param oRecord {YAHOO.widget.Record} Record instance.
9643
* @param oColumn {YAHOO.widget.Column | String | Number} A Column key, or a ColumnSet key index.
9644
* @param oData {Object} New data value for the cell.
9646
updateCell : function(oRecord, oColumn, oData) {
9647
// Validate Column and Record
9648
oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn);
9649
if(oColumn && oColumn.getKey() && (oRecord instanceof YAHOO.widget.Record)) {
9650
var sKey = oColumn.getKey(),
9652
// Copy data from the Record for the event that gets fired later
9653
//var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData());
9654
oldData = oRecord.getData(sKey);
9656
// Update Record with new data
9657
this._oRecordSet.updateRecordValue(oRecord, sKey, oData);
9659
// Update the TD only if row is on current page
9660
var elTd = this.getTdEl({record: oRecord, column: oColumn});
9662
this._oChainRender.add({
9663
method: function() {
9664
if((this instanceof DT) && this._sId) {
9665
this.formatCell(elTd.firstChild);
9666
this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
9667
YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
9668
", Record index = " + this.getRecordIndex(oRecord) +
9669
", page row index = " + this.getTrIndex(oRecord) +
9670
", Column key = " + oColumn.getKey(), "info", this.toString());
9674
timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
9676
this._runRenderChain();
9679
this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
9680
YAHOO.log("DataTable cell updated: Record ID = " + oRecord.getId() +
9681
", Record index = " + this.getRecordIndex(oRecord) +
9682
", page row index = " + this.getTrIndex(oRecord) +
9683
", Column key = " + oColumn.getKey(), "info", this.toString());
9740
* Method executed during set() operation for the "paginator" attribute.
9741
* Adds and/or severs event listeners between DataTable and Paginator
9743
* @method _updatePaginator
9744
* @param newPag {Paginator} Paginator instance (or null) for DataTable to use
9747
_updatePaginator : function (newPag) {
9748
var oldPag = this.get('paginator');
9749
if (oldPag && newPag !== oldPag) {
9750
oldPag.unsubscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
9753
newPag.subscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
9758
* Update the UI infrastructure in response to a "paginator" attribute change.
9760
* @method _handlePaginatorChange
9761
* @param e {Object} Change event object containing keys 'type','newValue',
9765
_handlePaginatorChange : function (e) {
9766
if (e.prevValue === e.newValue) { return; }
9768
var newPag = e.newValue,
9769
oldPag = e.prevValue,
9770
containers = this._defaultPaginatorContainers();
9773
if (oldPag.getContainerNodes()[0] == containers[0]) {
9774
oldPag.set('containers',[]);
9778
// Convenience: share the default containers if possible.
9779
// Otherwise, remove the default containers from the DOM.
9780
if (containers[0]) {
9781
if (newPag && !newPag.getContainerNodes().length) {
9782
newPag.set('containers',containers);
9784
// No new Paginator to use existing containers, OR new
9785
// Paginator has configured containers.
9786
for (var i = containers.length - 1; i >= 0; --i) {
9787
if (containers[i]) {
9788
containers[i].parentNode.removeChild(containers[i]);
9801
this.renderPaginator();
9807
* Returns the default containers used for Paginators. If create param is
9808
* passed, the containers will be created and added to the DataTable container.
9810
* @method _defaultPaginatorContainers
9811
* @param create {boolean} Create the default containers if not found
9814
_defaultPaginatorContainers : function (create) {
9815
var above_id = this._sId + '-paginator0',
9816
below_id = this._sId + '-paginator1',
9817
above = Dom.get(above_id),
9818
below = Dom.get(below_id);
9820
if (create && (!above || !below)) {
9821
// One above and one below the table
9823
above = document.createElement('div');
9824
above.id = above_id;
9825
Dom.addClass(above, DT.CLASS_PAGINATOR);
9827
this._elContainer.insertBefore(above,this._elContainer.firstChild);
9831
below = document.createElement('div');
9832
below.id = below_id;
9833
Dom.addClass(below, DT.CLASS_PAGINATOR);
9835
this._elContainer.appendChild(below);
9839
return [above,below];
9843
* Renders the Paginator to the DataTable UI
9845
* @method renderPaginator
9847
renderPaginator : function () {
9848
var pag = this.get("paginator");
9849
if (!pag) { return; }
9851
// Add the containers if the Paginator is not configured with containers
9852
if (!pag.getContainerNodes().length) {
9853
pag.set('containers',this._defaultPaginatorContainers(true));
9860
* Overridable method gives implementers a hook to show loading message before
9861
* changing Paginator value.
9863
* @method doBeforePaginatorChange
9864
* @param oPaginatorState {Object} An object literal describing the proposed pagination state.
9865
* @return {Boolean} Return true to continue changing Paginator value.
9867
doBeforePaginatorChange : function(oPaginatorState) {
9868
this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
9873
* Responds to new Pagination states. By default, updates the UI to reflect the
9874
* new state. If "dynamicData" is true, current selections are purged before
9875
* a request is sent to the DataSource for data for the new state (using the
9876
* request returned by "generateRequest()").
9878
* @method onPaginatorChangeRequest
9879
* @param oPaginatorState {Object} An object literal describing the proposed pagination state.
9881
onPaginatorChangeRequest : function (oPaginatorState) {
9882
var ok = this.doBeforePaginatorChange(oPaginatorState);
9884
// Server-side pagination
9885
if(this.get("dynamicData")) {
9886
// Get the current state
9887
var oState = this.getState();
9889
// Update pagination values
9890
oState.pagination = oPaginatorState;
9892
// Get the request for the new state
9893
var request = this.get("generateRequest")(oState, this);
9896
this.unselectAllRows();
9897
this.unselectAllCells();
9899
// Get the new data from the server
9901
success : this.onDataReturnSetRows,
9902
failure : this.onDataReturnSetRows,
9903
argument : oState, // Pass along the new state to the callback
9906
this._oDataSource.sendRequest(request, callback);
9908
// Client-side pagination
9910
// Set the core pagination values silently (the second param)
9911
// to avoid looping back through the changeRequest mechanism
9912
oPaginatorState.paginator.setStartIndex(oPaginatorState.recordOffset,true);
9913
oPaginatorState.paginator.setRowsPerPage(oPaginatorState.rowsPerPage,true);
9920
YAHOO.log("Could not change Paginator value \"" + oPaginatorState + "\"", "warn", this.toString());
9973
// SELECTION/HIGHLIGHTING
9976
* Reference to last highlighted cell element
9978
* @property _elLastHighlightedTd
9982
_elLastHighlightedTd : null,
9985
* ID string of last highlighted row element
9987
* @property _sLastHighlightedTrElId
9991
//_sLastHighlightedTrElId : null,
9994
* Array to track row selections (by sRecordId) and/or cell selections
9995
* (by {recordId:sRecordId, columnKey:sColumnKey})
9997
* @property _aSelections
10001
_aSelections : null,
10004
* Record instance of the row selection anchor.
10006
* @property _oAnchorRecord
10007
* @type YAHOO.widget.Record
10010
_oAnchorRecord : null,
10013
* Object literal representing cell selection anchor:
10014
* {recordId:sRecordId, columnKey:sColumnKey}.
10016
* @property _oAnchorCell
10020
_oAnchorCell : null,
10023
* Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
10024
* from all TR elements on the page.
10026
* @method _unselectAllTrEls
10029
_unselectAllTrEls : function() {
10030
var selectedRows = Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
10031
Dom.removeClass(selectedRows, DT.CLASS_SELECTED);
10035
* Returns object literal of values that represent the selection trigger. Used
10036
* to determine selection behavior resulting from a key event.
10038
* @method _getSelectionTrigger
10041
_getSelectionTrigger : function() {
10042
var sMode = this.get("selectionMode");
10044
var oTriggerCell, oTriggerRecord, nTriggerRecordIndex, elTriggerRow, nTriggerTrIndex;
10047
if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
10048
oTriggerCell = this.getLastSelectedCell();
10049
// No selected cells found
10050
if(!oTriggerCell) {
10054
oTriggerRecord = this.getRecord(oTriggerCell.recordId);
10055
nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
10056
elTriggerRow = this.getTrEl(oTriggerRecord);
10057
nTriggerTrIndex = this.getTrIndex(elTriggerRow);
10059
// Selected cell not found on this page
10060
if(nTriggerTrIndex === null) {
10064
oTrigger.record = oTriggerRecord;
10065
oTrigger.recordIndex = nTriggerRecordIndex;
10066
oTrigger.el = this.getTdEl(oTriggerCell);
10067
oTrigger.trIndex = nTriggerTrIndex;
10068
oTrigger.column = this.getColumn(oTriggerCell.columnKey);
10069
oTrigger.colKeyIndex = oTrigger.column.getKeyIndex();
10070
oTrigger.cell = oTriggerCell;
10077
oTriggerRecord = this.getLastSelectedRecord();
10078
// No selected rows found
10079
if(!oTriggerRecord) {
10083
// Selected row found, but is it on current page?
10084
oTriggerRecord = this.getRecord(oTriggerRecord);
10085
nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
10086
elTriggerRow = this.getTrEl(oTriggerRecord);
10087
nTriggerTrIndex = this.getTrIndex(elTriggerRow);
10089
// Selected row not found on this page
10090
if(nTriggerTrIndex === null) {
10094
oTrigger.record = oTriggerRecord;
10095
oTrigger.recordIndex = nTriggerRecordIndex;
10096
oTrigger.el = elTriggerRow;
10097
oTrigger.trIndex = nTriggerTrIndex;
10105
* Returns object literal of values that represent the selection anchor. Used
10106
* to determine selection behavior resulting from a user event.
10108
* @method _getSelectionAnchor
10109
* @param oTrigger {Object} (Optional) Object literal of selection trigger values
10110
* (for key events).
10113
_getSelectionAnchor : function(oTrigger) {
10114
var sMode = this.get("selectionMode");
10116
var oAnchorRecord, nAnchorRecordIndex, nAnchorTrIndex;
10119
if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
10120
// Validate anchor cell
10121
var oAnchorCell = this._oAnchorCell;
10124
oAnchorCell = this._oAnchorCell = oTrigger.cell;
10130
oAnchorRecord = this._oAnchorCell.record;
10131
nAnchorRecordIndex = this._oRecordSet.getRecordIndex(oAnchorRecord);
10132
nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
10133
// If anchor cell is not on this page...
10134
if(nAnchorTrIndex === null) {
10135
// ...set TR index equal to top TR
10136
if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
10137
nAnchorTrIndex = 0;
10139
// ...set TR index equal to bottom TR
10141
nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
10145
oAnchor.record = oAnchorRecord;
10146
oAnchor.recordIndex = nAnchorRecordIndex;
10147
oAnchor.trIndex = nAnchorTrIndex;
10148
oAnchor.column = this._oAnchorCell.column;
10149
oAnchor.colKeyIndex = oAnchor.column.getKeyIndex();
10150
oAnchor.cell = oAnchorCell;
10155
oAnchorRecord = this._oAnchorRecord;
10156
if(!oAnchorRecord) {
10158
oAnchorRecord = this._oAnchorRecord = oTrigger.record;
10165
nAnchorRecordIndex = this.getRecordIndex(oAnchorRecord);
10166
nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
10167
// If anchor row is not on this page...
10168
if(nAnchorTrIndex === null) {
10169
// ...set TR index equal to top TR
10170
if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
10171
nAnchorTrIndex = 0;
10173
// ...set TR index equal to bottom TR
10175
nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
10179
oAnchor.record = oAnchorRecord;
10180
oAnchor.recordIndex = nAnchorRecordIndex;
10181
oAnchor.trIndex = nAnchorTrIndex;
10187
* Determines selection behavior resulting from a mouse event when selection mode
10188
* is set to "standard".
10190
* @method _handleStandardSelectionByMouse
10191
* @param oArgs.event {HTMLEvent} Event object.
10192
* @param oArgs.target {HTMLElement} Target element.
10195
_handleStandardSelectionByMouse : function(oArgs) {
10196
var elTarget = oArgs.target;
10198
// Validate target row
10199
var elTargetRow = this.getTrEl(elTarget);
10201
var e = oArgs.event;
10202
var bSHIFT = e.shiftKey;
10203
var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
10205
var oTargetRecord = this.getRecord(elTargetRow);
10206
var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
10208
var oAnchor = this._getSelectionAnchor();
10212
// Both SHIFT and CTRL
10213
if(bSHIFT && bCTRL) {
10216
if(this.isSelected(oAnchor.record)) {
10217
// Select all rows between anchor row and target row, including target row
10218
if(oAnchor.recordIndex < nTargetRecordIndex) {
10219
for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex; i++) {
10220
if(!this.isSelected(i)) {
10225
// Select all rows between target row and anchor row, including target row
10227
for(i=oAnchor.recordIndex-1; i>=nTargetRecordIndex; i--) {
10228
if(!this.isSelected(i)) {
10235
// Unselect all rows between anchor row and target row
10236
if(oAnchor.recordIndex < nTargetRecordIndex) {
10237
for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex-1; i++) {
10238
if(this.isSelected(i)) {
10239
this.unselectRow(i);
10243
// Unselect all rows between target row and anchor row
10245
for(i=nTargetRecordIndex+1; i<=oAnchor.recordIndex-1; i++) {
10246
if(this.isSelected(i)) {
10247
this.unselectRow(i);
10251
// Select the target row
10252
this.selectRow(oTargetRecord);
10258
this._oAnchorRecord = oTargetRecord;
10260
// Toggle selection of target
10261
if(this.isSelected(oTargetRecord)) {
10262
this.unselectRow(oTargetRecord);
10265
this.selectRow(oTargetRecord);
10271
this.unselectAllRows();
10275
// Select all rows between anchor row and target row,
10276
// including the anchor row and target row
10277
if(oAnchor.recordIndex < nTargetRecordIndex) {
10278
for(i=oAnchor.recordIndex; i<=nTargetRecordIndex; i++) {
10282
// Select all rows between target row and anchor row,
10283
// including the target row and anchor row
10285
for(i=oAnchor.recordIndex; i>=nTargetRecordIndex; i--) {
10293
this._oAnchorRecord = oTargetRecord;
10295
// Select target row only
10296
this.selectRow(oTargetRecord);
10302
this._oAnchorRecord = oTargetRecord;
10304
// Toggle selection of target
10305
if(this.isSelected(oTargetRecord)) {
10306
this.unselectRow(oTargetRecord);
10309
this.selectRow(oTargetRecord);
10312
// Neither SHIFT nor CTRL
10314
this._handleSingleSelectionByMouse(oArgs);
10321
* Determines selection behavior resulting from a key event when selection mode
10322
* is set to "standard".
10324
* @method _handleStandardSelectionByKey
10325
* @param e {HTMLEvent} Event object.
10328
_handleStandardSelectionByKey : function(e) {
10329
var nKey = Ev.getCharCode(e);
10331
if((nKey == 38) || (nKey == 40)) {
10332
var bSHIFT = e.shiftKey;
10334
// Validate trigger
10335
var oTrigger = this._getSelectionTrigger();
10336
// Arrow selection only works if last selected row is on current page
10344
var oAnchor = this._getSelectionAnchor(oTrigger);
10346
// Determine which direction we're going to
10348
// Selecting down away from anchor row
10349
if((nKey == 40) && (oAnchor.recordIndex <= oTrigger.trIndex)) {
10350
this.selectRow(this.getNextTrEl(oTrigger.el));
10352
// Selecting up away from anchor row
10353
else if((nKey == 38) && (oAnchor.recordIndex >= oTrigger.trIndex)) {
10354
this.selectRow(this.getPreviousTrEl(oTrigger.el));
10356
// Unselect trigger
10358
this.unselectRow(oTrigger.el);
10362
this._handleSingleSelectionByKey(e);
10368
* Determines selection behavior resulting from a mouse event when selection mode
10369
* is set to "single".
10371
* @method _handleSingleSelectionByMouse
10372
* @param oArgs.event {HTMLEvent} Event object.
10373
* @param oArgs.target {HTMLElement} Target element.
10376
_handleSingleSelectionByMouse : function(oArgs) {
10377
var elTarget = oArgs.target;
10379
// Validate target row
10380
var elTargetRow = this.getTrEl(elTarget);
10382
var oTargetRecord = this.getRecord(elTargetRow);
10385
this._oAnchorRecord = oTargetRecord;
10387
// Select only target
10388
this.unselectAllRows();
10389
this.selectRow(oTargetRecord);
10394
* Determines selection behavior resulting from a key event when selection mode
10395
* is set to "single".
10397
* @method _handleSingleSelectionByKey
10398
* @param e {HTMLEvent} Event object.
10401
_handleSingleSelectionByKey : function(e) {
10402
var nKey = Ev.getCharCode(e);
10404
if((nKey == 38) || (nKey == 40)) {
10405
// Validate trigger
10406
var oTrigger = this._getSelectionTrigger();
10407
// Arrow selection only works if last selected row is on current page
10414
// Determine the new row to select
10416
if(nKey == 38) { // arrow up
10417
elNew = this.getPreviousTrEl(oTrigger.el);
10419
// Validate new row
10420
if(elNew === null) {
10421
//TODO: wrap around to last tr on current page
10422
//elNew = this.getLastTrEl();
10424
//TODO: wrap back to last tr of previous page
10426
// Top row selection is sticky
10427
elNew = this.getFirstTrEl();
10430
else if(nKey == 40) { // arrow down
10431
elNew = this.getNextTrEl(oTrigger.el);
10433
// Validate new row
10434
if(elNew === null) {
10435
//TODO: wrap around to first tr on current page
10436
//elNew = this.getFirstTrEl();
10438
//TODO: wrap forward to first tr of previous page
10440
// Bottom row selection is sticky
10441
elNew = this.getLastTrEl();
10445
// Unselect all rows
10446
this.unselectAllRows();
10448
// Select the new row
10449
this.selectRow(elNew);
10452
this._oAnchorRecord = this.getRecord(elNew);
10457
* Determines selection behavior resulting from a mouse event when selection mode
10458
* is set to "cellblock".
10460
* @method _handleCellBlockSelectionByMouse
10461
* @param oArgs.event {HTMLEvent} Event object.
10462
* @param oArgs.target {HTMLElement} Target element.
10465
_handleCellBlockSelectionByMouse : function(oArgs) {
10466
var elTarget = oArgs.target;
10468
// Validate target cell
10469
var elTargetCell = this.getTdEl(elTarget);
10471
var e = oArgs.event;
10472
var bSHIFT = e.shiftKey;
10473
var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
10475
var elTargetRow = this.getTrEl(elTargetCell);
10476
var nTargetTrIndex = this.getTrIndex(elTargetRow);
10477
var oTargetColumn = this.getColumn(elTargetCell);
10478
var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
10479
var oTargetRecord = this.getRecord(elTargetRow);
10480
var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
10481
var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
10483
var oAnchor = this._getSelectionAnchor();
10485
var allRows = this.getTbodyEl().rows;
10486
var startIndex, endIndex, currentRow, i, j;
10488
// Both SHIFT and CTRL
10489
if(bSHIFT && bCTRL) {
10493
// Anchor is selected
10494
if(this.isSelected(oAnchor.cell)) {
10495
// All cells are on the same row
10496
if(oAnchor.recordIndex === nTargetRecordIndex) {
10497
// Select all cells between anchor cell and target cell, including target cell
10498
if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10499
for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
10500
this.selectCell(elTargetRow.cells[i]);
10503
// Select all cells between target cell and anchor cell, including target cell
10504
else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10505
for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
10506
this.selectCell(elTargetRow.cells[i]);
10510
// Anchor row is above target row
10511
else if(oAnchor.recordIndex < nTargetRecordIndex) {
10512
startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
10513
endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
10515
// Select all cells from startIndex to endIndex on rows between anchor row and target row
10516
for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10517
for(j=startIndex; j<=endIndex; j++) {
10518
this.selectCell(allRows[i].cells[j]);
10522
// Anchor row is below target row
10524
startIndex = Math.min(oAnchor.trIndex, nTargetColKeyIndex);
10525
endIndex = Math.max(oAnchor.trIndex, nTargetColKeyIndex);
10527
// Select all cells from startIndex to endIndex on rows between target row and anchor row
10528
for(i=oAnchor.trIndex; i>=nTargetTrIndex; i--) {
10529
for(j=endIndex; j>=startIndex; j--) {
10530
this.selectCell(allRows[i].cells[j]);
10535
// Anchor cell is unselected
10537
// All cells are on the same row
10538
if(oAnchor.recordIndex === nTargetRecordIndex) {
10539
// Unselect all cells between anchor cell and target cell
10540
if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10541
for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
10542
this.unselectCell(elTargetRow.cells[i]);
10545
// Select all cells between target cell and anchor cell
10546
else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10547
for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
10548
this.unselectCell(elTargetRow.cells[i]);
10552
// Anchor row is above target row
10553
if(oAnchor.recordIndex < nTargetRecordIndex) {
10554
// Unselect all cells from anchor cell to target cell
10555
for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10556
currentRow = allRows[i];
10557
for(j=0; j<currentRow.cells.length; j++) {
10558
// This is the anchor row, only unselect cells after the anchor cell
10559
if(currentRow.sectionRowIndex === oAnchor.trIndex) {
10560
if(j>oAnchor.colKeyIndex) {
10561
this.unselectCell(currentRow.cells[j]);
10564
// This is the target row, only unelect cells before the target cell
10565
else if(currentRow.sectionRowIndex === nTargetTrIndex) {
10566
if(j<nTargetColKeyIndex) {
10567
this.unselectCell(currentRow.cells[j]);
10570
// Unselect all cells on this row
10572
this.unselectCell(currentRow.cells[j]);
10577
// Anchor row is below target row
10579
// Unselect all cells from target cell to anchor cell
10580
for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
10581
currentRow = allRows[i];
10582
for(j=0; j<currentRow.cells.length; j++) {
10583
// This is the target row, only unselect cells after the target cell
10584
if(currentRow.sectionRowIndex == nTargetTrIndex) {
10585
if(j>nTargetColKeyIndex) {
10586
this.unselectCell(currentRow.cells[j]);
10589
// This is the anchor row, only unselect cells before the anchor cell
10590
else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
10591
if(j<oAnchor.colKeyIndex) {
10592
this.unselectCell(currentRow.cells[j]);
10595
// Unselect all cells on this row
10597
this.unselectCell(currentRow.cells[j]);
10603
// Select the target cell
10604
this.selectCell(elTargetCell);
10610
this._oAnchorCell = oTargetCell;
10612
// Toggle selection of target
10613
if(this.isSelected(oTargetCell)) {
10614
this.unselectCell(oTargetCell);
10617
this.selectCell(oTargetCell);
10624
this.unselectAllCells();
10628
// All cells are on the same row
10629
if(oAnchor.recordIndex === nTargetRecordIndex) {
10630
// Select all cells between anchor cell and target cell,
10631
// including the anchor cell and target cell
10632
if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10633
for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
10634
this.selectCell(elTargetRow.cells[i]);
10637
// Select all cells between target cell and anchor cell
10638
// including the target cell and anchor cell
10639
else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10640
for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
10641
this.selectCell(elTargetRow.cells[i]);
10645
// Anchor row is above target row
10646
else if(oAnchor.recordIndex < nTargetRecordIndex) {
10647
// Select the cellblock from anchor cell to target cell
10648
// including the anchor cell and the target cell
10649
startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
10650
endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
10652
for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10653
for(j=startIndex; j<=endIndex; j++) {
10654
this.selectCell(allRows[i].cells[j]);
10658
// Anchor row is below target row
10660
// Select the cellblock from target cell to anchor cell
10661
// including the target cell and the anchor cell
10662
startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
10663
endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
10665
for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
10666
for(j=startIndex; j<=endIndex; j++) {
10667
this.selectCell(allRows[i].cells[j]);
10675
this._oAnchorCell = oTargetCell;
10677
// Select target only
10678
this.selectCell(oTargetCell);
10685
this._oAnchorCell = oTargetCell;
10687
// Toggle selection of target
10688
if(this.isSelected(oTargetCell)) {
10689
this.unselectCell(oTargetCell);
10692
this.selectCell(oTargetCell);
10696
// Neither SHIFT nor CTRL
10698
this._handleSingleCellSelectionByMouse(oArgs);
10704
* Determines selection behavior resulting from a key event when selection mode
10705
* is set to "cellblock".
10707
* @method _handleCellBlockSelectionByKey
10708
* @param e {HTMLEvent} Event object.
10711
_handleCellBlockSelectionByKey : function(e) {
10712
var nKey = Ev.getCharCode(e);
10713
var bSHIFT = e.shiftKey;
10714
if((nKey == 9) || !bSHIFT) {
10715
this._handleSingleCellSelectionByKey(e);
10719
if((nKey > 36) && (nKey < 41)) {
10720
// Validate trigger
10721
var oTrigger = this._getSelectionTrigger();
10722
// Arrow selection only works if last selected row is on current page
10730
var oAnchor = this._getSelectionAnchor(oTrigger);
10732
var i, startIndex, endIndex, elNew, elNewRow;
10733
var allRows = this.getTbodyEl().rows;
10734
var elThisRow = oTrigger.el.parentNode;
10736
// Determine which direction we're going to
10738
if(nKey == 40) { // arrow down
10739
// Selecting away from anchor cell
10740
if(oAnchor.recordIndex <= oTrigger.recordIndex) {
10741
// Select the horiz block on the next row...
10742
// ...making sure there is room below the trigger row
10743
elNewRow = this.getNextTrEl(oTrigger.el);
10745
startIndex = oAnchor.colKeyIndex;
10746
endIndex = oTrigger.colKeyIndex;
10748
if(startIndex > endIndex) {
10749
for(i=startIndex; i>=endIndex; i--) {
10750
elNew = elNewRow.cells[i];
10751
this.selectCell(elNew);
10756
for(i=startIndex; i<=endIndex; i++) {
10757
elNew = elNewRow.cells[i];
10758
this.selectCell(elNew);
10763
// Unselecting towards anchor cell
10765
startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10766
endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10767
// Unselect the horiz block on this row towards the next row
10768
for(i=startIndex; i<=endIndex; i++) {
10769
this.unselectCell(elThisRow.cells[i]);
10774
else if(nKey == 38) {
10775
// Selecting away from anchor cell
10776
if(oAnchor.recordIndex >= oTrigger.recordIndex) {
10777
// Select the horiz block on the previous row...
10778
// ...making sure there is room
10779
elNewRow = this.getPreviousTrEl(oTrigger.el);
10781
// Select in order from anchor to trigger...
10782
startIndex = oAnchor.colKeyIndex;
10783
endIndex = oTrigger.colKeyIndex;
10785
if(startIndex > endIndex) {
10786
for(i=startIndex; i>=endIndex; i--) {
10787
elNew = elNewRow.cells[i];
10788
this.selectCell(elNew);
10793
for(i=startIndex; i<=endIndex; i++) {
10794
elNew = elNewRow.cells[i];
10795
this.selectCell(elNew);
10800
// Unselecting towards anchor cell
10802
startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10803
endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
10804
// Unselect the horiz block on this row towards the previous row
10805
for(i=startIndex; i<=endIndex; i++) {
10806
this.unselectCell(elThisRow.cells[i]);
10811
else if(nKey == 39) {
10812
// Selecting away from anchor cell
10813
if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
10814
// Select the next vert block to the right...
10815
// ...making sure there is room
10816
if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
10817
// Select in order from anchor to trigger...
10818
startIndex = oAnchor.trIndex;
10819
endIndex = oTrigger.trIndex;
10821
if(startIndex > endIndex) {
10822
for(i=startIndex; i>=endIndex; i--) {
10823
elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
10824
this.selectCell(elNew);
10829
for(i=startIndex; i<=endIndex; i++) {
10830
elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
10831
this.selectCell(elNew);
10836
// Unselecting towards anchor cell
10838
// Unselect the vert block on this column towards the right
10839
startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
10840
endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
10841
for(i=startIndex; i<=endIndex; i++) {
10842
this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
10847
else if(nKey == 37) {
10848
// Selecting away from anchor cell
10849
if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
10850
//Select the previous vert block to the left
10851
if(oTrigger.colKeyIndex > 0) {
10852
// Select in order from anchor to trigger...
10853
startIndex = oAnchor.trIndex;
10854
endIndex = oTrigger.trIndex;
10856
if(startIndex > endIndex) {
10857
for(i=startIndex; i>=endIndex; i--) {
10858
elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
10859
this.selectCell(elNew);
10864
for(i=startIndex; i<=endIndex; i++) {
10865
elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
10866
this.selectCell(elNew);
10871
// Unselecting towards anchor cell
10873
// Unselect the vert block on this column towards the left
10874
startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
10875
endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
10876
for(i=startIndex; i<=endIndex; i++) {
10877
this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
10885
* Determines selection behavior resulting from a mouse event when selection mode
10886
* is set to "cellrange".
10888
* @method _handleCellRangeSelectionByMouse
10889
* @param oArgs.event {HTMLEvent} Event object.
10890
* @param oArgs.target {HTMLElement} Target element.
10893
_handleCellRangeSelectionByMouse : function(oArgs) {
10894
var elTarget = oArgs.target;
10896
// Validate target cell
10897
var elTargetCell = this.getTdEl(elTarget);
10899
var e = oArgs.event;
10900
var bSHIFT = e.shiftKey;
10901
var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
10903
var elTargetRow = this.getTrEl(elTargetCell);
10904
var nTargetTrIndex = this.getTrIndex(elTargetRow);
10905
var oTargetColumn = this.getColumn(elTargetCell);
10906
var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
10907
var oTargetRecord = this.getRecord(elTargetRow);
10908
var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
10909
var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
10911
var oAnchor = this._getSelectionAnchor();
10913
var allRows = this.getTbodyEl().rows;
10914
var currentRow, i, j;
10916
// Both SHIFT and CTRL
10917
if(bSHIFT && bCTRL) {
10921
// Anchor is selected
10922
if(this.isSelected(oAnchor.cell)) {
10923
// All cells are on the same row
10924
if(oAnchor.recordIndex === nTargetRecordIndex) {
10925
// Select all cells between anchor cell and target cell, including target cell
10926
if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10927
for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
10928
this.selectCell(elTargetRow.cells[i]);
10931
// Select all cells between target cell and anchor cell, including target cell
10932
else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10933
for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
10934
this.selectCell(elTargetRow.cells[i]);
10938
// Anchor row is above target row
10939
else if(oAnchor.recordIndex < nTargetRecordIndex) {
10940
// Select all cells on anchor row from anchor cell to the end of the row
10941
for(i=oAnchor.colKeyIndex+1; i<elTargetRow.cells.length; i++) {
10942
this.selectCell(elTargetRow.cells[i]);
10945
// Select all cells on all rows between anchor row and target row
10946
for(i=oAnchor.trIndex+1; i<nTargetTrIndex; i++) {
10947
for(j=0; j<allRows[i].cells.length; j++){
10948
this.selectCell(allRows[i].cells[j]);
10952
// Select all cells on target row from first cell to the target cell
10953
for(i=0; i<=nTargetColKeyIndex; i++) {
10954
this.selectCell(elTargetRow.cells[i]);
10957
// Anchor row is below target row
10959
// Select all cells on target row from target cell to the end of the row
10960
for(i=nTargetColKeyIndex; i<elTargetRow.cells.length; i++) {
10961
this.selectCell(elTargetRow.cells[i]);
10964
// Select all cells on all rows between target row and anchor row
10965
for(i=nTargetTrIndex+1; i<oAnchor.trIndex; i++) {
10966
for(j=0; j<allRows[i].cells.length; j++){
10967
this.selectCell(allRows[i].cells[j]);
10971
// Select all cells on anchor row from first cell to the anchor cell
10972
for(i=0; i<oAnchor.colKeyIndex; i++) {
10973
this.selectCell(elTargetRow.cells[i]);
10977
// Anchor cell is unselected
10979
// All cells are on the same row
10980
if(oAnchor.recordIndex === nTargetRecordIndex) {
10981
// Unselect all cells between anchor cell and target cell
10982
if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
10983
for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
10984
this.unselectCell(elTargetRow.cells[i]);
10987
// Select all cells between target cell and anchor cell
10988
else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
10989
for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
10990
this.unselectCell(elTargetRow.cells[i]);
10994
// Anchor row is above target row
10995
if(oAnchor.recordIndex < nTargetRecordIndex) {
10996
// Unselect all cells from anchor cell to target cell
10997
for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
10998
currentRow = allRows[i];
10999
for(j=0; j<currentRow.cells.length; j++) {
11000
// This is the anchor row, only unselect cells after the anchor cell
11001
if(currentRow.sectionRowIndex === oAnchor.trIndex) {
11002
if(j>oAnchor.colKeyIndex) {
11003
this.unselectCell(currentRow.cells[j]);
11006
// This is the target row, only unelect cells before the target cell
11007
else if(currentRow.sectionRowIndex === nTargetTrIndex) {
11008
if(j<nTargetColKeyIndex) {
11009
this.unselectCell(currentRow.cells[j]);
11012
// Unselect all cells on this row
11014
this.unselectCell(currentRow.cells[j]);
11019
// Anchor row is below target row
11021
// Unselect all cells from target cell to anchor cell
11022
for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
11023
currentRow = allRows[i];
11024
for(j=0; j<currentRow.cells.length; j++) {
11025
// This is the target row, only unselect cells after the target cell
11026
if(currentRow.sectionRowIndex == nTargetTrIndex) {
11027
if(j>nTargetColKeyIndex) {
11028
this.unselectCell(currentRow.cells[j]);
11031
// This is the anchor row, only unselect cells before the anchor cell
11032
else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
11033
if(j<oAnchor.colKeyIndex) {
11034
this.unselectCell(currentRow.cells[j]);
11037
// Unselect all cells on this row
11039
this.unselectCell(currentRow.cells[j]);
11045
// Select the target cell
11046
this.selectCell(elTargetCell);
11052
this._oAnchorCell = oTargetCell;
11054
// Toggle selection of target
11055
if(this.isSelected(oTargetCell)) {
11056
this.unselectCell(oTargetCell);
11059
this.selectCell(oTargetCell);
11066
this.unselectAllCells();
11070
// All cells are on the same row
11071
if(oAnchor.recordIndex === nTargetRecordIndex) {
11072
// Select all cells between anchor cell and target cell,
11073
// including the anchor cell and target cell
11074
if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
11075
for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
11076
this.selectCell(elTargetRow.cells[i]);
11079
// Select all cells between target cell and anchor cell
11080
// including the target cell and anchor cell
11081
else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
11082
for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
11083
this.selectCell(elTargetRow.cells[i]);
11087
// Anchor row is above target row
11088
else if(oAnchor.recordIndex < nTargetRecordIndex) {
11089
// Select all cells from anchor cell to target cell
11090
// including the anchor cell and target cell
11091
for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
11092
currentRow = allRows[i];
11093
for(j=0; j<currentRow.cells.length; j++) {
11094
// This is the anchor row, only select the anchor cell and after
11095
if(currentRow.sectionRowIndex == oAnchor.trIndex) {
11096
if(j>=oAnchor.colKeyIndex) {
11097
this.selectCell(currentRow.cells[j]);
11100
// This is the target row, only select the target cell and before
11101
else if(currentRow.sectionRowIndex == nTargetTrIndex) {
11102
if(j<=nTargetColKeyIndex) {
11103
this.selectCell(currentRow.cells[j]);
11106
// Select all cells on this row
11108
this.selectCell(currentRow.cells[j]);
11113
// Anchor row is below target row
11115
// Select all cells from target cell to anchor cell,
11116
// including the target cell and anchor cell
11117
for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
11118
currentRow = allRows[i];
11119
for(j=0; j<currentRow.cells.length; j++) {
11120
// This is the target row, only select the target cell and after
11121
if(currentRow.sectionRowIndex == nTargetTrIndex) {
11122
if(j>=nTargetColKeyIndex) {
11123
this.selectCell(currentRow.cells[j]);
11126
// This is the anchor row, only select the anchor cell and before
11127
else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
11128
if(j<=oAnchor.colKeyIndex) {
11129
this.selectCell(currentRow.cells[j]);
11132
// Select all cells on this row
11134
this.selectCell(currentRow.cells[j]);
11143
this._oAnchorCell = oTargetCell;
11145
// Select target only
11146
this.selectCell(oTargetCell);
11155
this._oAnchorCell = oTargetCell;
11157
// Toggle selection of target
11158
if(this.isSelected(oTargetCell)) {
11159
this.unselectCell(oTargetCell);
11162
this.selectCell(oTargetCell);
11166
// Neither SHIFT nor CTRL
11168
this._handleSingleCellSelectionByMouse(oArgs);
11174
* Determines selection behavior resulting from a key event when selection mode
11175
* is set to "cellrange".
11177
* @method _handleCellRangeSelectionByKey
11178
* @param e {HTMLEvent} Event object.
11181
_handleCellRangeSelectionByKey : function(e) {
11182
var nKey = Ev.getCharCode(e);
11183
var bSHIFT = e.shiftKey;
11184
if((nKey == 9) || !bSHIFT) {
11185
this._handleSingleCellSelectionByKey(e);
11189
if((nKey > 36) && (nKey < 41)) {
11190
// Validate trigger
11191
var oTrigger = this._getSelectionTrigger();
11192
// Arrow selection only works if last selected row is on current page
11200
var oAnchor = this._getSelectionAnchor(oTrigger);
11202
var i, elNewRow, elNew;
11203
var allRows = this.getTbodyEl().rows;
11204
var elThisRow = oTrigger.el.parentNode;
11208
elNewRow = this.getNextTrEl(oTrigger.el);
11210
// Selecting away from anchor cell
11211
if(oAnchor.recordIndex <= oTrigger.recordIndex) {
11212
// Select all cells to the end of this row
11213
for(i=oTrigger.colKeyIndex+1; i<elThisRow.cells.length; i++){
11214
elNew = elThisRow.cells[i];
11215
this.selectCell(elNew);
11218
// Select some of the cells on the next row down
11220
for(i=0; i<=oTrigger.colKeyIndex; i++){
11221
elNew = elNewRow.cells[i];
11222
this.selectCell(elNew);
11226
// Unselecting towards anchor cell
11228
// Unselect all cells to the end of this row
11229
for(i=oTrigger.colKeyIndex; i<elThisRow.cells.length; i++){
11230
this.unselectCell(elThisRow.cells[i]);
11233
// Unselect some of the cells on the next row down
11235
for(i=0; i<oTrigger.colKeyIndex; i++){
11236
this.unselectCell(elNewRow.cells[i]);
11242
else if(nKey == 38) {
11243
elNewRow = this.getPreviousTrEl(oTrigger.el);
11245
// Selecting away from anchor cell
11246
if(oAnchor.recordIndex >= oTrigger.recordIndex) {
11247
// Select all the cells to the beginning of this row
11248
for(i=oTrigger.colKeyIndex-1; i>-1; i--){
11249
elNew = elThisRow.cells[i];
11250
this.selectCell(elNew);
11253
// Select some of the cells from the end of the previous row
11255
for(i=elThisRow.cells.length-1; i>=oTrigger.colKeyIndex; i--){
11256
elNew = elNewRow.cells[i];
11257
this.selectCell(elNew);
11261
// Unselecting towards anchor cell
11263
// Unselect all the cells to the beginning of this row
11264
for(i=oTrigger.colKeyIndex; i>-1; i--){
11265
this.unselectCell(elThisRow.cells[i]);
11268
// Unselect some of the cells from the end of the previous row
11270
for(i=elThisRow.cells.length-1; i>oTrigger.colKeyIndex; i--){
11271
this.unselectCell(elNewRow.cells[i]);
11277
else if(nKey == 39) {
11278
elNewRow = this.getNextTrEl(oTrigger.el);
11280
// Selecting away from anchor cell
11281
if(oAnchor.recordIndex < oTrigger.recordIndex) {
11282
// Select the next cell to the right
11283
if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
11284
elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
11285
this.selectCell(elNew);
11287
// Select the first cell of the next row
11288
else if(elNewRow) {
11289
elNew = elNewRow.cells[0];
11290
this.selectCell(elNew);
11293
// Unselecting towards anchor cell
11294
else if(oAnchor.recordIndex > oTrigger.recordIndex) {
11295
this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11297
// Unselect this cell towards the right
11298
if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
11300
// Unselect this cells towards the first cell of the next row
11304
// Anchor is on this row
11306
// Selecting away from anchor
11307
if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
11308
// Select the next cell to the right
11309
if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
11310
elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
11311
this.selectCell(elNew);
11313
// Select the first cell on the next row
11314
else if(oTrigger.trIndex < allRows.length-1){
11315
elNew = elNewRow.cells[0];
11316
this.selectCell(elNew);
11319
// Unselecting towards anchor
11321
// Unselect this cell towards the right
11322
this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11327
else if(nKey == 37) {
11328
elNewRow = this.getPreviousTrEl(oTrigger.el);
11330
// Unselecting towards the anchor
11331
if(oAnchor.recordIndex < oTrigger.recordIndex) {
11332
this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11334
// Unselect this cell towards the left
11335
if(oTrigger.colKeyIndex > 0) {
11337
// Unselect this cell towards the last cell of the previous row
11341
// Selecting towards the anchor
11342
else if(oAnchor.recordIndex > oTrigger.recordIndex) {
11343
// Select the next cell to the left
11344
if(oTrigger.colKeyIndex > 0) {
11345
elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
11346
this.selectCell(elNew);
11348
// Select the last cell of the previous row
11349
else if(oTrigger.trIndex > 0){
11350
elNew = elNewRow.cells[elNewRow.cells.length-1];
11351
this.selectCell(elNew);
11354
// Anchor is on this row
11356
// Selecting away from anchor cell
11357
if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
11358
// Select the next cell to the left
11359
if(oTrigger.colKeyIndex > 0) {
11360
elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
11361
this.selectCell(elNew);
11363
// Select the last cell of the previous row
11364
else if(oTrigger.trIndex > 0){
11365
elNew = elNewRow.cells[elNewRow.cells.length-1];
11366
this.selectCell(elNew);
11369
// Unselecting towards anchor cell
11371
this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
11373
// Unselect this cell towards the left
11374
if(oTrigger.colKeyIndex > 0) {
11376
// Unselect this cell towards the last cell of the previous row
11386
* Determines selection behavior resulting from a mouse event when selection mode
11387
* is set to "singlecell".
11389
* @method _handleSingleCellSelectionByMouse
11390
* @param oArgs.event {HTMLEvent} Event object.
11391
* @param oArgs.target {HTMLElement} Target element.
11394
_handleSingleCellSelectionByMouse : function(oArgs) {
11395
var elTarget = oArgs.target;
11397
// Validate target cell
11398
var elTargetCell = this.getTdEl(elTarget);
11400
var elTargetRow = this.getTrEl(elTargetCell);
11401
var oTargetRecord = this.getRecord(elTargetRow);
11402
var oTargetColumn = this.getColumn(elTargetCell);
11403
var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
11406
this._oAnchorCell = oTargetCell;
11408
// Select only target
11409
this.unselectAllCells();
11410
this.selectCell(oTargetCell);
11415
* Determines selection behavior resulting from a key event when selection mode
11416
* is set to "singlecell".
11418
* @method _handleSingleCellSelectionByKey
11419
* @param e {HTMLEvent} Event object.
11422
_handleSingleCellSelectionByKey : function(e) {
11423
var nKey = Ev.getCharCode(e);
11424
if((nKey == 9) || ((nKey > 36) && (nKey < 41))) {
11425
var bSHIFT = e.shiftKey;
11427
// Validate trigger
11428
var oTrigger = this._getSelectionTrigger();
11429
// Arrow selection only works if last selected row is on current page
11434
// Determine the new cell to select
11436
if(nKey == 40) { // Arrow down
11437
elNew = this.getBelowTdEl(oTrigger.el);
11439
// Validate new cell
11440
if(elNew === null) {
11441
//TODO: wrap around to first tr on current page
11443
//TODO: wrap forward to first tr of next page
11445
// Bottom selection is sticky
11446
elNew = oTrigger.el;
11449
else if(nKey == 38) { // Arrow up
11450
elNew = this.getAboveTdEl(oTrigger.el);
11452
// Validate new cell
11453
if(elNew === null) {
11454
//TODO: wrap around to last tr on current page
11456
//TODO: wrap back to last tr of previous page
11458
// Top selection is sticky
11459
elNew = oTrigger.el;
11462
else if((nKey == 39) || (!bSHIFT && (nKey == 9))) { // Arrow right or tab
11463
elNew = this.getNextTdEl(oTrigger.el);
11465
// Validate new cell
11466
if(elNew === null) {
11467
//TODO: wrap around to first td on current page
11469
//TODO: wrap forward to first td of next page
11471
// Top-left selection is sticky, and release TAB focus
11472
//elNew = oTrigger.el;
11476
else if((nKey == 37) || (bSHIFT && (nKey == 9))) { // Arrow left or shift-tab
11477
elNew = this.getPreviousTdEl(oTrigger.el);
11479
// Validate new cell
11480
if(elNew === null) {
11481
//TODO: wrap around to last td on current page
11483
//TODO: wrap back to last td of previous page
11485
// Bottom-right selection is sticky, and release TAB focus
11486
//elNew = oTrigger.el;
11493
// Unselect all cells
11494
this.unselectAllCells();
11496
// Select the new cell
11497
this.selectCell(elNew);
11500
this._oAnchorCell = {record:this.getRecord(elNew), column:this.getColumn(elNew)};
11505
* Returns array of selected TR elements on the page.
11507
* @method getSelectedTrEls
11508
* @return {HTMLElement[]} Array of selected TR elements.
11510
getSelectedTrEls : function() {
11511
return Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
11515
* Sets given row to the selected state.
11517
* @method selectRow
11518
* @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
11519
* reference or ID string, Record instance, or RecordSet position index.
11521
selectRow : function(row) {
11522
var oRecord, elRow;
11524
if(row instanceof YAHOO.widget.Record) {
11525
oRecord = this._oRecordSet.getRecord(row);
11526
elRow = this.getTrEl(oRecord);
11528
else if(lang.isNumber(row)) {
11529
oRecord = this.getRecord(row);
11530
elRow = this.getTrEl(oRecord);
11533
elRow = this.getTrEl(row);
11534
oRecord = this.getRecord(elRow);
11538
// Update selection trackers
11539
var tracker = this._aSelections || [];
11540
var sRecordId = oRecord.getId();
11543
// Remove if already there:
11544
// Use Array.indexOf if available...
11545
/*if(tracker.indexOf && (tracker.indexOf(sRecordId) > -1)) {
11546
tracker.splice(tracker.indexOf(sRecordId),1);
11548
if(tracker.indexOf) {
11549
index = tracker.indexOf(sRecordId);
11552
// ...or do it the old-fashioned way
11554
for(var j=tracker.length-1; j>-1; j--) {
11555
if(tracker[j] === sRecordId){
11562
tracker.splice(index,1);
11566
tracker.push(sRecordId);
11567
this._aSelections = tracker;
11570
if(!this._oAnchorRecord) {
11571
this._oAnchorRecord = oRecord;
11576
Dom.addClass(elRow, DT.CLASS_SELECTED);
11579
this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
11580
YAHOO.log("Selected " + elRow, "info", this.toString());
11583
YAHOO.log("Could not select row " + row, "warn", this.toString());
11588
* Sets given row to the unselected state.
11590
* @method unselectRow
11591
* @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
11592
* reference or ID string, Record instance, or RecordSet position index.
11594
unselectRow : function(row) {
11595
var elRow = this.getTrEl(row);
11598
if(row instanceof YAHOO.widget.Record) {
11599
oRecord = this._oRecordSet.getRecord(row);
11601
else if(lang.isNumber(row)) {
11602
oRecord = this.getRecord(row);
11605
oRecord = this.getRecord(elRow);
11609
// Update selection trackers
11610
var tracker = this._aSelections || [];
11611
var sRecordId = oRecord.getId();
11614
// Use Array.indexOf if available...
11615
if(tracker.indexOf) {
11616
index = tracker.indexOf(sRecordId);
11618
// ...or do it the old-fashioned way
11620
for(var j=tracker.length-1; j>-1; j--) {
11621
if(tracker[j] === sRecordId){
11629
tracker.splice(index,1);
11630
this._aSelections = tracker;
11633
Dom.removeClass(elRow, DT.CLASS_SELECTED);
11635
this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
11636
YAHOO.log("Unselected " + elRow, "info", this.toString());
11641
YAHOO.log("Could not unselect row " + row, "warn", this.toString());
11645
* Clears out all row selections.
11647
* @method unselectAllRows
11649
unselectAllRows : function() {
11650
// Remove all rows from tracker
11651
var tracker = this._aSelections || [],
11654
for(var j=tracker.length-1; j>-1; j--) {
11655
if(lang.isString(tracker[j])){
11656
recId = tracker.splice(j,1);
11657
removed[removed.length] = this.getRecord(lang.isArray(recId) ? recId[0] : recId);
11662
this._aSelections = tracker;
11665
this._unselectAllTrEls();
11667
this.fireEvent("unselectAllRowsEvent", {records: removed});
11668
YAHOO.log("Unselected all rows", "info", this.toString());
11672
* Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
11673
* from all TD elements in the internal tracker.
11675
* @method _unselectAllTdEls
11678
_unselectAllTdEls : function() {
11679
var selectedCells = Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
11680
Dom.removeClass(selectedCells, DT.CLASS_SELECTED);
11684
* Returns array of selected TD elements on the page.
11686
* @method getSelectedTdEls
11687
* @return {HTMLElement[]} Array of selected TD elements.
11689
getSelectedTdEls : function() {
11690
return Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
11694
* Sets given cell to the selected state.
11696
* @method selectCell
11697
* @param cell {HTMLElement | String} DOM element reference or ID string
11698
* to DataTable page element or RecordSet index.
11700
selectCell : function(cell) {
11701
//TODO: accept {record} in selectCell()
11702
var elCell = this.getTdEl(cell);
11705
var oRecord = this.getRecord(elCell);
11706
var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
11708
if(oRecord && sColumnKey) {
11710
var tracker = this._aSelections || [];
11711
var sRecordId = oRecord.getId();
11714
for(var j=tracker.length-1; j>-1; j--) {
11715
if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
11716
tracker.splice(j,1);
11722
tracker.push({recordId:sRecordId, columnKey:sColumnKey});
11725
this._aSelections = tracker;
11726
if(!this._oAnchorCell) {
11727
this._oAnchorCell = {record:oRecord, column:this.getColumn(sColumnKey)};
11731
Dom.addClass(elCell, DT.CLASS_SELECTED);
11733
this.fireEvent("cellSelectEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key: this.getColumn(elCell.cellIndex).getKey(), el:elCell});
11734
YAHOO.log("Selected " + elCell, "info", this.toString());
11738
YAHOO.log("Could not select cell " + cell, "warn", this.toString());
11742
* Sets given cell to the unselected state.
11744
* @method unselectCell
11745
* @param cell {HTMLElement | String} DOM element reference or ID string
11746
* to DataTable page element or RecordSet index.
11748
unselectCell : function(cell) {
11749
var elCell = this.getTdEl(cell);
11752
var oRecord = this.getRecord(elCell);
11753
var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
11755
if(oRecord && sColumnKey) {
11757
var tracker = this._aSelections || [];
11758
var id = oRecord.getId();
11761
for(var j=tracker.length-1; j>-1; j--) {
11762
if((tracker[j].recordId === id) && (tracker[j].columnKey === sColumnKey)){
11763
// Remove from tracker
11764
tracker.splice(j,1);
11767
this._aSelections = tracker;
11770
Dom.removeClass(elCell, DT.CLASS_SELECTED);
11772
this.fireEvent("cellUnselectEvent", {record:oRecord, column: this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
11773
YAHOO.log("Unselected " + elCell, "info", this.toString());
11779
YAHOO.log("Could not unselect cell " + cell, "warn", this.toString());
11783
* Clears out all cell selections.
11785
* @method unselectAllCells
11787
unselectAllCells : function() {
11788
// Remove all cells from tracker
11789
var tracker = this._aSelections || [];
11790
for(var j=tracker.length-1; j>-1; j--) {
11791
if(lang.isObject(tracker[j])){
11792
tracker.splice(j,1);
11797
this._aSelections = tracker;
11800
this._unselectAllTdEls();
11802
//TODO: send data to unselectAllCellsEvent handler
11803
this.fireEvent("unselectAllCellsEvent");
11804
YAHOO.log("Unselected all cells", "info", this.toString());
11808
* Returns true if given item is selected, false otherwise.
11810
* @method isSelected
11811
* @param o {String | HTMLElement | YAHOO.widget.Record | Number
11812
* {record:YAHOO.widget.Record, column:YAHOO.widget.Column} } TR or TD element by
11813
* reference or ID string, a Record instance, a RecordSet position index,
11814
* or an object literal representation
11816
* @return {Boolean} True if item is selected.
11818
isSelected : function(o) {
11819
if(o && (o.ownerDocument == document)) {
11820
return (Dom.hasClass(this.getTdEl(o),DT.CLASS_SELECTED) || Dom.hasClass(this.getTrEl(o),DT.CLASS_SELECTED));
11823
var oRecord, sRecordId, j;
11824
var tracker = this._aSelections;
11825
if(tracker && tracker.length > 0) {
11826
// Looking for a Record?
11827
if(o instanceof YAHOO.widget.Record) {
11830
else if(lang.isNumber(o)) {
11831
oRecord = this.getRecord(o);
11834
sRecordId = oRecord.getId();
11837
// Use Array.indexOf if available...
11838
if(tracker.indexOf) {
11839
if(tracker.indexOf(sRecordId) > -1) {
11843
// ...or do it the old-fashioned way
11845
for(j=tracker.length-1; j>-1; j--) {
11846
if(tracker[j] === sRecordId){
11852
// Looking for a cell
11853
else if(o.record && o.column){
11854
sRecordId = o.record.getId();
11855
var sColumnKey = o.column.getKey();
11857
for(j=tracker.length-1; j>-1; j--) {
11858
if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
11869
* Returns selected rows as an array of Record IDs.
11871
* @method getSelectedRows
11872
* @return {String[]} Array of selected rows by Record ID.
11874
getSelectedRows : function() {
11875
var aSelectedRows = [];
11876
var tracker = this._aSelections || [];
11877
for(var j=0; j<tracker.length; j++) {
11878
if(lang.isString(tracker[j])){
11879
aSelectedRows.push(tracker[j]);
11882
return aSelectedRows;
11886
* Returns selected cells as an array of object literals:
11887
* {recordId:sRecordId, columnKey:sColumnKey}.
11889
* @method getSelectedCells
11890
* @return {Object[]} Array of selected cells by Record ID and Column ID.
11892
getSelectedCells : function() {
11893
var aSelectedCells = [];
11894
var tracker = this._aSelections || [];
11895
for(var j=0; j<tracker.length; j++) {
11896
if(tracker[j] && lang.isObject(tracker[j])){
11897
aSelectedCells.push(tracker[j]);
11900
return aSelectedCells;
11904
* Returns last selected Record ID.
11906
* @method getLastSelectedRecord
11907
* @return {String} Record ID of last selected row.
11909
getLastSelectedRecord : function() {
11910
var tracker = this._aSelections;
11911
if(tracker && tracker.length > 0) {
11912
for(var i=tracker.length-1; i>-1; i--) {
11913
if(lang.isString(tracker[i])){
11921
* Returns last selected cell as an object literal:
11922
* {recordId:sRecordId, columnKey:sColumnKey}.
11924
* @method getLastSelectedCell
11925
* @return {Object} Object literal representation of a cell.
11927
getLastSelectedCell : function() {
11928
var tracker = this._aSelections;
11929
if(tracker && tracker.length > 0) {
11930
for(var i=tracker.length-1; i>-1; i--) {
11931
if(tracker[i].recordId && tracker[i].columnKey){
11939
* Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row.
11941
* @method highlightRow
11942
* @param row {HTMLElement | String} DOM element reference or ID string.
11944
highlightRow : function(row) {
11945
var elRow = this.getTrEl(row);
11948
// Make sure previous row is unhighlighted
11949
/* if(this._sLastHighlightedTrElId) {
11950
Dom.removeClass(this._sLastHighlightedTrElId,DT.CLASS_HIGHLIGHTED);
11952
var oRecord = this.getRecord(elRow);
11953
Dom.addClass(elRow,DT.CLASS_HIGHLIGHTED);
11954
//this._sLastHighlightedTrElId = elRow.id;
11955
this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow});
11956
YAHOO.log("Highlighted " + elRow, "info", this.toString());
11959
YAHOO.log("Could not highlight row " + row, "warn", this.toString());
11963
* Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row.
11965
* @method unhighlightRow
11966
* @param row {HTMLElement | String} DOM element reference or ID string.
11968
unhighlightRow : function(row) {
11969
var elRow = this.getTrEl(row);
11972
var oRecord = this.getRecord(elRow);
11973
Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED);
11974
this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
11975
YAHOO.log("Unhighlighted " + elRow, "info", this.toString());
11978
YAHOO.log("Could not unhighlight row " + row, "warn", this.toString());
11982
* Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell.
11984
* @method highlightCell
11985
* @param cell {HTMLElement | String} DOM element reference or ID string.
11987
highlightCell : function(cell) {
11988
var elCell = this.getTdEl(cell);
11991
// Make sure previous cell is unhighlighted
11992
if(this._elLastHighlightedTd) {
11993
this.unhighlightCell(this._elLastHighlightedTd);
11996
var oRecord = this.getRecord(elCell);
11997
var sColumnKey = this.getColumn(elCell.cellIndex).getKey();
11998
Dom.addClass(elCell,DT.CLASS_HIGHLIGHTED);
11999
this._elLastHighlightedTd = elCell;
12000
this.fireEvent("cellHighlightEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
12001
YAHOO.log("Highlighted " + elCell, "info", this.toString());
12004
YAHOO.log("Could not highlight cell " + cell, "warn", this.toString());
12008
* Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell.
12010
* @method unhighlightCell
12011
* @param cell {HTMLElement | String} DOM element reference or ID string.
12013
unhighlightCell : function(cell) {
12014
var elCell = this.getTdEl(cell);
12017
var oRecord = this.getRecord(elCell);
12018
Dom.removeClass(elCell,DT.CLASS_HIGHLIGHTED);
12019
this._elLastHighlightedTd = null;
12020
this.fireEvent("cellUnhighlightEvent", {record:oRecord, column:this.getColumn(elCell.cellIndex), key:this.getColumn(elCell.cellIndex).getKey(), el:elCell});
12021
YAHOO.log("Unhighlighted " + elCell, "info", this.toString());
12024
YAHOO.log("Could not unhighlight cell " + cell, "warn", this.toString());
12074
* Returns current CellEditor instance, or null.
12075
* @method getCellEditor
12076
* @return {YAHOO.widget.CellEditor} CellEditor instance.
12078
getCellEditor : function() {
12079
return this._oCellEditor;
12084
* Activates and shows CellEditor instance for the given cell while deactivating and
12085
* canceling previous CellEditor. It is baked into DataTable that only one CellEditor
12086
* can be active at any given time.
12088
* @method showCellEditor
12089
* @param elCell {HTMLElement | String} Cell to edit.
12091
showCellEditor : function(elCell, oRecord, oColumn) {
12092
// Get a particular CellEditor
12093
elCell = this.getTdEl(elCell);
12095
oColumn = this.getColumn(elCell);
12096
if(oColumn && oColumn.editor) {
12097
var oCellEditor = this._oCellEditor;
12098
// Clean up active CellEditor
12100
if(this._oCellEditor.cancel) {
12101
this._oCellEditor.cancel();
12103
else if(oCellEditor.isActive) {
12104
this.cancelCellEditor();
12108
if(oColumn.editor instanceof YAHOO.widget.BaseCellEditor) {
12110
oCellEditor = oColumn.editor;
12111
var ok = oCellEditor.attach(this, elCell);
12113
oCellEditor.move();
12114
ok = this.doBeforeShowCellEditor(oCellEditor);
12116
oCellEditor.show();
12117
this._oCellEditor = oCellEditor;
12121
// Backward compatibility
12123
if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) {
12124
oRecord = this.getRecord(elCell);
12126
if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) {
12127
oColumn = this.getColumn(elCell);
12129
if(oRecord && oColumn) {
12130
if(!this._oCellEditor || this._oCellEditor.container) {
12131
this._initCellEditorEl();
12134
// Update Editor values
12135
oCellEditor = this._oCellEditor;
12136
oCellEditor.cell = elCell;
12137
oCellEditor.record = oRecord;
12138
oCellEditor.column = oColumn;
12139
oCellEditor.validator = (oColumn.editorOptions &&
12140
lang.isFunction(oColumn.editorOptions.validator)) ?
12141
oColumn.editorOptions.validator : null;
12142
oCellEditor.value = oRecord.getData(oColumn.key);
12143
oCellEditor.defaultValue = null;
12146
var elContainer = oCellEditor.container;
12147
var x = Dom.getX(elCell);
12148
var y = Dom.getY(elCell);
12150
// SF doesn't get xy for cells in scrolling table
12151
// when tbody display is set to block
12152
if(isNaN(x) || isNaN(y)) {
12153
x = elCell.offsetLeft + // cell pos relative to table
12154
Dom.getX(this._elTbody.parentNode) - // plus table pos relative to document
12155
this._elTbody.scrollLeft; // minus tbody scroll
12156
y = elCell.offsetTop + // cell pos relative to table
12157
Dom.getY(this._elTbody.parentNode) - // plus table pos relative to document
12158
this._elTbody.scrollTop + // minus tbody scroll
12159
this._elThead.offsetHeight; // account for fixed THEAD cells
12162
elContainer.style.left = x + "px";
12163
elContainer.style.top = y + "px";
12165
// Hook to customize the UI
12166
this.doBeforeShowCellEditor(this._oCellEditor);
12168
//TODO: This is temporarily up here due so elements can be focused
12170
elContainer.style.display = "";
12173
Ev.addListener(elContainer, "keydown", function(e, oSelf) {
12174
// ESC hides Cell Editor
12175
if((e.keyCode == 27)) {
12176
oSelf.cancelCellEditor();
12177
oSelf.focusTbodyEl();
12180
oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e});
12184
// Render Editor markup
12186
if(lang.isString(oColumn.editor)) {
12187
switch(oColumn.editor) {
12189
fnEditor = DT.editCheckbox;
12192
fnEditor = DT.editDate;
12195
fnEditor = DT.editDropdown;
12198
fnEditor = DT.editRadio;
12201
fnEditor = DT.editTextarea;
12204
fnEditor = DT.editTextbox;
12210
else if(lang.isFunction(oColumn.editor)) {
12211
fnEditor = oColumn.editor;
12215
// Create DOM input elements
12216
fnEditor(this._oCellEditor, this);
12218
// Show Save/Cancel buttons
12219
if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) {
12220
this.showCellEditorBtns(elContainer);
12223
oCellEditor.isActive = true;
12225
//TODO: verify which args to pass
12226
this.fireEvent("editorShowEvent", {editor:oCellEditor});
12227
YAHOO.log("Cell Editor shown for " + elCell, "info", this.toString());
12241
* Backward compatibility.
12243
* @method _initCellEditorEl
12247
_initCellEditorEl : function() {
12248
// Attach Cell Editor container element as first child of body
12249
var elCellEditor = document.createElement("div");
12250
elCellEditor.id = this._sId + "-celleditor";
12251
elCellEditor.style.display = "none";
12252
elCellEditor.tabIndex = 0;
12253
Dom.addClass(elCellEditor, DT.CLASS_EDITOR);
12254
var elFirstChild = Dom.getFirstChild(document.body);
12256
elCellEditor = Dom.insertBefore(elCellEditor, elFirstChild);
12259
elCellEditor = document.body.appendChild(elCellEditor);
12262
// Internal tracker of Cell Editor values
12263
var oCellEditor = {};
12264
oCellEditor.container = elCellEditor;
12265
oCellEditor.value = null;
12266
oCellEditor.isActive = false;
12267
this._oCellEditor = oCellEditor;
12271
* Overridable abstract method to customize CellEditor before showing.
12273
* @method doBeforeShowCellEditor
12274
* @param oCellEditor {YAHOO.widget.CellEditor} The CellEditor instance.
12275
* @return {Boolean} Return true to continue showing CellEditor.
12277
doBeforeShowCellEditor : function(oCellEditor) {
12282
* Saves active CellEditor input to Record and upates DOM UI.
12284
* @method saveCellEditor
12286
saveCellEditor : function() {
12287
if(this._oCellEditor) {
12288
if(this._oCellEditor.save) {
12289
this._oCellEditor.save();
12291
// Backward compatibility
12292
else if(this._oCellEditor.isActive) {
12293
var newData = this._oCellEditor.value;
12294
// Copy the data to pass to the event
12295
//var oldData = YAHOO.widget.DataTable._cloneObject(this._oCellEditor.record.getData(this._oCellEditor.column.key));
12296
var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key);
12298
// Validate input data
12299
if(this._oCellEditor.validator) {
12300
newData = this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData, this._oCellEditor);
12301
if(newData === null ) {
12302
this.resetCellEditor();
12303
this.fireEvent("editorRevertEvent",
12304
{editor:this._oCellEditor, oldData:oldData, newData:newData});
12305
YAHOO.log("Could not save Cell Editor input due to invalid data " +
12306
lang.dump(newData), "warn", this.toString());
12310
// Update the Record
12311
this._oRecordSet.updateRecordValue(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
12313
this.formatCell(this._oCellEditor.cell.firstChild);
12316
this._oChainRender.add({
12317
method: function() {
12318
this.validateColumnWidths();
12322
this._oChainRender.run();
12323
// Clear out the Cell Editor
12324
this.resetCellEditor();
12326
this.fireEvent("editorSaveEvent",
12327
{editor:this._oCellEditor, oldData:oldData, newData:newData});
12328
YAHOO.log("Cell Editor input saved", "info", this.toString());
12334
* Cancels active CellEditor.
12336
* @method cancelCellEditor
12338
cancelCellEditor : function() {
12339
if(this._oCellEditor) {
12340
if(this._oCellEditor.cancel) {
12341
this._oCellEditor.cancel();
12343
// Backward compatibility
12344
else if(this._oCellEditor.isActive) {
12345
this.resetCellEditor();
12346
//TODO: preserve values for the event?
12347
this.fireEvent("editorCancelEvent", {editor:this._oCellEditor});
12348
YAHOO.log("Cell Editor input canceled", "info", this.toString());
12351
YAHOO.log("CellEditor input canceled", "info", this.toString());
12356
* Destroys active CellEditor instance and UI.
12358
* @method destroyCellEditor
12360
destroyCellEditor : function() {
12361
if(this._oCellEditor) {
12362
this._oCellEditor.destroy();
12363
this._oCellEditor = null;
12368
* Passes through showEvent of the active CellEditor.
12370
* @method _onEditorShowEvent
12371
* @param oArgs {Object} Custom Event args.
12374
_onEditorShowEvent : function(oArgs) {
12375
this.fireEvent("editorShowEvent", oArgs);
12379
* Passes through keydownEvent of the active CellEditor.
12380
* @param oArgs {Object} Custom Event args.
12382
* @method _onEditorKeydownEvent
12385
_onEditorKeydownEvent : function(oArgs) {
12386
this.fireEvent("editorKeydownEvent", oArgs);
12390
* Passes through revertEvent of the active CellEditor.
12392
* @method _onEditorRevertEvent
12393
* @param oArgs {Object} Custom Event args.
12396
_onEditorRevertEvent : function(oArgs) {
12397
this.fireEvent("editorRevertEvent", oArgs);
12401
* Passes through saveEvent of the active CellEditor.
12403
* @method _onEditorSaveEvent
12404
* @param oArgs {Object} Custom Event args.
12407
_onEditorSaveEvent : function(oArgs) {
12408
this.fireEvent("editorSaveEvent", oArgs);
12412
* Passes through cancelEvent of the active CellEditor.
12414
* @method _onEditorCancelEvent
12415
* @param oArgs {Object} Custom Event args.
12418
_onEditorCancelEvent : function(oArgs) {
12419
this.fireEvent("editorCancelEvent", oArgs);
12423
* Passes through blurEvent of the active CellEditor.
12425
* @method _onEditorBlurEvent
12426
* @param oArgs {Object} Custom Event args.
12429
_onEditorBlurEvent : function(oArgs) {
12430
this.fireEvent("editorBlurEvent", oArgs);
12434
* Passes through blockEvent of the active CellEditor.
12436
* @method _onEditorBlockEvent
12437
* @param oArgs {Object} Custom Event args.
12440
_onEditorBlockEvent : function(oArgs) {
12441
this.fireEvent("editorBlockEvent", oArgs);
12445
* Passes through unblockEvent of the active CellEditor.
12447
* @method _onEditorUnblockEvent
12448
* @param oArgs {Object} Custom Event args.
12451
_onEditorUnblockEvent : function(oArgs) {
12452
this.fireEvent("editorUnblockEvent", oArgs);
12456
* Public handler of the editorBlurEvent. By default, saves on blur if
12457
* disableBtns is true, otherwise cancels on blur.
12459
* @method onEditorBlurEvent
12460
* @param oArgs {Object} Custom Event args.
12462
onEditorBlurEvent : function(oArgs) {
12463
if(oArgs.editor.disableBtns) {
12465
if(oArgs.editor.save) { // Backward incompatible
12466
oArgs.editor.save();
12469
else if(oArgs.editor.cancel) { // Backward incompatible
12471
oArgs.editor.cancel();
12476
* Public handler of the editorBlockEvent. By default, disables DataTable UI.
12478
* @method onEditorBlockEvent
12479
* @param oArgs {Object} Custom Event args.
12481
onEditorBlockEvent : function(oArgs) {
12486
* Public handler of the editorUnblockEvent. By default, undisables DataTable UI.
12488
* @method onEditorUnblockEvent
12489
* @param oArgs {Object} Custom Event args.
12491
onEditorUnblockEvent : function(oArgs) {
12532
// ABSTRACT METHODS
12535
* Overridable method gives implementers a hook to access data before
12536
* it gets added to RecordSet and rendered to the TBODY.
12538
* @method doBeforeLoadData
12539
* @param sRequest {String} Original request.
12540
* @param oResponse {Object} Response object.
12541
* @param oPayload {MIXED} additional arguments
12542
* @return {Boolean} Return true to continue loading data into RecordSet and
12543
* updating DataTable with new Records, false to cancel.
12545
doBeforeLoadData : function(sRequest, oResponse, oPayload) {
12611
/////////////////////////////////////////////////////////////////////////////
12613
// Public Custom Event Handlers
12615
/////////////////////////////////////////////////////////////////////////////
12618
* Overridable custom event handler to sort Column.
12620
* @method onEventSortColumn
12621
* @param oArgs.event {HTMLEvent} Event object.
12622
* @param oArgs.target {HTMLElement} Target element.
12624
onEventSortColumn : function(oArgs) {
12625
//TODO: support form elements in sortable columns
12626
var evt = oArgs.event;
12627
var target = oArgs.target;
12629
var el = this.getThEl(target) || this.getTdEl(target);
12631
var oColumn = this.getColumn(el);
12632
if(oColumn.sortable) {
12634
this.sortColumn(oColumn);
12638
YAHOO.log("Could not find Column for " + target, "warn", this.toString());
12643
* Overridable custom event handler to select Column.
12645
* @method onEventSelectColumn
12646
* @param oArgs.event {HTMLEvent} Event object.
12647
* @param oArgs.target {HTMLElement} Target element.
12649
onEventSelectColumn : function(oArgs) {
12650
this.selectColumn(oArgs.target);
12654
* Overridable custom event handler to highlight Column. Accounts for spurious
12655
* caused-by-child events.
12657
* @method onEventHighlightColumn
12658
* @param oArgs.event {HTMLEvent} Event object.
12659
* @param oArgs.target {HTMLElement} Target element.
12661
onEventHighlightColumn : function(oArgs) {
12662
//TODO: filter for all spurious events at a lower level
12663
if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12664
this.highlightColumn(oArgs.target);
12669
* Overridable custom event handler to unhighlight Column. Accounts for spurious
12670
* caused-by-child events.
12672
* @method onEventUnhighlightColumn
12673
* @param oArgs.event {HTMLEvent} Event object.
12674
* @param oArgs.target {HTMLElement} Target element.
12676
onEventUnhighlightColumn : function(oArgs) {
12677
//TODO: filter for all spurious events at a lower level
12678
if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12679
this.unhighlightColumn(oArgs.target);
12684
* Overridable custom event handler to manage selection according to desktop paradigm.
12686
* @method onEventSelectRow
12687
* @param oArgs.event {HTMLEvent} Event object.
12688
* @param oArgs.target {HTMLElement} Target element.
12690
onEventSelectRow : function(oArgs) {
12691
var sMode = this.get("selectionMode");
12692
if(sMode == "single") {
12693
this._handleSingleSelectionByMouse(oArgs);
12696
this._handleStandardSelectionByMouse(oArgs);
12701
* Overridable custom event handler to select cell.
12703
* @method onEventSelectCell
12704
* @param oArgs.event {HTMLEvent} Event object.
12705
* @param oArgs.target {HTMLElement} Target element.
12707
onEventSelectCell : function(oArgs) {
12708
var sMode = this.get("selectionMode");
12709
if(sMode == "cellblock") {
12710
this._handleCellBlockSelectionByMouse(oArgs);
12712
else if(sMode == "cellrange") {
12713
this._handleCellRangeSelectionByMouse(oArgs);
12716
this._handleSingleCellSelectionByMouse(oArgs);
12721
* Overridable custom event handler to highlight row. Accounts for spurious
12722
* caused-by-child events.
12724
* @method onEventHighlightRow
12725
* @param oArgs.event {HTMLEvent} Event object.
12726
* @param oArgs.target {HTMLElement} Target element.
12728
onEventHighlightRow : function(oArgs) {
12729
//TODO: filter for all spurious events at a lower level
12730
if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12731
this.highlightRow(oArgs.target);
12736
* Overridable custom event handler to unhighlight row. Accounts for spurious
12737
* caused-by-child events.
12739
* @method onEventUnhighlightRow
12740
* @param oArgs.event {HTMLEvent} Event object.
12741
* @param oArgs.target {HTMLElement} Target element.
12743
onEventUnhighlightRow : function(oArgs) {
12744
//TODO: filter for all spurious events at a lower level
12745
if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12746
this.unhighlightRow(oArgs.target);
12751
* Overridable custom event handler to highlight cell. Accounts for spurious
12752
* caused-by-child events.
12754
* @method onEventHighlightCell
12755
* @param oArgs.event {HTMLEvent} Event object.
12756
* @param oArgs.target {HTMLElement} Target element.
12758
onEventHighlightCell : function(oArgs) {
12759
//TODO: filter for all spurious events at a lower level
12760
if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12761
this.highlightCell(oArgs.target);
12766
* Overridable custom event handler to unhighlight cell. Accounts for spurious
12767
* caused-by-child events.
12769
* @method onEventUnhighlightCell
12770
* @param oArgs.event {HTMLEvent} Event object.
12771
* @param oArgs.target {HTMLElement} Target element.
12773
onEventUnhighlightCell : function(oArgs) {
12774
//TODO: filter for all spurious events at a lower level
12775
if(!Dom.isAncestor(oArgs.target,Ev.getRelatedTarget(oArgs.event))) {
12776
this.unhighlightCell(oArgs.target);
12781
* Overridable custom event handler to format cell.
12783
* @method onEventFormatCell
12784
* @param oArgs.event {HTMLEvent} Event object.
12785
* @param oArgs.target {HTMLElement} Target element.
12787
onEventFormatCell : function(oArgs) {
12788
var target = oArgs.target;
12790
var elCell = this.getTdEl(target);
12792
var oColumn = this.getColumn(elCell.cellIndex);
12793
this.formatCell(elCell.firstChild, this.getRecord(elCell), oColumn);
12796
YAHOO.log("Could not format cell " + target, "warn", this.toString());
12801
* Overridable custom event handler to edit cell.
12803
* @method onEventShowCellEditor
12804
* @param oArgs.event {HTMLEvent} Event object.
12805
* @param oArgs.target {HTMLElement} Target element.
12807
onEventShowCellEditor : function(oArgs) {
12808
this.showCellEditor(oArgs.target);
12812
* Overridable custom event handler to save active CellEditor input.
12814
* @method onEventSaveCellEditor
12816
onEventSaveCellEditor : function(oArgs) {
12817
if(this._oCellEditor) {
12818
if(this._oCellEditor.save) {
12819
this._oCellEditor.save();
12821
// Backward compatibility
12823
this.saveCellEditor();
12829
* Overridable custom event handler to cancel active CellEditor.
12831
* @method onEventCancelCellEditor
12833
onEventCancelCellEditor : function(oArgs) {
12834
if(this._oCellEditor) {
12835
if(this._oCellEditor.cancel) {
12836
this._oCellEditor.cancel();
12838
// Backward compatibility
12840
this.cancelCellEditor();
12846
* Callback function receives data from DataSource and populates an entire
12847
* DataTable with Records and TR elements, clearing previous Records, if any.
12849
* @method onDataReturnInitializeTable
12850
* @param sRequest {String} Original request.
12851
* @param oResponse {Object} Response object.
12852
* @param oPayload {MIXED} (optional) Additional argument(s)
12854
onDataReturnInitializeTable : function(sRequest, oResponse, oPayload) {
12855
if((this instanceof DT) && this._sId) {
12856
this.initializeTable();
12858
this.onDataReturnSetRows(sRequest,oResponse,oPayload);
12863
* Callback function receives reponse from DataSource, replaces all existing
12864
* Records in RecordSet, updates TR elements with new data, and updates state
12865
* UI for pagination and sorting from payload data, if necessary.
12867
* @method onDataReturnReplaceRows
12868
* @param oRequest {MIXED} Original generated request.
12869
* @param oResponse {Object} Response object.
12870
* @param oPayload {MIXED} (optional) Additional argument(s)
12872
onDataReturnReplaceRows : function(oRequest, oResponse, oPayload) {
12873
if((this instanceof DT) && this._sId) {
12874
this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
12876
// Pass data through abstract method for any transformations
12877
var ok = this.doBeforeLoadData(oRequest, oResponse, oPayload),
12878
pag = this.get('paginator'),
12882
if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
12884
this._oRecordSet.reset();
12886
if (this.get('dynamicData')) {
12887
if (oPayload && oPayload.pagination &&
12888
lang.isNumber(oPayload.pagination.recordOffset)) {
12889
index = oPayload.pagination.recordOffset;
12891
index = pag.getStartIndex();
12895
this._oRecordSet.setRecords(oResponse.results, index | 0);
12898
this._handleDataReturnPayload(oRequest, oResponse, oPayload);
12904
else if(ok && oResponse.error) {
12905
this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
12911
* Callback function receives data from DataSource and appends to an existing
12912
* DataTable new Records and, if applicable, creates or updates
12913
* corresponding TR elements.
12915
* @method onDataReturnAppendRows
12916
* @param sRequest {String} Original request.
12917
* @param oResponse {Object} Response object.
12918
* @param oPayload {MIXED} (optional) Additional argument(s)
12920
onDataReturnAppendRows : function(sRequest, oResponse, oPayload) {
12921
if((this instanceof DT) && this._sId) {
12922
this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
12924
// Pass data through abstract method for any transformations
12925
var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
12927
// Data ok to append
12928
if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
12930
this.addRows(oResponse.results);
12933
this._handleDataReturnPayload(sRequest, oResponse, oPayload);
12936
else if(ok && oResponse.error) {
12937
this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
12943
* Callback function receives data from DataSource and inserts new records
12944
* starting at the index specified in oPayload.insertIndex. The value for
12945
* oPayload.insertIndex can be populated when sending the request to the DataSource,
12946
* or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime.
12947
* If applicable, creates or updates corresponding TR elements.
12949
* @method onDataReturnInsertRows
12950
* @param sRequest {String} Original request.
12951
* @param oResponse {Object} Response object.
12952
* @param oPayload {MIXED} Argument payload, looks in oPayload.insertIndex.
12954
onDataReturnInsertRows : function(sRequest, oResponse, oPayload) {
12955
if((this instanceof DT) && this._sId) {
12956
this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
12958
// Pass data through abstract method for any transformations
12959
var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
12961
// Data ok to append
12962
if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
12964
this.addRows(oResponse.results, (oPayload ? oPayload.insertIndex : 0));
12967
this._handleDataReturnPayload(sRequest, oResponse, oPayload);
12970
else if(ok && oResponse.error) {
12971
this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
12977
* Callback function receives data from DataSource and incrementally updates Records
12978
* starting at the index specified in oPayload.updateIndex. The value for
12979
* oPayload.updateIndex can be populated when sending the request to the DataSource,
12980
* or by accessing oPayload.updateIndex with the doBeforeLoadData() method at runtime.
12981
* If applicable, creates or updates corresponding TR elements.
12983
* @method onDataReturnUpdateRows
12984
* @param sRequest {String} Original request.
12985
* @param oResponse {Object} Response object.
12986
* @param oPayload {MIXED} Argument payload, looks in oPayload.updateIndex.
12988
onDataReturnUpdateRows : function(sRequest, oResponse, oPayload) {
12989
if((this instanceof DT) && this._sId) {
12990
this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
12992
// Pass data through abstract method for any transformations
12993
var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
12995
// Data ok to append
12996
if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
12998
this.updateRows((oPayload ? oPayload.updateIndex : 0), oResponse.results);
13001
this._handleDataReturnPayload(sRequest, oResponse, oPayload);
13004
else if(ok && oResponse.error) {
13005
this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
13011
* Callback function receives reponse from DataSource and populates the
13012
* RecordSet with the results.
13014
* @method onDataReturnSetRows
13015
* @param oRequest {MIXED} Original generated request.
13016
* @param oResponse {Object} Response object.
13017
* @param oPayload {MIXED} (optional) Additional argument(s)
13019
onDataReturnSetRows : function(oRequest, oResponse, oPayload) {
13020
if((this instanceof DT) && this._sId) {
13021
this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
13023
// Pass data through abstract method for any transformations
13024
var ok = this.doBeforeLoadData(oRequest, oResponse, oPayload),
13025
pag = this.get('paginator'),
13029
if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
13031
if (this.get('dynamicData')) {
13032
if (oPayload && oPayload.pagination &&
13033
lang.isNumber(oPayload.pagination.recordOffset)) {
13034
index = oPayload.pagination.recordOffset;
13036
index = pag.getStartIndex();
13039
this._oRecordSet.reset(); // Bug 2290604: dyanmic data shouldn't keep accumulating by default
13042
this._oRecordSet.setRecords(oResponse.results, index | 0);
13045
this._handleDataReturnPayload(oRequest, oResponse, oPayload);
13051
else if(ok && oResponse.error) {
13052
this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
13056
YAHOO.log("Instance destroyed before data returned.","info",this.toString());
13061
* Hook to update oPayload before consumption.
13063
* @method handleDataReturnPayload
13064
* @param oRequest {MIXED} Original generated request.
13065
* @param oResponse {Object} Response object.
13066
* @param oPayload {MIXED} State values.
13067
* @return oPayload {MIXED} State values.
13069
handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
13074
* Updates the DataTable with state data sent in an onDataReturn* payload.
13076
* @method handleDataReturnPayload
13077
* @param oRequest {MIXED} Original generated request.
13078
* @param oResponse {Object} Response object.
13079
* @param oPayload {MIXED} State values
13081
_handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
13082
oPayload = this.handleDataReturnPayload(oRequest, oResponse, oPayload);
13084
// Update pagination
13085
var oPaginator = this.get('paginator');
13087
// Update totalRecords
13088
if(this.get("dynamicData")) {
13089
if (widget.Paginator.isNumeric(oPayload.totalRecords)) {
13090
oPaginator.set('totalRecords',oPayload.totalRecords);
13094
oPaginator.set('totalRecords',this._oRecordSet.getLength());
13096
// Update other paginator values
13097
if (lang.isObject(oPayload.pagination)) {
13098
oPaginator.set('rowsPerPage',oPayload.pagination.rowsPerPage);
13099
oPaginator.set('recordOffset',oPayload.pagination.recordOffset);
13104
if (oPayload.sortedBy) {
13105
// Set the sorting values in preparation for refresh
13106
this.set('sortedBy', oPayload.sortedBy);
13108
// Backwards compatibility for sorting
13109
else if (oPayload.sorting) {
13110
// Set the sorting values in preparation for refresh
13111
this.set('sortedBy', oPayload.sorting);
13148
/////////////////////////////////////////////////////////////////////////////
13152
/////////////////////////////////////////////////////////////////////////////
13155
* Fired when the DataTable's rows are rendered from an initialized state.
13161
* Fired when the DataTable's DOM is rendered or modified.
13163
* @event renderEvent
13167
* Fired when the DataTable's post-render routine is complete, including
13168
* Column width validations.
13170
* @event postRenderEvent
13174
* Fired when the DataTable is disabled.
13176
* @event disableEvent
13180
* Fired when the DataTable is undisabled.
13182
* @event undisableEvent
13186
* Fired when data is returned from DataSource but before it is consumed by
13189
* @event dataReturnEvent
13190
* @param oArgs.request {String} Original request.
13191
* @param oArgs.response {Object} Response object.
13195
* Fired when the DataTable has a focus event.
13197
* @event tableFocusEvent
13201
* Fired when the DataTable THEAD element has a focus event.
13203
* @event theadFocusEvent
13207
* Fired when the DataTable TBODY element has a focus event.
13209
* @event tbodyFocusEvent
13213
* Fired when the DataTable has a blur event.
13215
* @event tableBlurEvent
13218
/*TODO implement theadBlurEvent
13219
* Fired when the DataTable THEAD element has a blur event.
13221
* @event theadBlurEvent
13224
/*TODO: implement tbodyBlurEvent
13225
* Fired when the DataTable TBODY element has a blur event.
13227
* @event tbodyBlurEvent
13231
* Fired when the DataTable has a key event.
13233
* @event tableKeyEvent
13234
* @param oArgs.event {HTMLEvent} The event object.
13235
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13239
* Fired when the DataTable THEAD element has a key event.
13241
* @event theadKeyEvent
13242
* @param oArgs.event {HTMLEvent} The event object.
13243
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13247
* Fired when the DataTable TBODY element has a key event.
13249
* @event tbodyKeyEvent
13250
* @param oArgs.event {HTMLEvent} The event object.
13251
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13255
* Fired when the DataTable has a mouseover.
13257
* @event tableMouseoverEvent
13258
* @param oArgs.event {HTMLEvent} The event object.
13259
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13264
* Fired when the DataTable has a mouseout.
13266
* @event tableMouseoutEvent
13267
* @param oArgs.event {HTMLEvent} The event object.
13268
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13273
* Fired when the DataTable has a mousedown.
13275
* @event tableMousedownEvent
13276
* @param oArgs.event {HTMLEvent} The event object.
13277
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13282
* Fired when the DataTable has a mouseup.
13284
* @event tableMouseupEvent
13285
* @param oArgs.event {HTMLEvent} The event object.
13286
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13291
* Fired when the DataTable has a click.
13293
* @event tableClickEvent
13294
* @param oArgs.event {HTMLEvent} The event object.
13295
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13300
* Fired when the DataTable has a dblclick.
13302
* @event tableDblclickEvent
13303
* @param oArgs.event {HTMLEvent} The event object.
13304
* @param oArgs.target {HTMLElement} The DataTable's TABLE element.
13309
* Fired when a message is shown in the DataTable's message element.
13311
* @event tableMsgShowEvent
13312
* @param oArgs.html {String} The HTML displayed.
13313
* @param oArgs.className {String} The className assigned.
13318
* Fired when the DataTable's message element is hidden.
13320
* @event tableMsgHideEvent
13324
* Fired when a THEAD row has a mouseover.
13326
* @event theadRowMouseoverEvent
13327
* @param oArgs.event {HTMLEvent} The event object.
13328
* @param oArgs.target {HTMLElement} The TR element.
13332
* Fired when a THEAD row has a mouseout.
13334
* @event theadRowMouseoutEvent
13335
* @param oArgs.event {HTMLEvent} The event object.
13336
* @param oArgs.target {HTMLElement} The TR element.
13340
* Fired when a THEAD row has a mousedown.
13342
* @event theadRowMousedownEvent
13343
* @param oArgs.event {HTMLEvent} The event object.
13344
* @param oArgs.target {HTMLElement} The TR element.
13348
* Fired when a THEAD row has a mouseup.
13350
* @event theadRowMouseupEvent
13351
* @param oArgs.event {HTMLEvent} The event object.
13352
* @param oArgs.target {HTMLElement} The TR element.
13356
* Fired when a THEAD row has a click.
13358
* @event theadRowClickEvent
13359
* @param oArgs.event {HTMLEvent} The event object.
13360
* @param oArgs.target {HTMLElement} The TR element.
13364
* Fired when a THEAD row has a dblclick.
13366
* @event theadRowDblclickEvent
13367
* @param oArgs.event {HTMLEvent} The event object.
13368
* @param oArgs.target {HTMLElement} The TR element.
13372
* Fired when a THEAD cell has a mouseover.
13374
* @event theadCellMouseoverEvent
13375
* @param oArgs.event {HTMLEvent} The event object.
13376
* @param oArgs.target {HTMLElement} The TH element.
13381
* Fired when a THEAD cell has a mouseout.
13383
* @event theadCellMouseoutEvent
13384
* @param oArgs.event {HTMLEvent} The event object.
13385
* @param oArgs.target {HTMLElement} The TH element.
13390
* Fired when a THEAD cell has a mousedown.
13392
* @event theadCellMousedownEvent
13393
* @param oArgs.event {HTMLEvent} The event object.
13394
* @param oArgs.target {HTMLElement} The TH element.
13398
* Fired when a THEAD cell has a mouseup.
13400
* @event theadCellMouseupEvent
13401
* @param oArgs.event {HTMLEvent} The event object.
13402
* @param oArgs.target {HTMLElement} The TH element.
13406
* Fired when a THEAD cell has a click.
13408
* @event theadCellClickEvent
13409
* @param oArgs.event {HTMLEvent} The event object.
13410
* @param oArgs.target {HTMLElement} The TH element.
13414
* Fired when a THEAD cell has a dblclick.
13416
* @event theadCellDblclickEvent
13417
* @param oArgs.event {HTMLEvent} The event object.
13418
* @param oArgs.target {HTMLElement} The TH element.
13422
* Fired when a THEAD label has a mouseover.
13424
* @event theadLabelMouseoverEvent
13425
* @param oArgs.event {HTMLEvent} The event object.
13426
* @param oArgs.target {HTMLElement} The SPAN element.
13431
* Fired when a THEAD label has a mouseout.
13433
* @event theadLabelMouseoutEvent
13434
* @param oArgs.event {HTMLEvent} The event object.
13435
* @param oArgs.target {HTMLElement} The SPAN element.
13440
* Fired when a THEAD label has a mousedown.
13442
* @event theadLabelMousedownEvent
13443
* @param oArgs.event {HTMLEvent} The event object.
13444
* @param oArgs.target {HTMLElement} The SPAN element.
13448
* Fired when a THEAD label has a mouseup.
13450
* @event theadLabelMouseupEvent
13451
* @param oArgs.event {HTMLEvent} The event object.
13452
* @param oArgs.target {HTMLElement} The SPAN element.
13456
* Fired when a THEAD label has a click.
13458
* @event theadLabelClickEvent
13459
* @param oArgs.event {HTMLEvent} The event object.
13460
* @param oArgs.target {HTMLElement} The SPAN element.
13464
* Fired when a THEAD label has a dblclick.
13466
* @event theadLabelDblclickEvent
13467
* @param oArgs.event {HTMLEvent} The event object.
13468
* @param oArgs.target {HTMLElement} The SPAN element.
13472
* Fired when a column is sorted.
13474
* @event columnSortEvent
13475
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13476
* @param oArgs.dir {String} Sort direction: YAHOO.widget.DataTable.CLASS_ASC
13477
* or YAHOO.widget.DataTable.CLASS_DESC.
13481
* Fired when a column width is set.
13483
* @event columnSetWidthEvent
13484
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13485
* @param oArgs.width {Number} The width in pixels.
13489
* Fired when a column width is unset.
13491
* @event columnUnsetWidthEvent
13492
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13496
* Fired when a column is drag-resized.
13498
* @event columnResizeEvent
13499
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13500
* @param oArgs.target {HTMLElement} The TH element.
13501
* @param oArgs.width {Number} Width in pixels.
13505
* Fired when a Column is moved to a new index.
13507
* @event columnReorderEvent
13508
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13509
* @param oArgs.oldIndex {Number} The previous index position.
13513
* Fired when a column is hidden.
13515
* @event columnHideEvent
13516
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13520
* Fired when a column is shown.
13522
* @event columnShowEvent
13523
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13527
* Fired when a column is selected.
13529
* @event columnSelectEvent
13530
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13534
* Fired when a column is unselected.
13536
* @event columnUnselectEvent
13537
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13540
* Fired when a column is removed.
13542
* @event columnRemoveEvent
13543
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13547
* Fired when a column is inserted.
13549
* @event columnInsertEvent
13550
* @param oArgs.column {YAHOO.widget.Column} The Column instance.
13551
* @param oArgs.index {Number} The index position.
13555
* Fired when a column is highlighted.
13557
* @event columnHighlightEvent
13558
* @param oArgs.column {YAHOO.widget.Column} The highlighted Column.
13562
* Fired when a column is unhighlighted.
13564
* @event columnUnhighlightEvent
13565
* @param oArgs.column {YAHOO.widget.Column} The unhighlighted Column.
13570
* Fired when a row has a mouseover.
13572
* @event rowMouseoverEvent
13573
* @param oArgs.event {HTMLEvent} The event object.
13574
* @param oArgs.target {HTMLElement} The TR element.
13578
* Fired when a row has a mouseout.
13580
* @event rowMouseoutEvent
13581
* @param oArgs.event {HTMLEvent} The event object.
13582
* @param oArgs.target {HTMLElement} The TR element.
13586
* Fired when a row has a mousedown.
13588
* @event rowMousedownEvent
13589
* @param oArgs.event {HTMLEvent} The event object.
13590
* @param oArgs.target {HTMLElement} The TR element.
13594
* Fired when a row has a mouseup.
13596
* @event rowMouseupEvent
13597
* @param oArgs.event {HTMLEvent} The event object.
13598
* @param oArgs.target {HTMLElement} The TR element.
13602
* Fired when a row has a click.
13604
* @event rowClickEvent
13605
* @param oArgs.event {HTMLEvent} The event object.
13606
* @param oArgs.target {HTMLElement} The TR element.
13610
* Fired when a row has a dblclick.
13612
* @event rowDblclickEvent
13613
* @param oArgs.event {HTMLEvent} The event object.
13614
* @param oArgs.target {HTMLElement} The TR element.
13618
* Fired when a row is added.
13620
* @event rowAddEvent
13621
* @param oArgs.record {YAHOO.widget.Record} The added Record.
13625
* Fired when rows are added.
13627
* @event rowsAddEvent
13628
* @param oArgs.record {YAHOO.widget.Record[]} The added Records.
13632
* Fired when a row is updated.
13634
* @event rowUpdateEvent
13635
* @param oArgs.record {YAHOO.widget.Record} The updated Record.
13636
* @param oArgs.oldData {Object} Object literal of the old data.
13640
* Fired when a row is deleted.
13642
* @event rowDeleteEvent
13643
* @param oArgs.oldData {Object} Object literal of the deleted data.
13644
* @param oArgs.recordIndex {Number} Index of the deleted Record.
13645
* @param oArgs.trElIndex {Number} Index of the deleted TR element, if on current page.
13649
* Fired when rows are deleted.
13651
* @event rowsDeleteEvent
13652
* @param oArgs.oldData {Object[]} Array of object literals of the deleted data.
13653
* @param oArgs.recordIndex {Number} Index of the first deleted Record.
13654
* @param oArgs.count {Number} Number of deleted Records.
13658
* Fired when a row is selected.
13660
* @event rowSelectEvent
13661
* @param oArgs.el {HTMLElement} The selected TR element, if applicable.
13662
* @param oArgs.record {YAHOO.widget.Record} The selected Record.
13666
* Fired when a row is unselected.
13668
* @event rowUnselectEvent
13669
* @param oArgs.el {HTMLElement} The unselected TR element, if applicable.
13670
* @param oArgs.record {YAHOO.widget.Record} The unselected Record.
13674
* Fired when all row selections are cleared.
13676
* @event unselectAllRowsEvent
13680
* Fired when a row is highlighted.
13682
* @event rowHighlightEvent
13683
* @param oArgs.el {HTMLElement} The highlighted TR element.
13684
* @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
13688
* Fired when a row is unhighlighted.
13690
* @event rowUnhighlightEvent
13691
* @param oArgs.el {HTMLElement} The highlighted TR element.
13692
* @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
13696
* Fired when a cell is updated.
13698
* @event cellUpdateEvent
13699
* @param oArgs.record {YAHOO.widget.Record} The updated Record.
13700
* @param oArgs.column {YAHOO.widget.Column} The updated Column.
13701
* @param oArgs.oldData {Object} Original data value of the updated cell.
13705
* Fired when a cell has a mouseover.
13707
* @event cellMouseoverEvent
13708
* @param oArgs.event {HTMLEvent} The event object.
13709
* @param oArgs.target {HTMLElement} The TD element.
13713
* Fired when a cell has a mouseout.
13715
* @event cellMouseoutEvent
13716
* @param oArgs.event {HTMLEvent} The event object.
13717
* @param oArgs.target {HTMLElement} The TD element.
13721
* Fired when a cell has a mousedown.
13723
* @event cellMousedownEvent
13724
* @param oArgs.event {HTMLEvent} The event object.
13725
* @param oArgs.target {HTMLElement} The TD element.
13729
* Fired when a cell has a mouseup.
13731
* @event cellMouseupEvent
13732
* @param oArgs.event {HTMLEvent} The event object.
13733
* @param oArgs.target {HTMLElement} The TD element.
13737
* Fired when a cell has a click.
13739
* @event cellClickEvent
13740
* @param oArgs.event {HTMLEvent} The event object.
13741
* @param oArgs.target {HTMLElement} The TD element.
13745
* Fired when a cell has a dblclick.
13747
* @event cellDblclickEvent
13748
* @param oArgs.event {HTMLEvent} The event object.
13749
* @param oArgs.target {HTMLElement} The TD element.
13753
* Fired when a cell is formatted.
13755
* @event cellFormatEvent
13756
* @param oArgs.el {HTMLElement} The formatted TD element.
13757
* @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13758
* @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13759
* @param oArgs.key {String} (deprecated) The key of the formatted cell.
13763
* Fired when a cell is selected.
13765
* @event cellSelectEvent
13766
* @param oArgs.el {HTMLElement} The selected TD element.
13767
* @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13768
* @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13769
* @param oArgs.key {String} (deprecated) The key of the selected cell.
13773
* Fired when a cell is unselected.
13775
* @event cellUnselectEvent
13776
* @param oArgs.el {HTMLElement} The unselected TD element.
13777
* @param oArgs.record {YAHOO.widget.Record} The associated Record.
13778
* @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13779
* @param oArgs.key {String} (deprecated) The key of the unselected cell.
13784
* Fired when a cell is highlighted.
13786
* @event cellHighlightEvent
13787
* @param oArgs.el {HTMLElement} The highlighted TD element.
13788
* @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13789
* @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13790
* @param oArgs.key {String} (deprecated) The key of the highlighted cell.
13795
* Fired when a cell is unhighlighted.
13797
* @event cellUnhighlightEvent
13798
* @param oArgs.el {HTMLElement} The unhighlighted TD element.
13799
* @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
13800
* @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
13801
* @param oArgs.key {String} (deprecated) The key of the unhighlighted cell.
13806
* Fired when all cell selections are cleared.
13808
* @event unselectAllCellsEvent
13812
* Fired when a CellEditor is shown.
13814
* @event editorShowEvent
13815
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13819
* Fired when a CellEditor has a keydown.
13821
* @event editorKeydownEvent
13822
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13823
* @param oArgs.event {HTMLEvent} The event object.
13827
* Fired when a CellEditor input is reverted.
13829
* @event editorRevertEvent
13830
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13831
* @param oArgs.newData {Object} New data value from form input field.
13832
* @param oArgs.oldData {Object} Old data value.
13836
* Fired when a CellEditor input is saved.
13838
* @event editorSaveEvent
13839
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13840
* @param oArgs.newData {Object} New data value from form input field.
13841
* @param oArgs.oldData {Object} Old data value.
13845
* Fired when a CellEditor input is canceled.
13847
* @event editorCancelEvent
13848
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13852
* Fired when a CellEditor has a blur event.
13854
* @event editorBlurEvent
13855
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13859
* Fired when a CellEditor is blocked.
13861
* @event editorBlockEvent
13862
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13866
* Fired when a CellEditor is unblocked.
13868
* @event editorUnblockEvent
13869
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
13877
* Fired when a link is clicked.
13879
* @event linkClickEvent
13880
* @param oArgs.event {HTMLEvent} The event object.
13881
* @param oArgs.target {HTMLElement} The A element.
13885
* Fired when a BUTTON element or INPUT element of type "button", "image",
13886
* "submit", "reset" is clicked.
13888
* @event buttonClickEvent
13889
* @param oArgs.event {HTMLEvent} The event object.
13890
* @param oArgs.target {HTMLElement} The BUTTON element.
13894
* Fired when a CHECKBOX element is clicked.
13896
* @event checkboxClickEvent
13897
* @param oArgs.event {HTMLEvent} The event object.
13898
* @param oArgs.target {HTMLElement} The CHECKBOX element.
13902
* Fired when a SELECT element is changed.
13904
* @event dropdownChangeEvent
13905
* @param oArgs.event {HTMLEvent} The event object.
13906
* @param oArgs.target {HTMLElement} The SELECT element.
13910
* Fired when a RADIO element is clicked.
13912
* @event radioClickEvent
13913
* @param oArgs.event {HTMLEvent} The event object.
13914
* @param oArgs.target {HTMLElement} The RADIO element.
13942
/////////////////////////////////////////////////////////////////////////////
13946
/////////////////////////////////////////////////////////////////////////////
13949
* @method showCellEditorBtns
13950
* @deprecated Use CellEditor.renderBtns()
13952
showCellEditorBtns : function(elContainer) {
13954
var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
13955
Dom.addClass(elBtnsDiv, DT.CLASS_BUTTON);
13958
var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
13959
Dom.addClass(elSaveBtn, DT.CLASS_DEFAULT);
13960
elSaveBtn.innerHTML = "OK";
13961
Ev.addListener(elSaveBtn, "click", function(oArgs, oSelf) {
13962
oSelf.onEventSaveCellEditor(oArgs, oSelf);
13963
oSelf.focusTbodyEl();
13967
var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
13968
elCancelBtn.innerHTML = "Cancel";
13969
Ev.addListener(elCancelBtn, "click", function(oArgs, oSelf) {
13970
oSelf.onEventCancelCellEditor(oArgs, oSelf);
13971
oSelf.focusTbodyEl();
13974
YAHOO.log("The method showCellEditorBtns() has been deprecated." +
13975
" Please use the CellEditor class.", "warn", this.toString());
13979
* @method resetCellEditor
13980
* @deprecated Use destroyCellEditor
13982
resetCellEditor : function() {
13983
var elContainer = this._oCellEditor.container;
13984
elContainer.style.display = "none";
13985
Ev.purgeElement(elContainer, true);
13986
elContainer.innerHTML = "";
13987
this._oCellEditor.value = null;
13988
this._oCellEditor.isActive = false;
13990
YAHOO.log("The method resetCellEditor() has been deprecated." +
13991
" Please use the CellEditor class.", "warn", this.toString());
13995
* @event editorUpdateEvent
13996
* @deprecated Use CellEditor class.
14001
* @deprecated Use getTbodyEl().
14003
getBody : function() {
14004
// Backward compatibility
14005
YAHOO.log("The method getBody() has been deprecated" +
14006
" in favor of getTbodyEl()", "warn", this.toString());
14007
return this.getTbodyEl();
14012
* @deprecated Use getTdEl().
14014
getCell : function(index) {
14015
// Backward compatibility
14016
YAHOO.log("The method getCell() has been deprecated" +
14017
" in favor of getTdEl()", "warn", this.toString());
14018
return this.getTdEl(index);
14023
* @deprecated Use getTrEl().
14025
getRow : function(index) {
14026
// Backward compatibility
14027
YAHOO.log("The method getRow() has been deprecated" +
14028
" in favor of getTrEl()", "warn", this.toString());
14029
return this.getTrEl(index);
14033
* @method refreshView
14034
* @deprecated Use render.
14036
refreshView : function() {
14037
// Backward compatibility
14038
YAHOO.log("The method refreshView() has been deprecated" +
14039
" in favor of render()", "warn", this.toString());
14045
* @deprecated Use selectRow.
14047
select : function(els) {
14048
// Backward compatibility
14049
YAHOO.log("The method select() has been deprecated" +
14050
" in favor of selectRow()", "warn", this.toString());
14051
if(!lang.isArray(els)) {
14054
for(var i=0; i<els.length; i++) {
14055
this.selectRow(els[i]);
14060
* @method onEventEditCell
14061
* @deprecated Use onEventShowCellEditor.
14063
onEventEditCell : function(oArgs) {
14064
// Backward compatibility
14065
YAHOO.log("The method onEventEditCell() has been deprecated" +
14066
" in favor of onEventShowCellEditor()", "warn", this.toString());
14067
this.onEventShowCellEditor(oArgs);
14071
* @method _syncColWidths
14072
* @deprecated Use validateColumnWidths.
14074
_syncColWidths : function() {
14075
// Backward compatibility
14076
YAHOO.log("The method _syncColWidths() has been deprecated" +
14077
" in favor of validateColumnWidths()", "warn", this.toString());
14078
this.validateColumnWidths();
14082
* @event headerRowMouseoverEvent
14083
* @deprecated Use theadRowMouseoverEvent.
14087
* @event headerRowMouseoutEvent
14088
* @deprecated Use theadRowMouseoutEvent.
14092
* @event headerRowMousedownEvent
14093
* @deprecated Use theadRowMousedownEvent.
14097
* @event headerRowClickEvent
14098
* @deprecated Use theadRowClickEvent.
14102
* @event headerRowDblclickEvent
14103
* @deprecated Use theadRowDblclickEvent.
14107
* @event headerCellMouseoverEvent
14108
* @deprecated Use theadCellMouseoverEvent.
14112
* @event headerCellMouseoutEvent
14113
* @deprecated Use theadCellMouseoutEvent.
14117
* @event headerCellMousedownEvent
14118
* @deprecated Use theadCellMousedownEvent.
14122
* @event headerCellClickEvent
14123
* @deprecated Use theadCellClickEvent.
14127
* @event headerCellDblclickEvent
14128
* @deprecated Use theadCellDblclickEvent.
14132
* @event headerLabelMouseoverEvent
14133
* @deprecated Use theadLabelMouseoverEvent.
14137
* @event headerLabelMouseoutEvent
14138
* @deprecated Use theadLabelMouseoutEvent.
14142
* @event headerLabelMousedownEvent
14143
* @deprecated Use theadLabelMousedownEvent.
14147
* @event headerLabelClickEvent
14148
* @deprecated Use theadLabelClickEvent.
14152
* @event headerLabelDbllickEvent
14153
* @deprecated Use theadLabelDblclickEvent.
14159
* Alias for onDataReturnSetRows for backward compatibility
14160
* @method onDataReturnSetRecords
14161
* @deprecated Use onDataReturnSetRows
14163
DT.prototype.onDataReturnSetRecords = DT.prototype.onDataReturnSetRows;
14166
* Alias for onPaginatorChange for backward compatibility
14167
* @method onPaginatorChange
14168
* @deprecated Use onPaginatorChangeRequest
14170
DT.prototype.onPaginatorChange = DT.prototype.onPaginatorChangeRequest;
14172
/////////////////////////////////////////////////////////////////////////////
14174
// Deprecated static APIs
14176
/////////////////////////////////////////////////////////////////////////////
14178
* @method DataTable.formatTheadCell
14179
* @deprecated Use formatTheadCell.
14181
DT.formatTheadCell = function() {};
14184
* @method DataTable.editCheckbox
14185
* @deprecated Use YAHOO.widget.CheckboxCellEditor.
14187
DT.editCheckbox = function() {};
14190
* @method DataTable.editDate
14191
* @deprecated Use YAHOO.widget.DateCellEditor.
14193
DT.editDate = function() {};
14196
* @method DataTable.editDropdown
14197
* @deprecated Use YAHOO.widget.DropdownCellEditor.
14199
DT.editDropdown = function() {};
14202
* @method DataTable.editRadio
14203
* @deprecated Use YAHOO.widget.RadioCellEditor.
14205
DT.editRadio = function() {};
14208
* @method DataTable.editTextarea
14209
* @deprecated Use YAHOO.widget.TextareaCellEditor
14211
DT.editTextarea = function() {};
14214
* @method DataTable.editTextbox
14215
* @deprecated Use YAHOO.widget.TextboxCellEditor
14217
DT.editTextbox= function() {};
14223
var lang = YAHOO.lang,
14225
widget = YAHOO.widget,
14230
DS = util.DataSourceBase,
14231
DT = widget.DataTable,
14232
Pag = widget.Paginator;
14235
* The ScrollingDataTable class extends the DataTable class to provide
14236
* functionality for x-scrolling, y-scrolling, and xy-scrolling.
14238
* @namespace YAHOO.widget
14239
* @class ScrollingDataTable
14240
* @extends YAHOO.widget.DataTable
14242
* @param elContainer {HTMLElement} Container element for the TABLE.
14243
* @param aColumnDefs {Object[]} Array of object literal Column definitions.
14244
* @param oDataSource {YAHOO.util.DataSource} DataSource instance.
14245
* @param oConfigs {object} (optional) Object literal of configuration values.
14247
widget.ScrollingDataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
14248
oConfigs = oConfigs || {};
14250
// Prevent infinite loop
14251
if(oConfigs.scrollable) {
14252
oConfigs.scrollable = false;
14255
widget.ScrollingDataTable.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs);
14257
// Once per instance
14258
this.subscribe("columnShowEvent", this._onColumnChange);
14261
var SDT = widget.ScrollingDataTable;
14263
/////////////////////////////////////////////////////////////////////////////
14265
// Public constants
14267
/////////////////////////////////////////////////////////////////////////////
14268
lang.augmentObject(SDT, {
14271
* Class name assigned to inner DataTable header container.
14273
* @property DataTable.CLASS_HEADER
14277
* @default "yui-dt-hd"
14279
CLASS_HEADER : "yui-dt-hd",
14282
* Class name assigned to inner DataTable body container.
14284
* @property DataTable.CLASS_BODY
14288
* @default "yui-dt-bd"
14290
CLASS_BODY : "yui-dt-bd"
14293
lang.extend(SDT, DT, {
14296
* Container for fixed header TABLE element.
14298
* @property _elHdContainer
14299
* @type HTMLElement
14302
_elHdContainer : null,
14305
* Fixed header TABLE element.
14307
* @property _elHdTable
14308
* @type HTMLElement
14314
* Container for scrolling body TABLE element.
14316
* @property _elBdContainer
14317
* @type HTMLElement
14320
_elBdContainer : null,
14323
* Body THEAD element.
14325
* @property _elBdThead
14326
* @type HTMLElement
14332
* Offscreen container to temporarily clone SDT for auto-width calculation.
14334
* @property _elTmpContainer
14335
* @type HTMLElement
14338
_elTmpContainer : null,
14341
* Offscreen TABLE element for auto-width calculation.
14343
* @property _elTmpTable
14344
* @type HTMLElement
14347
_elTmpTable : null,
14350
* True if x-scrollbar is currently visible.
14351
* @property _bScrollbarX
14355
_bScrollbarX : null,
14371
/////////////////////////////////////////////////////////////////////////////
14373
// Superclass methods
14375
/////////////////////////////////////////////////////////////////////////////
14378
* Implementation of Element's abstract method. Sets up config values.
14380
* @method initAttributes
14381
* @param oConfigs {Object} (Optional) Object literal definition of configuration values.
14385
initAttributes : function(oConfigs) {
14386
oConfigs = oConfigs || {};
14387
SDT.superclass.initAttributes.call(this, oConfigs);
14391
* @description Table width for scrollable tables (e.g., "40em").
14394
this.setAttributeConfig("width", {
14396
validator: lang.isString,
14397
method: function(oParam) {
14398
if(this._elHdContainer && this._elBdContainer) {
14399
this._elHdContainer.style.width = oParam;
14400
this._elBdContainer.style.width = oParam;
14401
this._syncScrollX();
14402
this._syncScrollOverhang();
14408
* @attribute height
14409
* @description Table body height for scrollable tables, not including headers (e.g., "40em").
14412
this.setAttributeConfig("height", {
14414
validator: lang.isString,
14415
method: function(oParam) {
14416
if(this._elHdContainer && this._elBdContainer) {
14417
this._elBdContainer.style.height = oParam;
14418
this._syncScrollX();
14419
this._syncScrollY();
14420
this._syncScrollOverhang();
14426
* @attribute COLOR_COLUMNFILLER
14427
* @description CSS color value assigned to header filler on scrollable tables.
14429
* @default "#F2F2F2"
14431
this.setAttributeConfig("COLOR_COLUMNFILLER", {
14433
validator: lang.isString,
14434
method: function(oParam) {
14435
this._elHdContainer.style.backgroundColor = oParam;
14441
* Initializes DOM elements for a ScrollingDataTable, including creation of
14442
* two separate TABLE elements.
14444
* @method _initDomElements
14445
* @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
14446
* return {Boolean} False in case of error, otherwise true
14449
_initDomElements : function(elContainer) {
14450
// Outer and inner containers
14451
this._initContainerEl(elContainer);
14452
if(this._elContainer && this._elHdContainer && this._elBdContainer) {
14454
this._initTableEl();
14456
if(this._elHdTable && this._elTable) {
14458
///this._initColgroupEl(this._elHdTable, this._elTable);
14459
this._initColgroupEl(this._elHdTable);
14462
this._initTheadEl(this._elHdTable, this._elTable);
14465
this._initTbodyEl(this._elTable);
14467
this._initMsgTbodyEl(this._elTable);
14470
if(!this._elContainer || !this._elTable || !this._elColgroup || !this._elThead || !this._elTbody || !this._elMsgTbody ||
14471
!this._elHdTable || !this._elBdThead) {
14472
YAHOO.log("Could not instantiate DataTable due to an invalid DOM elements", "error", this.toString());
14481
* Destroy's the DataTable outer and inner container elements, if available.
14483
* @method _destroyContainerEl
14484
* @param elContainer {HTMLElement} Reference to the container element.
14487
_destroyContainerEl : function(elContainer) {
14488
Dom.removeClass(elContainer, DT.CLASS_SCROLLABLE);
14489
SDT.superclass._destroyContainerEl.call(this, elContainer);
14490
this._elHdContainer = null;
14491
this._elBdContainer = null;
14495
* Initializes the DataTable outer container element and creates inner header
14496
* and body container elements.
14498
* @method _initContainerEl
14499
* @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
14502
_initContainerEl : function(elContainer) {
14503
SDT.superclass._initContainerEl.call(this, elContainer);
14505
if(this._elContainer) {
14506
elContainer = this._elContainer; // was constructor input, now is DOM ref
14507
Dom.addClass(elContainer, DT.CLASS_SCROLLABLE);
14509
// Container for header TABLE
14510
var elHdContainer = document.createElement("div");
14511
elHdContainer.style.width = this.get("width") || "";
14512
elHdContainer.style.backgroundColor = this.get("COLOR_COLUMNFILLER");
14513
Dom.addClass(elHdContainer, SDT.CLASS_HEADER);
14514
this._elHdContainer = elHdContainer;
14515
elContainer.appendChild(elHdContainer);
14517
// Container for body TABLE
14518
var elBdContainer = document.createElement("div");
14519
elBdContainer.style.width = this.get("width") || "";
14520
elBdContainer.style.height = this.get("height") || "";
14521
Dom.addClass(elBdContainer, SDT.CLASS_BODY);
14522
Ev.addListener(elBdContainer, "scroll", this._onScroll, this); // to sync horiz scroll headers
14523
this._elBdContainer = elBdContainer;
14524
elContainer.appendChild(elBdContainer);
14529
* Creates HTML markup CAPTION element.
14531
* @method _initCaptionEl
14532
* @param sCaption {String} Text for caption.
14535
_initCaptionEl : function(sCaption) {
14536
// Not yet supported
14537
/*if(this._elHdTable && sCaption) {
14538
// Create CAPTION element
14539
if(!this._elCaption) {
14540
this._elCaption = this._elHdTable.createCaption();
14542
// Set CAPTION value
14543
this._elCaption.innerHTML = sCaption;
14545
else if(this._elCaption) {
14546
this._elCaption.parentNode.removeChild(this._elCaption);
14551
* Destroy's the DataTable head TABLE element, if available.
14553
* @method _destroyHdTableEl
14556
_destroyHdTableEl : function() {
14557
var elTable = this._elHdTable;
14559
Ev.purgeElement(elTable, true);
14560
elTable.parentNode.removeChild(elTable);
14562
// A little out of place, but where else can we null out these extra elements?
14563
///this._elBdColgroup = null;
14564
this._elBdThead = null;
14569
* Initializes ScrollingDataTable TABLE elements into the two inner containers.
14571
* @method _initTableEl
14574
_initTableEl : function() {
14576
if(this._elHdContainer) {
14577
this._destroyHdTableEl();
14580
this._elHdTable = this._elHdContainer.appendChild(document.createElement("table"));
14583
SDT.superclass._initTableEl.call(this, this._elBdContainer);
14587
* Initializes ScrollingDataTable THEAD elements into the two inner containers.
14589
* @method _initTheadEl
14590
* @param elHdTable {HTMLElement} (optional) Fixed header TABLE element reference.
14591
* @param elTable {HTMLElement} (optional) TABLE element reference.
14594
_initTheadEl : function(elHdTable, elTable) {
14595
elHdTable = elHdTable || this._elHdTable;
14596
elTable = elTable || this._elTable;
14598
// Scrolling body's THEAD
14599
this._initBdTheadEl(elTable);
14600
// Standard fixed head THEAD
14601
SDT.superclass._initTheadEl.call(this, elHdTable);
14605
* SDT changes ID so as not to duplicate the accessibility TH IDs.
14607
* @method _initThEl
14608
* @param elTh {HTMLElement} TH element reference.
14609
* @param oColumn {YAHOO.widget.Column} Column object.
14612
_initThEl : function(elTh, oColumn) {
14613
SDT.superclass._initThEl.call(this, elTh, oColumn);
14614
elTh.id = this.getId() +"-fixedth-" + oColumn.getSanitizedKey(); // Needed for getColumn by TH and ColumnDD
14618
* Destroy's the DataTable body THEAD element, if available.
14620
* @method _destroyBdTheadEl
14623
_destroyBdTheadEl : function() {
14624
var elBdThead = this._elBdThead;
14626
var elTable = elBdThead.parentNode;
14627
Ev.purgeElement(elBdThead, true);
14628
elTable.removeChild(elBdThead);
14629
this._elBdThead = null;
14631
this._destroyColumnHelpers();
14636
* Initializes body THEAD element.
14638
* @method _initBdTheadEl
14639
* @param elTable {HTMLElement} TABLE element into which to create THEAD.
14640
* @return {HTMLElement} Initialized THEAD element.
14643
_initBdTheadEl : function(elTable) {
14645
// Destroy previous
14646
this._destroyBdTheadEl();
14648
var elThead = elTable.insertBefore(document.createElement("thead"), elTable.firstChild);
14650
// Add TRs to the THEAD;
14651
var oColumnSet = this._oColumnSet,
14652
colTree = oColumnSet.tree,
14653
elTh, elTheadTr, oColumn, i, j, k, len;
14655
for(i=0, k=colTree.length; i<k; i++) {
14656
elTheadTr = elThead.appendChild(document.createElement("tr"));
14658
// ...and create TH cells
14659
for(j=0, len=colTree[i].length; j<len; j++) {
14660
oColumn = colTree[i][j];
14661
elTh = elTheadTr.appendChild(document.createElement("th"));
14662
this._initBdThEl(elTh,oColumn,i,j);
14665
this._elBdThead = elThead;
14666
YAHOO.log("Accessibility TH cells for " + this._oColumnSet.keys.length + " keys created","info",this.toString());
14671
* Populates TH element for the body THEAD element.
14673
* @method _initBdThEl
14674
* @param elTh {HTMLElement} TH element reference.
14675
* @param oColumn {YAHOO.widget.Column} Column object.
14678
_initBdThEl : function(elTh, oColumn) {
14679
elTh.id = this.getId()+"-th-" + oColumn.getSanitizedKey(); // Needed for accessibility
14680
elTh.rowSpan = oColumn.getRowspan();
14681
elTh.colSpan = oColumn.getColspan();
14682
// Assign abbr attribute
14684
elTh.abbr = oColumn.abbr;
14687
// TODO: strip links and form elements
14688
var sKey = oColumn.getKey();
14689
var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
14690
elTh.innerHTML = sLabel;
14694
* Initializes ScrollingDataTable TBODY element for data
14696
* @method _initTbodyEl
14697
* @param elTable {HTMLElement} TABLE element into which to create TBODY .
14700
_initTbodyEl : function(elTable) {
14701
SDT.superclass._initTbodyEl.call(this, elTable);
14703
// Bug 2105534 - Safari 3 gap
14704
// Bug 2492591 - IE8 offsetTop
14705
elTable.style.marginTop = (this._elTbody.offsetTop > 0) ?
14706
"-"+this._elTbody.offsetTop+"px" : 0;
14738
* Sets focus on the given element.
14741
* @param el {HTMLElement} Element.
14744
_focusEl : function(el) {
14745
el = el || this._elTbody;
14747
this._storeScrollPositions();
14748
// http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
14749
// The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
14750
// strange unexpected things as the user clicks on buttons and other controls.
14752
// Bug 1921135: Wrap the whole thing in a setTimeout
14753
setTimeout(function() {
14754
setTimeout(function() {
14757
oSelf._restoreScrollPositions();
14784
* Internal wrapper calls run() on render Chain instance.
14786
* @method _runRenderChain
14789
_runRenderChain : function() {
14790
this._storeScrollPositions();
14791
this._oChainRender.run();
14795
* Stores scroll positions so they can be restored after a render.
14797
* @method _storeScrollPositions
14800
_storeScrollPositions : function() {
14801
this._nScrollTop = this._elBdContainer.scrollTop;
14802
this._nScrollLeft = this._elBdContainer.scrollLeft;
14806
* Restores scroll positions to stored value.
14808
* @method _retoreScrollPositions
14811
_restoreScrollPositions : function() {
14812
// Reset scroll positions
14813
if(this._nScrollTop) {
14814
this._elBdContainer.scrollTop = this._nScrollTop;
14815
this._nScrollTop = null;
14817
if(this._nScrollLeft) {
14818
this._elBdContainer.scrollLeft = this._nScrollLeft;
14819
this._nScrollLeft = null;
14824
* Helper function calculates and sets a validated width for a Column in a ScrollingDataTable.
14826
* @method _validateColumnWidth
14827
* @param oColumn {YAHOO.widget.Column} Column instance.
14828
* @param elTd {HTMLElement} TD element to validate against.
14831
_validateColumnWidth : function(oColumn, elTd) {
14832
// Only Columns without widths that are not hidden
14833
if(!oColumn.width && !oColumn.hidden) {
14834
var elTh = oColumn.getThEl();
14835
// Unset a calculated auto-width
14836
if(oColumn._calculatedWidth) {
14837
this._setColumnWidth(oColumn, "auto", "visible");
14839
// Compare auto-widths
14840
if(elTh.offsetWidth !== elTd.offsetWidth) {
14841
var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
14842
oColumn.getThLinerEl() : elTd.firstChild;
14844
// Grab the wider liner width, unless the minWidth is wider
14845
var newWidth = Math.max(0,
14846
(elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
14849
var sOverflow = 'visible';
14851
// Now validate against maxAutoWidth
14852
if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
14853
newWidth = oColumn.maxAutoWidth;
14854
sOverflow = "hidden";
14857
// Set to the wider auto-width
14858
this._elTbody.style.display = "none";
14859
this._setColumnWidth(oColumn, newWidth+'px', sOverflow);
14860
oColumn._calculatedWidth = newWidth;
14861
this._elTbody.style.display = "";
14867
* For one or all Columns of a ScrollingDataTable, when Column is not hidden,
14868
* and width is not set, syncs widths of header and body cells and
14869
* validates that width against minWidth and/or maxAutoWidth as necessary.
14871
* @method validateColumnWidths
14872
* @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
14874
validateColumnWidths : function(oColumn) {
14875
// Validate there is at least one TR with proper TDs
14876
var allKeys = this._oColumnSet.keys,
14877
allKeysLength = allKeys.length,
14878
elRow = this.getFirstTrEl();
14880
// Reset overhang for IE
14882
this._setOverhangValue(1);
14885
if(allKeys && elRow && (elRow.childNodes.length === allKeysLength)) {
14886
// Temporarily unsnap container since it causes inaccurate calculations
14887
var sWidth = this.get("width");
14889
this._elHdContainer.style.width = "";
14890
this._elBdContainer.style.width = "";
14892
this._elContainer.style.width = "";
14894
//Validate just one Column
14895
if(oColumn && lang.isNumber(oColumn.getKeyIndex())) {
14896
this._validateColumnWidth(oColumn, elRow.childNodes[oColumn.getKeyIndex()]);
14898
// Iterate through all Columns to unset calculated widths in one pass
14900
var elTd, todos = [], thisTodo, i, len;
14901
for(i=0; i<allKeysLength; i++) {
14902
oColumn = allKeys[i];
14903
// Only Columns without widths that are not hidden, unset a calculated auto-width
14904
if(!oColumn.width && !oColumn.hidden && oColumn._calculatedWidth) {
14905
todos[todos.length] = oColumn;
14909
this._elTbody.style.display = "none";
14910
for(i=0, len=todos.length; i<len; i++) {
14911
this._setColumnWidth(todos[i], "auto", "visible");
14913
this._elTbody.style.display = "";
14917
// Iterate through all Columns and make the store the adjustments to make in one pass
14918
for(i=0; i<allKeysLength; i++) {
14919
oColumn = allKeys[i];
14920
elTd = elRow.childNodes[i];
14921
// Only Columns without widths that are not hidden
14922
if(!oColumn.width && !oColumn.hidden) {
14923
var elTh = oColumn.getThEl();
14925
// Compare auto-widths
14926
if(elTh.offsetWidth !== elTd.offsetWidth) {
14927
var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
14928
oColumn.getThLinerEl() : elTd.firstChild;
14930
// Grab the wider liner width, unless the minWidth is wider
14931
var newWidth = Math.max(0,
14932
(elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
14935
var sOverflow = 'visible';
14937
// Now validate against maxAutoWidth
14938
if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
14939
newWidth = oColumn.maxAutoWidth;
14940
sOverflow = "hidden";
14943
todos[todos.length] = [oColumn, newWidth, sOverflow];
14948
this._elTbody.style.display = "none";
14949
for(i=0, len=todos.length; i<len; i++) {
14950
thisTodo = todos[i];
14951
// Set to the wider auto-width
14952
this._setColumnWidth(thisTodo[0], thisTodo[1]+"px", thisTodo[2]);
14953
thisTodo[0]._calculatedWidth = thisTodo[1];
14955
this._elTbody.style.display = "";
14958
// Resnap unsnapped containers
14960
this._elHdContainer.style.width = sWidth;
14961
this._elBdContainer.style.width = sWidth;
14965
this._syncScroll();
14966
this._restoreScrollPositions();
14970
* Syncs padding around scrollable tables, including Column header right-padding
14971
* and container width and height.
14973
* @method _syncScroll
14976
_syncScroll : function() {
14977
this._syncScrollX();
14978
this._syncScrollY();
14979
this._syncScrollOverhang();
14982
this._elHdContainer.scrollLeft = this._elBdContainer.scrollLeft;
14983
if(!this.get("width")) {
14985
document.body.style += '';
14991
* Snaps container width for y-scrolling tables.
14993
* @method _syncScrollY
14996
_syncScrollY : function() {
14997
var elTbody = this._elTbody,
14998
elBdContainer = this._elBdContainer;
15000
// X-scrolling not enabled
15001
if(!this.get("width")) {
15002
// Snap outer container width to content
15003
this._elContainer.style.width =
15004
(elBdContainer.scrollHeight > elBdContainer.clientHeight) ?
15005
// but account for y-scrollbar since it is visible
15006
(elTbody.parentNode.clientWidth + 19) + "px" :
15007
// no y-scrollbar, just borders
15008
(elTbody.parentNode.clientWidth + 2) + "px";
15013
* Snaps container height for x-scrolling tables in IE. Syncs message TBODY width.
15015
* @method _syncScrollX
15018
_syncScrollX : function() {
15019
var elTbody = this._elTbody,
15020
elBdContainer = this._elBdContainer;
15022
// IE 6 and 7 only when y-scrolling not enabled
15023
if(!this.get("height") && (ua.ie)) {
15024
// Snap outer container height to content
15025
elBdContainer.style.height =
15026
// but account for x-scrollbar if it is visible
15027
(elBdContainer.scrollWidth > elBdContainer.offsetWidth ) ?
15028
(elTbody.parentNode.offsetHeight + 18) + "px" :
15029
elTbody.parentNode.offsetHeight + "px";
15032
// Sync message tbody
15033
if(this._elTbody.rows.length === 0) {
15034
this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
15037
this._elMsgTbody.parentNode.style.width = "";
15042
* Adds/removes Column header overhang as necesary.
15044
* @method _syncScrollOverhang
15047
_syncScrollOverhang : function() {
15048
var elBdContainer = this._elBdContainer,
15049
// Overhang should be either 1 (default) or 18px, depending on the location of the right edge of the table
15052
// Y-scrollbar is visible, which is when the overhang needs to jut out
15053
if((elBdContainer.scrollHeight > elBdContainer.clientHeight) &&
15054
// X-scrollbar is also visible, which means the right is jagged, not flush with the Column
15055
(elBdContainer.scrollWidth > elBdContainer.clientWidth)) {
15059
this._setOverhangValue(nPadding);
15064
* Sets Column header overhang to given width.
15066
* @method _setOverhangValue
15067
* @param nBorderWidth {Number} Value of new border for overhang.
15070
_setOverhangValue : function(nBorderWidth) {
15071
var aLastHeaders = this._oColumnSet.headers[this._oColumnSet.headers.length-1] || [],
15072
len = aLastHeaders.length,
15073
sPrefix = this._sId+"-fixedth-",
15074
sValue = nBorderWidth + "px solid " + this.get("COLOR_COLUMNFILLER");
15076
this._elThead.style.display = "none";
15077
for(var i=0; i<len; i++) {
15078
Dom.get(sPrefix+aLastHeaders[i]).style.borderRight = sValue;
15080
this._elThead.style.display = "";
15121
* Returns DOM reference to the DataTable's fixed header container element.
15123
* @method getHdContainerEl
15124
* @return {HTMLElement} Reference to DIV element.
15126
getHdContainerEl : function() {
15127
return this._elHdContainer;
15131
* Returns DOM reference to the DataTable's scrolling body container element.
15133
* @method getBdContainerEl
15134
* @return {HTMLElement} Reference to DIV element.
15136
getBdContainerEl : function() {
15137
return this._elBdContainer;
15141
* Returns DOM reference to the DataTable's fixed header TABLE element.
15143
* @method getHdTableEl
15144
* @return {HTMLElement} Reference to TABLE element.
15146
getHdTableEl : function() {
15147
return this._elHdTable;
15151
* Returns DOM reference to the DataTable's scrolling body TABLE element.
15153
* @method getBdTableEl
15154
* @return {HTMLElement} Reference to TABLE element.
15156
getBdTableEl : function() {
15157
return this._elTable;
15161
* Disables ScrollingDataTable UI.
15165
disable : function() {
15166
var elMask = this._elMask;
15167
elMask.style.width = this._elBdContainer.offsetWidth + "px";
15168
elMask.style.height = this._elHdContainer.offsetHeight + this._elBdContainer.offsetHeight + "px";
15169
elMask.style.display = "";
15170
this.fireEvent("disableEvent");
15174
* Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
15175
* non-nested Columns, and top-level parent Columns (which will remove all
15176
* children Columns).
15178
* @method removeColumn
15179
* @param oColumn {YAHOO.widget.Column} Column instance.
15180
* @return oColumn {YAHOO.widget.Column} Removed Column instance.
15182
removeColumn : function(oColumn) {
15183
// Store scroll pos
15184
var hdPos = this._elHdContainer.scrollLeft;
15185
var bdPos = this._elBdContainer.scrollLeft;
15187
// Call superclass method
15188
oColumn = SDT.superclass.removeColumn.call(this, oColumn);
15190
// Restore scroll pos
15191
this._elHdContainer.scrollLeft = hdPos;
15192
this._elBdContainer.scrollLeft = bdPos;
15198
* Inserts given Column at the index if given, otherwise at the end. NOTE: You
15199
* can only add non-nested Columns and top-level parent Columns. You cannot add
15200
* a nested Column to an existing parent.
15202
* @method insertColumn
15203
* @param oColumn {Object | YAHOO.widget.Column} Object literal Column
15204
* definition or a Column instance.
15205
* @param index {Number} (optional) New tree index.
15206
* @return oColumn {YAHOO.widget.Column} Inserted Column instance.
15208
insertColumn : function(oColumn, index) {
15209
// Store scroll pos
15210
var hdPos = this._elHdContainer.scrollLeft;
15211
var bdPos = this._elBdContainer.scrollLeft;
15213
// Call superclass method
15214
var oNewColumn = SDT.superclass.insertColumn.call(this, oColumn, index);
15216
// Restore scroll pos
15217
this._elHdContainer.scrollLeft = hdPos;
15218
this._elBdContainer.scrollLeft = bdPos;
15224
* Removes given Column and inserts into given tree index. NOTE: You
15225
* can only reorder non-nested Columns and top-level parent Columns. You cannot
15226
* reorder a nested Column to an existing parent.
15228
* @method reorderColumn
15229
* @param oColumn {YAHOO.widget.Column} Column instance.
15230
* @param index {Number} New tree index.
15232
reorderColumn : function(oColumn, index) {
15233
// Store scroll pos
15234
var hdPos = this._elHdContainer.scrollLeft;
15235
var bdPos = this._elBdContainer.scrollLeft;
15237
// Call superclass method
15238
var oNewColumn = SDT.superclass.reorderColumn.call(this, oColumn, index);
15240
// Restore scroll pos
15241
this._elHdContainer.scrollLeft = hdPos;
15242
this._elBdContainer.scrollLeft = bdPos;
15248
* Sets given Column to given pixel width. If new width is less than minWidth
15249
* width, sets to minWidth. Updates oColumn.width value.
15251
* @method setColumnWidth
15252
* @param oColumn {YAHOO.widget.Column} Column instance.
15253
* @param nWidth {Number} New width in pixels.
15255
setColumnWidth : function(oColumn, nWidth) {
15256
oColumn = this.getColumn(oColumn);
15258
// Validate new width against minWidth
15259
if(lang.isNumber(nWidth)) {
15260
nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
15263
oColumn.width = nWidth;
15265
// Resize the DOM elements
15266
this._setColumnWidth(oColumn, nWidth+"px");
15267
this._syncScroll();
15269
this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
15270
YAHOO.log("Set width of Column " + oColumn + " to " + nWidth + "px", "info", this.toString());
15272
// Unsets a width to auto-size
15273
else if(nWidth === null) {
15275
oColumn.width = nWidth;
15277
// Resize the DOM elements
15278
this._setColumnWidth(oColumn, "auto");
15279
this.validateColumnWidths(oColumn);
15280
this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
15281
YAHOO.log("Column " + oColumn + " width unset", "info", this.toString());
15284
// Bug 2339454: resize then sort misaligment
15285
this._clearTrTemplateEl();
15288
YAHOO.log("Could not set width of Column " + oColumn + " to " + nWidth + "px", "warn", this.toString());
15293
* Displays message within secondary TBODY.
15295
* @method showTableMessage
15296
* @param sHTML {String} (optional) Value for innerHTMlang.
15297
* @param sClassName {String} (optional) Classname.
15299
showTableMessage : function(sHTML, sClassName) {
15300
var elCell = this._elMsgTd;
15301
if(lang.isString(sHTML)) {
15302
elCell.firstChild.innerHTML = sHTML;
15304
if(lang.isString(sClassName)) {
15305
Dom.addClass(elCell.firstChild, sClassName);
15308
// Needed for SDT only
15309
var elThead = this.getTheadEl();
15310
var elTable = elThead.parentNode;
15311
var newWidth = elTable.offsetWidth;
15312
this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
15314
this._elMsgTbody.style.display = "";
15316
this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
15317
YAHOO.log("DataTable showing message: " + sHTML, "info", this.toString());
15332
/////////////////////////////////////////////////////////////////////////////
15334
// Private Custom Event Handlers
15336
/////////////////////////////////////////////////////////////////////////////
15339
* Handles Column mutations
15341
* @method onColumnChange
15342
* @param oArgs {Object} Custom Event data.
15344
_onColumnChange : function(oArg) {
15345
// Figure out which Column changed
15346
var oColumn = (oArg.column) ? oArg.column :
15347
(oArg.editor) ? oArg.editor.column : null;
15348
this._storeScrollPositions();
15349
this.validateColumnWidths(oColumn);
15366
/////////////////////////////////////////////////////////////////////////////
15368
// Private DOM Event Handlers
15370
/////////////////////////////////////////////////////////////////////////////
15373
* Syncs scrolltop and scrollleft of all TABLEs.
15375
* @method _onScroll
15376
* @param e {HTMLEvent} The scroll event.
15377
* @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
15380
_onScroll : function(e, oSelf) {
15381
oSelf._elHdContainer.scrollLeft = oSelf._elBdContainer.scrollLeft;
15383
if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
15384
oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
15385
oSelf.cancelCellEditor();
15388
var elTarget = Ev.getTarget(e);
15389
var elTag = elTarget.nodeName.toLowerCase();
15390
oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget});
15394
* Handles keydown events on the THEAD element.
15396
* @method _onTheadKeydown
15397
* @param e {HTMLEvent} The key event.
15398
* @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
15401
_onTheadKeydown : function(e, oSelf) {
15402
// If tabbing to next TH label link causes THEAD to scroll,
15403
// need to sync scrollLeft with TBODY
15404
if(Ev.getCharCode(e) === 9) {
15405
setTimeout(function() {
15406
if((oSelf instanceof SDT) && oSelf._sId) {
15407
oSelf._elBdContainer.scrollLeft = oSelf._elHdContainer.scrollLeft;
15412
var elTarget = Ev.getTarget(e);
15413
var elTag = elTarget.nodeName.toLowerCase();
15414
var bKeepBubbling = true;
15415
while(elTarget && (elTag != "table")) {
15421
// TODO: implement textareaKeyEvent
15424
bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
15429
if(bKeepBubbling === false) {
15433
elTarget = elTarget.parentNode;
15435
elTag = elTarget.nodeName.toLowerCase();
15439
oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
15446
* Fired when a fixed scrolling DataTable has a scroll.
15448
* @event tableScrollEvent
15449
* @param oArgs.event {HTMLEvent} The event object.
15450
* @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE)
15451
* or the DataTable's TBODY element (everyone else).
15464
var lang = YAHOO.lang,
15466
widget = YAHOO.widget,
15472
DT = widget.DataTable;
15473
/****************************************************************************/
15474
/****************************************************************************/
15475
/****************************************************************************/
15478
* The BaseCellEditor class provides base functionality common to all inline cell
15479
* editors for a DataTable widget.
15481
* @namespace YAHOO.widget
15482
* @class BaseCellEditor
15483
* @uses YAHOO.util.EventProvider
15485
* @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
15486
* @param oConfigs {Object} (Optional) Object literal of configs.
15488
widget.BaseCellEditor = function(sType, oConfigs) {
15489
this._sId = this._sId || "yui-ceditor" + YAHOO.widget.BaseCellEditor._nCount++;
15490
this._sType = sType;
15493
this._initConfigs(oConfigs);
15495
// Create Custom Events
15496
this._initEvents();
15502
var BCE = widget.BaseCellEditor;
15504
/////////////////////////////////////////////////////////////////////////////
15508
/////////////////////////////////////////////////////////////////////////////
15509
lang.augmentObject(BCE, {
15512
* Global instance counter.
15514
* @property CellEditor._nCount
15523
* Class applied to CellEditor container.
15525
* @property CellEditor.CLASS_CELLEDITOR
15528
* @default "yui-ceditor"
15530
CLASS_CELLEDITOR : "yui-ceditor"
15535
/////////////////////////////////////////////////////////////////////////////
15539
/////////////////////////////////////////////////////////////////////////////
15541
* Unique id assigned to instance "yui-ceditorN", useful prefix for generating unique
15542
* DOM ID strings and log messages.
15560
* DataTable instance.
15562
* @property _oDataTable
15563
* @type YAHOO.widget.DataTable
15566
_oDataTable : null,
15571
* @property _oColumn
15572
* @type YAHOO.widget.Column
15580
* @property _oRecord
15581
* @type YAHOO.widget.Record
15591
* @type HTMLElement
15598
* Container for inline editor.
15600
* @property _elContainer
15601
* @type HTMLElement
15604
_elContainer : null,
15607
* Reference to Cancel button, if available.
15609
* @property _elCancelBtn
15610
* @type HTMLElement
15614
_elCancelBtn : null,
15617
* Reference to Save button, if available.
15619
* @property _elSaveBtn
15620
* @type HTMLElement
15633
/////////////////////////////////////////////////////////////////////////////
15637
/////////////////////////////////////////////////////////////////////////////
15640
* Initialize configs.
15642
* @method _initConfigs
15645
_initConfigs : function(oConfigs) {
15646
// Object literal defines CellEditor configs
15647
if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
15648
for(var sConfig in oConfigs) {
15650
this[sConfig] = oConfigs[sConfig];
15657
* Initialize Custom Events.
15659
* @method _initEvents
15662
_initEvents : function() {
15663
this.createEvent("showEvent");
15664
this.createEvent("keydownEvent");
15665
this.createEvent("invalidDataEvent");
15666
this.createEvent("revertEvent");
15667
this.createEvent("saveEvent");
15668
this.createEvent("cancelEvent");
15669
this.createEvent("blurEvent");
15670
this.createEvent("blockEvent");
15671
this.createEvent("unblockEvent");
15686
/////////////////////////////////////////////////////////////////////////////
15688
// Public properties
15690
/////////////////////////////////////////////////////////////////////////////
15692
* Implementer defined function that can submit the input value to a server. This
15693
* function must accept the arguments fnCallback and oNewValue. When the submission
15694
* is complete, the function must also call fnCallback(bSuccess, oNewValue) to
15695
* finish the save routine in the CellEditor. This function can also be used to
15696
* perform extra validation or input value manipulation.
15698
* @property asyncSubmitter
15699
* @type HTMLFunction
15701
asyncSubmitter : null,
15712
* Default value in case Record data is undefined. NB: Null values will not trigger
15713
* the default value.
15715
* @property defaultValue
15719
defaultValue : null,
15722
* Validator function for input data, called from the DataTable instance scope,
15723
* receives the arguments (inputValue, currentValue, editorInstance) and returns
15724
* either the validated (or type-converted) value or undefined.
15726
* @property validator
15727
* @type HTMLFunction
15733
* If validation is enabled, resets input field of invalid data.
15735
* @property resetInvalidData
15739
resetInvalidData : true,
15742
* True if currently active.
15744
* @property isActive
15750
* Text to display on Save button.
15752
* @property LABEL_SAVE
15756
LABEL_SAVE : "Save",
15759
* Text to display on Cancel button.
15761
* @property LABEL_CANCEL
15763
* @default "Cancel"
15765
LABEL_CANCEL : "Cancel",
15768
* True if Save/Cancel buttons should not be displayed in the CellEditor.
15770
* @property disableBtns
15774
disableBtns : false,
15782
/////////////////////////////////////////////////////////////////////////////
15786
/////////////////////////////////////////////////////////////////////////////
15788
* CellEditor instance name, for logging.
15791
* @return {String} Unique name of the CellEditor instance.
15794
toString : function() {
15795
return "CellEditor instance " + this._sId;
15799
* CellEditor unique ID.
15802
* @return {String} Unique ID of the CellEditor instance.
15805
getId : function() {
15810
* Returns reference to associated DataTable instance.
15812
* @method getDataTable
15813
* @return {YAHOO.widget.DataTable} DataTable instance.
15816
getDataTable : function() {
15817
return this._oDataTable;
15821
* Returns reference to associated Column instance.
15823
* @method getColumn
15824
* @return {YAHOO.widget.Column} Column instance.
15827
getColumn : function() {
15828
return this._oColumn;
15832
* Returns reference to associated Record instance.
15834
* @method getRecord
15835
* @return {YAHOO.widget.Record} Record instance.
15838
getRecord : function() {
15839
return this._oRecord;
15845
* Returns reference to associated TD element.
15848
* @return {HTMLElement} TD element.
15851
getTdEl : function() {
15856
* Returns container element.
15858
* @method getContainerEl
15859
* @return {HTMLElement} Reference to container element.
15862
getContainerEl : function() {
15863
return this._elContainer;
15867
* Nulls out the entire CellEditor instance and related objects, removes attached
15868
* event listeners, and clears out DOM elements inside the container, removes
15869
* container from the DOM.
15873
destroy : function() {
15874
this.unsubscribeAll();
15876
// Column is late-binding in attach()
15877
var oColumn = this.getColumn();
15879
oColumn.editor = null;
15882
var elContainer = this.getContainerEl();
15883
Ev.purgeElement(elContainer, true);
15884
elContainer.parentNode.removeChild(elContainer);
15888
* Renders DOM elements and attaches event listeners.
15892
render : function() {
15893
if(this._elContainer) {
15894
YAHOO.util.Event.purgeElement(this._elContainer, true);
15895
this._elContainer.innerHTML = "";
15898
// Render Cell Editor container element as first child of body
15899
var elContainer = document.createElement("div");
15900
elContainer.id = this.getId() + "-container"; // Needed for tracking blur event
15901
elContainer.style.display = "none";
15902
elContainer.tabIndex = 0;
15903
elContainer.className = DT.CLASS_EDITOR;
15904
document.body.insertBefore(elContainer, document.body.firstChild);
15905
this._elContainer = elContainer;
15908
Ev.addListener(elContainer, "keydown", function(e, oSelf) {
15909
// ESC cancels Cell Editor
15910
if((e.keyCode == 27)) {
15911
var target = Ev.getTarget(e);
15912
// workaround for Mac FF3 bug that disabled clicks when ESC hit when
15913
// select is open. [bug 2273056]
15914
if (target.nodeName && target.nodeName.toLowerCase() === 'select') {
15919
// Pass through event
15920
oSelf.fireEvent("keydownEvent", {editor:this, event:e});
15925
// Show Save/Cancel buttons
15926
if(!this.disableBtns) {
15930
this.doAfterRender();
15934
* Renders Save/Cancel buttons.
15936
* @method renderBtns
15938
renderBtns : function() {
15940
var elBtnsDiv = this.getContainerEl().appendChild(document.createElement("div"));
15941
elBtnsDiv.className = DT.CLASS_BUTTON;
15944
var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
15945
elSaveBtn.className = DT.CLASS_DEFAULT;
15946
elSaveBtn.innerHTML = this.LABEL_SAVE;
15947
Ev.addListener(elSaveBtn, "click", function(oArgs) {
15950
this._elSaveBtn = elSaveBtn;
15953
var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
15954
elCancelBtn.innerHTML = this.LABEL_CANCEL;
15955
Ev.addListener(elCancelBtn, "click", function(oArgs) {
15958
this._elCancelBtn = elCancelBtn;
15962
* Attach CellEditor for a new interaction.
15965
* @param oDataTable {YAHOO.widget.DataTable} Associated DataTable instance.
15966
* @param elCell {HTMLElement} Cell to edit.
15968
attach : function(oDataTable, elCell) {
15970
if(oDataTable instanceof YAHOO.widget.DataTable) {
15971
this._oDataTable = oDataTable;
15974
elCell = oDataTable.getTdEl(elCell);
15976
this._elTd = elCell;
15979
var oColumn = oDataTable.getColumn(elCell);
15981
this._oColumn = oColumn;
15984
var oRecord = oDataTable.getRecord(elCell);
15986
this._oRecord = oRecord;
15987
var value = oRecord.getData(this.getColumn().getKey());
15988
this.value = (value !== undefined) ? value : this.defaultValue;
15994
YAHOO.log("Could not attach CellEditor","error",this.toString());
15999
* Moves container into position for display.
16003
move : function() {
16005
var elContainer = this.getContainerEl(),
16006
elTd = this.getTdEl(),
16007
x = Dom.getX(elTd),
16008
y = Dom.getY(elTd);
16010
//TODO: remove scrolling logic
16011
// SF doesn't get xy for cells in scrolling table
16012
// when tbody display is set to block
16013
if(isNaN(x) || isNaN(y)) {
16014
var elTbody = this.getDataTable().getTbodyEl();
16015
x = elTd.offsetLeft + // cell pos relative to table
16016
Dom.getX(elTbody.parentNode) - // plus table pos relative to document
16017
elTbody.scrollLeft; // minus tbody scroll
16018
y = elTd.offsetTop + // cell pos relative to table
16019
Dom.getY(elTbody.parentNode) - // plus table pos relative to document
16020
elTbody.scrollTop + // minus tbody scroll
16021
this.getDataTable().getTheadEl().offsetHeight; // account for fixed THEAD cells
16024
elContainer.style.left = x + "px";
16025
elContainer.style.top = y + "px";
16029
* Displays CellEditor UI in the correct position.
16033
show : function() {
16035
this.isActive = true;
16036
this.getContainerEl().style.display = "";
16038
this.fireEvent("showEvent", {editor:this});
16039
YAHOO.log("CellEditor shown", "info", this.toString());
16047
block : function() {
16048
this.fireEvent("blockEvent", {editor:this});
16049
YAHOO.log("CellEditor blocked", "info", this.toString());
16053
* Fires unblockEvent
16057
unblock : function() {
16058
this.fireEvent("unblockEvent", {editor:this});
16059
YAHOO.log("CellEditor unblocked", "info", this.toString());
16063
* Saves value of CellEditor and hides UI.
16067
save : function() {
16069
var inputValue = this.getInputValue();
16070
var validValue = inputValue;
16072
// Validate new value
16073
if(this.validator) {
16074
validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this);
16075
if(validValue === undefined ) {
16076
if(this.resetInvalidData) {
16079
this.fireEvent("invalidDataEvent",
16080
{editor:this, oldData:this.value, newData:inputValue});
16081
YAHOO.log("Could not save Cell Editor input due to invalid data " +
16082
lang.dump(inputValue), "warn", this.toString());
16088
var finishSave = function(bSuccess, oNewValue) {
16089
var oOrigValue = oSelf.value;
16091
// Update new value
16092
oSelf.value = oNewValue;
16093
oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue);
16096
oSelf.getContainerEl().style.display = "none";
16097
oSelf.isActive = false;
16098
oSelf.getDataTable()._oCellEditor = null;
16100
oSelf.fireEvent("saveEvent",
16101
{editor:oSelf, oldData:oOrigValue, newData:oSelf.value});
16102
YAHOO.log("Cell Editor input saved", "info", this.toString());
16106
oSelf.fireEvent("revertEvent",
16107
{editor:oSelf, oldData:oOrigValue, newData:oNewValue});
16108
YAHOO.log("Could not save Cell Editor input " +
16109
lang.dump(oNewValue), "warn", oSelf.toString());
16115
if(lang.isFunction(this.asyncSubmitter)) {
16116
this.asyncSubmitter.call(this, finishSave, validValue);
16119
finishSave(true, validValue);
16124
* Cancels CellEditor input and hides UI.
16128
cancel : function() {
16129
if(this.isActive) {
16130
this.getContainerEl().style.display = "none";
16131
this.isActive = false;
16132
this.getDataTable()._oCellEditor = null;
16133
this.fireEvent("cancelEvent", {editor:this});
16134
YAHOO.log("CellEditor canceled", "info", this.toString());
16137
YAHOO.log("Unable to cancel CellEditor", "warn", this.toString());
16142
* Renders form elements.
16144
* @method renderForm
16146
renderForm : function() {
16147
// To be implemented by subclass
16151
* Access to add additional event listeners.
16153
* @method doAfterRender
16155
doAfterRender : function() {
16156
// To be implemented by subclass
16161
* After rendering form, if disabledBtns is set to true, then sets up a mechanism
16162
* to save input without them.
16164
* @method handleDisabledBtns
16166
handleDisabledBtns : function() {
16167
// To be implemented by subclass
16171
* Resets CellEditor UI to initial state.
16173
* @method resetForm
16175
resetForm : function() {
16176
// To be implemented by subclass
16180
* Sets focus in CellEditor.
16184
focus : function() {
16185
// To be implemented by subclass
16189
* Retrieves input value from CellEditor.
16191
* @method getInputValue
16193
getInputValue : function() {
16194
// To be implemented by subclass
16199
lang.augmentProto(BCE, util.EventProvider);
16202
/////////////////////////////////////////////////////////////////////////////
16206
/////////////////////////////////////////////////////////////////////////////
16209
* Fired when a CellEditor is shown.
16212
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16216
* Fired when a CellEditor has a keydown.
16218
* @event keydownEvent
16219
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16220
* @param oArgs.event {HTMLEvent} The event object.
16224
* Fired when a CellEditor input is reverted due to invalid data.
16226
* @event invalidDataEvent
16227
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16228
* @param oArgs.newData {Object} New data value from form input field.
16229
* @param oArgs.oldData {Object} Old data value.
16233
* Fired when a CellEditor input is reverted due to asyncSubmitter failure.
16235
* @event revertEvent
16236
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16237
* @param oArgs.newData {Object} New data value from form input field.
16238
* @param oArgs.oldData {Object} Old data value.
16242
* Fired when a CellEditor input is saved.
16245
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16246
* @param oArgs.newData {Object} New data value from form input field.
16247
* @param oArgs.oldData {Object} Old data value.
16251
* Fired when a CellEditor input is canceled.
16253
* @event cancelEvent
16254
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16258
* Fired when a CellEditor has a blur event.
16261
* @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
16277
/****************************************************************************/
16278
/****************************************************************************/
16279
/****************************************************************************/
16282
* The CheckboxCellEditor class provides functionality for inline editing
16283
* DataTable cell data with checkboxes.
16285
* @namespace YAHOO.widget
16286
* @class CheckboxCellEditor
16287
* @extends YAHOO.widget.BaseCellEditor
16289
* @param oConfigs {Object} (Optional) Object literal of configs.
16291
widget.CheckboxCellEditor = function(oConfigs) {
16292
this._sId = "yui-checkboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16293
widget.CheckboxCellEditor.superclass.constructor.call(this, "checkbox", oConfigs);
16296
// CheckboxCellEditor extends BaseCellEditor
16297
lang.extend(widget.CheckboxCellEditor, BCE, {
16299
/////////////////////////////////////////////////////////////////////////////
16301
// CheckboxCellEditor public properties
16303
/////////////////////////////////////////////////////////////////////////////
16305
* Array of checkbox values. Can either be a simple array (e.g., ["red","green","blue"])
16306
* or a an array of objects (e.g., [{label:"red", value:"#FF0000"},
16307
* {label:"green", value:"#00FF00"}, {label:"blue", value:"#0000FF"}]).
16309
* @property checkboxOptions
16310
* @type String[] | Object[]
16312
checkboxOptions : null,
16315
* Reference to the checkbox elements.
16317
* @property checkboxes
16318
* @type HTMLElement[]
16323
* Array of checked values
16330
/////////////////////////////////////////////////////////////////////////////
16332
// CheckboxCellEditor public methods
16334
/////////////////////////////////////////////////////////////////////////////
16337
* Render a form with input(s) type=checkbox.
16339
* @method renderForm
16341
renderForm : function() {
16342
if(lang.isArray(this.checkboxOptions)) {
16343
var checkboxOption, checkboxValue, checkboxId, elLabel, j, len;
16345
// Create the checkbox buttons in an IE-friendly way...
16346
for(j=0,len=this.checkboxOptions.length; j<len; j++) {
16347
checkboxOption = this.checkboxOptions[j];
16348
checkboxValue = lang.isValue(checkboxOption.value) ?
16349
checkboxOption.value : checkboxOption;
16351
checkboxId = this.getId() + "-chk" + j;
16352
this.getContainerEl().innerHTML += "<input type=\"checkbox\"" +
16353
" id=\"" + checkboxId + "\"" + // Needed for label
16354
" value=\"" + checkboxValue + "\" />";
16356
// Create the labels in an IE-friendly way
16357
elLabel = this.getContainerEl().appendChild(document.createElement("label"));
16358
elLabel.htmlFor = checkboxId;
16359
elLabel.innerHTML = lang.isValue(checkboxOption.label) ?
16360
checkboxOption.label : checkboxOption;
16363
// Store the reference to the checkbox elements
16364
var allCheckboxes = [];
16365
for(j=0; j<len; j++) {
16366
allCheckboxes[allCheckboxes.length] = this.getContainerEl().childNodes[j*2];
16368
this.checkboxes = allCheckboxes;
16370
if(this.disableBtns) {
16371
this.handleDisabledBtns();
16375
YAHOO.log("Could not find checkboxOptions", "error", this.toString());
16380
* After rendering form, if disabledBtns is set to true, then sets up a mechanism
16381
* to save input without them.
16383
* @method handleDisabledBtns
16385
handleDisabledBtns : function() {
16386
Ev.addListener(this.getContainerEl(), "click", function(v){
16387
if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
16395
* Resets CheckboxCellEditor UI to initial state.
16397
* @method resetForm
16399
resetForm : function() {
16400
// Normalize to array
16401
var originalValues = lang.isArray(this.value) ? this.value : [this.value];
16403
// Match checks to value
16404
for(var i=0, j=this.checkboxes.length; i<j; i++) {
16405
this.checkboxes[i].checked = false;
16406
for(var k=0, len=originalValues.length; k<len; k++) {
16407
if(this.checkboxes[i].value === originalValues[k]) {
16408
this.checkboxes[i].checked = true;
16415
* Sets focus in CheckboxCellEditor.
16419
focus : function() {
16420
this.checkboxes[0].focus();
16424
* Retrieves input value from CheckboxCellEditor.
16426
* @method getInputValue
16428
getInputValue : function() {
16429
var checkedValues = [];
16430
for(var i=0, j=this.checkboxes.length; i<j; i++) {
16431
if(this.checkboxes[i].checked) {
16432
checkedValues[checkedValues.length] = this.checkboxes[i].value;
16435
return checkedValues;
16440
// Copy static members to CheckboxCellEditor class
16441
lang.augmentObject(widget.CheckboxCellEditor, BCE);
16450
/****************************************************************************/
16451
/****************************************************************************/
16452
/****************************************************************************/
16455
* The DataCellEditor class provides functionality for inline editing
16456
* DataTable cell data with a YUI Calendar.
16458
* @namespace YAHOO.widget
16459
* @class DateCellEditor
16460
* @extends YAHOO.widget.BaseCellEditor
16462
* @param oConfigs {Object} (Optional) Object literal of configs.
16464
widget.DateCellEditor = function(oConfigs) {
16465
this._sId = "yui-dateceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16466
widget.DateCellEditor.superclass.constructor.call(this, "date", oConfigs);
16469
// CheckboxCellEditor extends BaseCellEditor
16470
lang.extend(widget.DateCellEditor, BCE, {
16472
/////////////////////////////////////////////////////////////////////////////
16474
// DateCellEditor public properties
16476
/////////////////////////////////////////////////////////////////////////////
16478
* Reference to Calendar instance.
16480
* @property calendar
16481
* @type YAHOO.widget.Calendar
16486
* Configs for the calendar instance, to be passed to Calendar constructor.
16488
* @property calendarOptions
16491
calendarOptions : null,
16496
* @property defaultValue
16498
* @default new Date()
16500
defaultValue : new Date(),
16503
/////////////////////////////////////////////////////////////////////////////
16505
// DateCellEditor public methods
16507
/////////////////////////////////////////////////////////////////////////////
16510
* Render a Calendar.
16512
* @method renderForm
16514
renderForm : function() {
16516
if(YAHOO.widget.Calendar) {
16517
var calContainer = this.getContainerEl().appendChild(document.createElement("div"));
16518
calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor
16520
new YAHOO.widget.Calendar(this.getId() + "-date",
16521
calContainer.id, this.calendarOptions);
16523
calContainer.style.cssFloat = "none";
16526
var calFloatClearer = this.getContainerEl().appendChild(document.createElement("div"));
16527
calFloatClearer.style.clear = "both";
16530
this.calendar = calendar;
16532
if(this.disableBtns) {
16533
this.handleDisabledBtns();
16537
YAHOO.log("Could not find YUI Calendar", "error", this.toString());
16543
* After rendering form, if disabledBtns is set to true, then sets up a mechanism
16544
* to save input without them.
16546
* @method handleDisabledBtns
16548
handleDisabledBtns : function() {
16549
this.calendar.selectEvent.subscribe(function(v){
16556
* Resets DateCellEditor UI to initial state.
16558
* @method resetForm
16560
resetForm : function() {
16561
var value = this.value;
16562
var selectedValue = (value.getMonth()+1)+"/"+value.getDate()+"/"+value.getFullYear();
16563
this.calendar.cfg.setProperty("selected",selectedValue,false);
16564
this.calendar.render();
16568
* Sets focus in DateCellEditor.
16572
focus : function() {
16573
// To be impmlemented by subclass
16577
* Retrieves input value from DateCellEditor.
16579
* @method getInputValue
16581
getInputValue : function() {
16582
return this.calendar.getSelectedDates()[0];
16587
// Copy static members to DateCellEditor class
16588
lang.augmentObject(widget.DateCellEditor, BCE);
16598
/****************************************************************************/
16599
/****************************************************************************/
16600
/****************************************************************************/
16603
* The DropdownCellEditor class provides functionality for inline editing
16604
* DataTable cell data a SELECT element.
16606
* @namespace YAHOO.widget
16607
* @class DropdownCellEditor
16608
* @extends YAHOO.widget.BaseCellEditor
16610
* @param oConfigs {Object} (Optional) Object literal of configs.
16612
widget.DropdownCellEditor = function(oConfigs) {
16613
this._sId = "yui-dropdownceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16614
widget.DropdownCellEditor.superclass.constructor.call(this, "dropdown", oConfigs);
16617
// DropdownCellEditor extends BaseCellEditor
16618
lang.extend(widget.DropdownCellEditor, BCE, {
16620
/////////////////////////////////////////////////////////////////////////////
16622
// DropdownCellEditor public properties
16624
/////////////////////////////////////////////////////////////////////////////
16626
* Array of dropdown values. Can either be a simple array (e.g.,
16627
* ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g.,
16628
* [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"},
16629
* {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]).
16631
* @property dropdownOptions
16632
* @type String[] | Object[]
16634
dropdownOptions : null,
16637
* Reference to Dropdown element.
16639
* @property dropdown
16640
* @type HTMLElement
16645
/////////////////////////////////////////////////////////////////////////////
16647
// DropdownCellEditor public methods
16649
/////////////////////////////////////////////////////////////////////////////
16652
* Render a form with select element.
16654
* @method renderForm
16656
renderForm : function() {
16657
var elDropdown = this.getContainerEl().appendChild(document.createElement("select"));
16658
elDropdown.style.zoom = 1;
16659
this.dropdown = elDropdown;
16661
if(lang.isArray(this.dropdownOptions)) {
16662
var dropdownOption, elOption;
16663
for(var i=0, j=this.dropdownOptions.length; i<j; i++) {
16664
dropdownOption = this.dropdownOptions[i];
16665
elOption = document.createElement("option");
16666
elOption.value = (lang.isValue(dropdownOption.value)) ?
16667
dropdownOption.value : dropdownOption;
16668
elOption.innerHTML = (lang.isValue(dropdownOption.label)) ?
16669
dropdownOption.label : dropdownOption;
16670
elOption = elDropdown.appendChild(elOption);
16673
if(this.disableBtns) {
16674
this.handleDisabledBtns();
16680
* After rendering form, if disabledBtns is set to true, then sets up a mechanism
16681
* to save input without them.
16683
* @method handleDisabledBtns
16685
handleDisabledBtns : function() {
16686
Ev.addListener(this.dropdown, "change", function(v){
16693
* Resets DropdownCellEditor UI to initial state.
16695
* @method resetForm
16697
resetForm : function() {
16698
for(var i=0, j=this.dropdown.options.length; i<j; i++) {
16699
if(this.value === this.dropdown.options[i].value) {
16700
this.dropdown.options[i].selected = true;
16706
* Sets focus in DropdownCellEditor.
16710
focus : function() {
16711
this.getDataTable()._focusEl(this.dropdown);
16715
* Retrieves input value from DropdownCellEditor.
16717
* @method getInputValue
16719
getInputValue : function() {
16720
return this.dropdown.options[this.dropdown.options.selectedIndex].value;
16725
// Copy static members to DropdownCellEditor class
16726
lang.augmentObject(widget.DropdownCellEditor, BCE);
16733
/****************************************************************************/
16734
/****************************************************************************/
16735
/****************************************************************************/
16738
* The RadioCellEditor class provides functionality for inline editing
16739
* DataTable cell data with radio buttons.
16741
* @namespace YAHOO.widget
16742
* @class RadioCellEditor
16743
* @extends YAHOO.widget.BaseCellEditor
16745
* @param oConfigs {Object} (Optional) Object literal of configs.
16747
widget.RadioCellEditor = function(oConfigs) {
16748
this._sId = "yui-radioceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16749
widget.RadioCellEditor.superclass.constructor.call(this, "radio", oConfigs);
16752
// RadioCellEditor extends BaseCellEditor
16753
lang.extend(widget.RadioCellEditor, BCE, {
16755
/////////////////////////////////////////////////////////////////////////////
16757
// RadioCellEditor public properties
16759
/////////////////////////////////////////////////////////////////////////////
16761
* Reference to radio elements.
16764
* @type HTMLElement[]
16769
* Array of radio values. Can either be a simple array (e.g., ["yes","no","maybe"])
16770
* or a an array of objects (e.g., [{label:"yes", value:1}, {label:"no", value:-1},
16771
* {label:"maybe", value:0}]).
16773
* @property radioOptions
16774
* @type String[] | Object[]
16776
radioOptions : null,
16778
/////////////////////////////////////////////////////////////////////////////
16780
// RadioCellEditor public methods
16782
/////////////////////////////////////////////////////////////////////////////
16785
* Render a form with input(s) type=radio.
16787
* @method renderForm
16789
renderForm : function() {
16790
if(lang.isArray(this.radioOptions)) {
16791
var radioOption, radioValue, radioId, elLabel;
16793
// Create the radio buttons in an IE-friendly way
16794
for(var i=0, len=this.radioOptions.length; i<len; i++) {
16795
radioOption = this.radioOptions[i];
16796
radioValue = lang.isValue(radioOption.value) ?
16797
radioOption.value : radioOption;
16798
radioId = this.getId() + "-radio" + i;
16799
this.getContainerEl().innerHTML += "<input type=\"radio\"" +
16800
" name=\"" + this.getId() + "\"" +
16801
" value=\"" + radioValue + "\"" +
16802
" id=\"" + radioId + "\" />"; // Needed for label
16804
// Create the labels in an IE-friendly way
16805
elLabel = this.getContainerEl().appendChild(document.createElement("label"));
16806
elLabel.htmlFor = radioId;
16807
elLabel.innerHTML = (lang.isValue(radioOption.label)) ?
16808
radioOption.label : radioOption;
16811
// Store the reference to the checkbox elements
16812
var allRadios = [],
16814
for(var j=0; j<len; j++) {
16815
elRadio = this.getContainerEl().childNodes[j*2];
16816
allRadios[allRadios.length] = elRadio;
16818
this.radios = allRadios;
16820
if(this.disableBtns) {
16821
this.handleDisabledBtns();
16825
YAHOO.log("Could not find radioOptions", "error", this.toString());
16830
* After rendering form, if disabledBtns is set to true, then sets up a mechanism
16831
* to save input without them.
16833
* @method handleDisabledBtns
16835
handleDisabledBtns : function() {
16836
Ev.addListener(this.getContainerEl(), "click", function(v){
16837
if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
16845
* Resets RadioCellEditor UI to initial state.
16847
* @method resetForm
16849
resetForm : function() {
16850
for(var i=0, j=this.radios.length; i<j; i++) {
16851
var elRadio = this.radios[i];
16852
if(this.value === elRadio.value) {
16853
elRadio.checked = true;
16860
* Sets focus in RadioCellEditor.
16864
focus : function() {
16865
for(var i=0, j=this.radios.length; i<j; i++) {
16866
if(this.radios[i].checked) {
16867
this.radios[i].focus();
16874
* Retrieves input value from RadioCellEditor.
16876
* @method getInputValue
16878
getInputValue : function() {
16879
for(var i=0, j=this.radios.length; i<j; i++) {
16880
if(this.radios[i].checked) {
16881
return this.radios[i].value;
16888
// Copy static members to RadioCellEditor class
16889
lang.augmentObject(widget.RadioCellEditor, BCE);
16896
/****************************************************************************/
16897
/****************************************************************************/
16898
/****************************************************************************/
16901
* The TextareaCellEditor class provides functionality for inline editing
16902
* DataTable cell data with a TEXTAREA element.
16904
* @namespace YAHOO.widget
16905
* @class TextareaCellEditor
16906
* @extends YAHOO.widget.BaseCellEditor
16908
* @param oConfigs {Object} (Optional) Object literal of configs.
16910
widget.TextareaCellEditor = function(oConfigs) {
16911
this._sId = "yui-textareaceditor" + YAHOO.widget.BaseCellEditor._nCount++;
16912
widget.TextareaCellEditor.superclass.constructor.call(this, "textarea", oConfigs);
16915
// TextareaCellEditor extends BaseCellEditor
16916
lang.extend(widget.TextareaCellEditor, BCE, {
16918
/////////////////////////////////////////////////////////////////////////////
16920
// TextareaCellEditor public properties
16922
/////////////////////////////////////////////////////////////////////////////
16924
* Reference to textarea element.
16926
* @property textarea
16927
* @type HTMLElement
16932
/////////////////////////////////////////////////////////////////////////////
16934
// TextareaCellEditor public methods
16936
/////////////////////////////////////////////////////////////////////////////
16939
* Render a form with textarea.
16941
* @method renderForm
16943
renderForm : function() {
16944
var elTextarea = this.getContainerEl().appendChild(document.createElement("textarea"));
16945
this.textarea = elTextarea;
16947
if(this.disableBtns) {
16948
this.handleDisabledBtns();
16953
* After rendering form, if disabledBtns is set to true, then sets up a mechanism
16954
* to save input without them.
16956
* @method handleDisabledBtns
16958
handleDisabledBtns : function() {
16959
Ev.addListener(this.textarea, "blur", function(v){
16966
* Moves TextareaCellEditor UI to a cell.
16970
move : function() {
16971
this.textarea.style.width = this.getTdEl().offsetWidth + "px";
16972
this.textarea.style.height = "3em";
16973
YAHOO.widget.TextareaCellEditor.superclass.move.call(this);
16977
* Resets TextareaCellEditor UI to initial state.
16979
* @method resetForm
16981
resetForm : function() {
16982
this.textarea.value = this.value;
16986
* Sets focus in TextareaCellEditor.
16990
focus : function() {
16991
// Bug 2303181, Bug 2263600
16992
this.getDataTable()._focusEl(this.textarea);
16993
this.textarea.select();
16997
* Retrieves input value from TextareaCellEditor.
16999
* @method getInputValue
17001
getInputValue : function() {
17002
return this.textarea.value;
17007
// Copy static members to TextareaCellEditor class
17008
lang.augmentObject(widget.TextareaCellEditor, BCE);
17018
/****************************************************************************/
17019
/****************************************************************************/
17020
/****************************************************************************/
17023
* The TextboxCellEditor class provides functionality for inline editing
17024
* DataTable cell data with an INPUT TYPE=TEXT element.
17026
* @namespace YAHOO.widget
17027
* @class TextboxCellEditor
17028
* @extends YAHOO.widget.BaseCellEditor
17030
* @param oConfigs {Object} (Optional) Object literal of configs.
17032
widget.TextboxCellEditor = function(oConfigs) {
17033
this._sId = "yui-textboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
17034
widget.TextboxCellEditor.superclass.constructor.call(this, "textbox", oConfigs);
17037
// TextboxCellEditor extends BaseCellEditor
17038
lang.extend(widget.TextboxCellEditor, BCE, {
17040
/////////////////////////////////////////////////////////////////////////////
17042
// TextboxCellEditor public properties
17044
/////////////////////////////////////////////////////////////////////////////
17046
* Reference to the textbox element.
17048
* @property textbox
17052
/////////////////////////////////////////////////////////////////////////////
17054
// TextboxCellEditor public methods
17056
/////////////////////////////////////////////////////////////////////////////
17059
* Render a form with input type=text.
17061
* @method renderForm
17063
renderForm : function() {
17065
// Bug 1802582: SF3/Mac needs a form element wrapping the input
17066
if(ua.webkit>420) {
17067
elTextbox = this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input"));
17070
elTextbox = this.getContainerEl().appendChild(document.createElement("input"));
17072
elTextbox.type = "text";
17073
this.textbox = elTextbox;
17075
// Save on enter by default
17076
// Bug: 1802582 Set up a listener on each textbox to track on keypress
17077
// since SF/OP can't preventDefault on keydown
17078
Ev.addListener(elTextbox, "keypress", function(v){
17079
if((v.keyCode === 13)) {
17080
// Prevent form submit
17081
YAHOO.util.Event.preventDefault(v);
17086
if(this.disableBtns) {
17087
// By default this is no-op since enter saves by default
17088
this.handleDisabledBtns();
17093
* Moves TextboxCellEditor UI to a cell.
17097
move : function() {
17098
this.textbox.style.width = this.getTdEl().offsetWidth + "px";
17099
widget.TextboxCellEditor.superclass.move.call(this);
17103
* Resets TextboxCellEditor UI to initial state.
17105
* @method resetForm
17107
resetForm : function() {
17108
this.textbox.value = lang.isValue(this.value) ? this.value.toString() : "";
17112
* Sets focus in TextboxCellEditor.
17116
focus : function() {
17117
// Bug 2303181, Bug 2263600
17118
this.getDataTable()._focusEl(this.textbox);
17119
this.textbox.select();
17123
* Returns new value for TextboxCellEditor.
17125
* @method getInputValue
17127
getInputValue : function() {
17128
return this.textbox.value;
17133
// Copy static members to TextboxCellEditor class
17134
lang.augmentObject(widget.TextboxCellEditor, BCE);
17142
/////////////////////////////////////////////////////////////////////////////
17144
// DataTable extension
17146
/////////////////////////////////////////////////////////////////////////////
17149
* CellEditor subclasses.
17150
* @property DataTable.Editors
17155
checkbox : widget.CheckboxCellEditor,
17156
"date" : widget.DateCellEditor,
17157
dropdown : widget.DropdownCellEditor,
17158
radio : widget.RadioCellEditor,
17159
textarea : widget.TextareaCellEditor,
17160
textbox : widget.TextboxCellEditor
17163
/****************************************************************************/
17164
/****************************************************************************/
17165
/****************************************************************************/
17168
* Factory class for instantiating a BaseCellEditor subclass.
17170
* @namespace YAHOO.widget
17171
* @class CellEditor
17172
* @extends YAHOO.widget.BaseCellEditor
17174
* @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
17175
* @param oConfigs {Object} (Optional) Object literal of configs.
17177
widget.CellEditor = function(sType, oConfigs) {
17178
// Point to one of the subclasses
17179
if(sType && DT.Editors[sType]) {
17180
lang.augmentObject(BCE, DT.Editors[sType]);
17181
return new DT.Editors[sType](oConfigs);
17184
return new BCE(null, oConfigs);
17188
var CE = widget.CellEditor;
17190
// Copy static members to CellEditor class
17191
lang.augmentObject(CE, BCE);
17196
YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.7.0", build: "1799"});