source: Dev/branches/rest-dojo-ui/client/dojox/grid/_Grid.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 41.5 KB
Line 
1define([
2        "dojo/_base/kernel",
3        "../main",
4        "dojo/_base/declare",
5        "./_Events",
6        "./_Scroller",
7        "./_Layout",
8        "./_View",
9        "./_ViewManager",
10        "./_RowManager",
11        "./_FocusManager",
12        "./_EditManager",
13        "./Selection",
14        "./_RowSelector",
15        "./util",
16        "dijit/_Widget",
17        "dijit/_TemplatedMixin",
18        "dijit/CheckedMenuItem",
19        "dojo/text!./resources/_Grid.html",
20        "dojo/string",
21        "dojo/_base/array",
22        "dojo/_base/lang",
23        "dojo/_base/sniff",
24        "dojox/html/metrics",
25        "dojo/_base/html",
26        "dojo/query",
27        "dojo/dnd/common",
28        "dojo/i18n!dijit/nls/loading"
29], function(dojo, dojox, declare, _Events, _Scroller, _Layout, _View, _ViewManager,
30        _RowManager, _FocusManager, _EditManager, Selection, _RowSelector, util, _Widget,
31         _TemplatedMixin, CheckedMenuItem, template, string, array, lang, has, metrics, html, query){
32
33        // NOTE: this is for backwards compatibility with Dojo 1.3
34        if(!dojo.isCopyKey){
35                dojo.isCopyKey = dojo.dnd.getCopyKeyState;
36        }
37        /*=====
38        dojox.grid.__CellDef = function(){
39                //      name: String?
40                //              The text to use in the header of the grid for this cell.
41                //      get: Function?
42                //              function(rowIndex){} rowIndex is of type Integer.  This
43                //              function will be called when a cell     requests data.  Returns the
44                //              unformatted data for the cell.
45                //      value: String?
46                //              If "get" is not specified, this is used as the data for the cell.
47                //      defaultValue: String?
48                //              If "get" and "value" aren't specified or if "get" returns an undefined
49                //              value, this is used as the data for the cell.  "formatter" is not run
50                //              on this if "get" returns an undefined value.
51                //      formatter: Function?
52                //              function(data, rowIndex){} data is of type anything, rowIndex
53                //              is of type Integer.  This function will be called after the cell
54                //              has its data but before it passes it back to the grid to render.
55                //              Returns the formatted version of the cell's data.
56                //      type: dojox.grid.cells._Base|Function?
57                //              TODO
58                //      editable: Boolean?
59                //              Whether this cell should be editable or not.
60                //      hidden: Boolean?
61                //              If true, the cell will not be displayed.
62                //      noresize: Boolean?
63                //              If true, the cell will not be able to be resized.
64                //      width: Integer|String?
65                //              A CSS size.  If it's an Integer, the width will be in em's.
66                //      colSpan: Integer?
67                //              How many columns to span this cell.  Will not work in the first
68                //              sub-row of cells.
69                //      rowSpan: Integer?
70                //              How many sub-rows to span this cell.
71                //      styles: String?
72                //              A string of styles to apply to both the header cell and main
73                //              grid cells.  Must end in a ';'.
74                //      headerStyles: String?
75                //              A string of styles to apply to just the header cell.  Must end
76                //              in a ';'
77                //      cellStyles: String?
78                //              A string of styles to apply to just the main grid cells.  Must
79                //              end in a ';'
80                //      classes: String?
81                //              A space separated list of classes to apply to both the header
82                //              cell and the main grid cells.
83                //      headerClasses: String?
84                //              A space separated list of classes to apply to just the header
85                //              cell.
86                //      cellClasses: String?
87                //              A space separated list of classes to apply to just the main
88                //              grid cells.
89                //      attrs: String?
90                //              A space separated string of attribute='value' pairs to add to
91                //              the header cell element and main grid cell elements.
92                this.name = name;
93                this.value = value;
94                this.get = get;
95                this.formatter = formatter;
96                this.type = type;
97                this.editable = editable;
98                this.hidden = hidden;
99                this.width = width;
100                this.colSpan = colSpan;
101                this.rowSpan = rowSpan;
102                this.styles = styles;
103                this.headerStyles = headerStyles;
104                this.cellStyles = cellStyles;
105                this.classes = classes;
106                this.headerClasses = headerClasses;
107                this.cellClasses = cellClasses;
108                this.attrs = attrs;
109        }
110        =====*/
111
112        /*=====
113        dojox.grid.__ViewDef = function(){
114                //      noscroll: Boolean?
115                //              If true, no scrollbars will be rendered without scrollbars.
116                //      width: Integer|String?
117                //              A CSS size.  If it's an Integer, the width will be in em's. If
118                //              "noscroll" is true, this value is ignored.
119                //      cells: dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]]?
120                //              The structure of the cells within this grid.
121                //      type: String?
122                //              A string containing the constructor of a subclass of
123                //              dojox.grid._View.  If this is not specified, dojox.grid._View
124                //              is used.
125                //      defaultCell: dojox.grid.__CellDef?
126                //              A cell definition with default values for all cells in this view.  If
127                //              a property is defined in a cell definition in the "cells" array and
128                //              this property, the cell definition's property will override this
129                //              property's property.
130                //      onBeforeRow: Function?
131                //              function(rowIndex, cells){} rowIndex is of type Integer, cells
132                //              is of type Array[dojox.grid.__CellDef[]].  This function is called
133                //              before each row of data is rendered.  Before the header is
134                //              rendered, rowIndex will be -1.  "cells" is a reference to the
135                //              internal structure of this view's cells so any changes you make to
136                //              it will persist between calls.
137                //      onAfterRow: Function?
138                //              function(rowIndex, cells, rowNode){} rowIndex is of type Integer, cells
139                //              is of type Array[dojox.grid.__CellDef[]], rowNode is of type DOMNode.
140                //              This function is called after each row of data is rendered.  After the
141                //              header is rendered, rowIndex will be -1.  "cells" is a reference to the
142                //              internal structure of this view's cells so any changes you make to
143                //              it will persist between calls.
144                this.noscroll = noscroll;
145                this.width = width;
146                this.cells = cells;
147                this.type = type;
148                this.defaultCell = defaultCell;
149                this.onBeforeRow = onBeforeRow;
150                this.onAfterRow = onAfterRow;
151        }
152        =====*/
153
154        var _Grid = declare('dojox.grid._Grid',
155                [ _Widget, _TemplatedMixin, _Events ],
156                {
157                // summary:
158                //              A grid widget with virtual scrolling, cell editing, complex rows,
159                //              sorting, fixed columns, sizeable columns, etc.
160                //
161                //      description:
162                //              _Grid provides the full set of grid features without any
163                //              direct connection to a data store.
164                //
165                //              The grid exposes a get function for the grid, or optionally
166                //              individual columns, to populate cell contents.
167                //
168                //              The grid is rendered based on its structure, an object describing
169                //              column and cell layout.
170                //
171                //      example:
172                //              A quick sample:
173                //
174                //              define a get function
175                //      |       function get(inRowIndex){ // called in cell context
176                //      |               return [this.index, inRowIndex].join(', ');
177                //      |       }
178                //
179                //              define the grid structure:
180                //      |       var structure = [ // array of view objects
181                //      |               { cells: [// array of rows, a row is an array of cells
182                //      |                       [
183                //      |                               { name: "Alpha", width: 6 },
184                //      |                               { name: "Beta" },
185                //      |                               { name: "Gamma", get: get }]
186                //      |               ]}
187                //      |       ];
188                //
189                //      |       <div id="grid"
190                //      |               rowCount="100" get="get"
191                //      |               structure="structure"
192                //      |               dojoType="dojox.grid._Grid"></div>
193
194                templateString: template,
195
196                // classTag: String
197                //              CSS class applied to the grid's domNode
198                classTag: 'dojoxGrid',
199
200                // settings
201                // rowCount: Integer
202                //              Number of rows to display.
203                rowCount: 5,
204
205                // keepRows: Integer
206                //              Number of rows to keep in the rendering cache.
207                keepRows: 75,
208
209                // rowsPerPage: Integer
210                //              Number of rows to render at a time.
211                rowsPerPage: 25,
212
213                // autoWidth: Boolean
214                //              If autoWidth is true, grid width is automatically set to fit the data.
215                autoWidth: false,
216               
217                // initialWidth: String
218                //              A css string to use to set our initial width (only used if autoWidth
219                //              is true).  The first rendering of the grid will be this width, any
220                //              resizing of columns, etc will result in the grid switching to
221                //              autoWidth mode.  Note, this width will override any styling in a
222                //              stylesheet or directly on the node.
223                initialWidth: "",
224
225                // autoHeight: Boolean|Integer
226                //              If autoHeight is true, grid height is automatically set to fit the data.
227                //              If it is an integer, the height will be automatically set to fit the data
228                //              if there are fewer than that many rows - and the height will be set to show
229                //              that many rows if there are more
230                autoHeight: '',
231
232                // rowHeight: Integer
233                //              If rowHeight is set to a positive number, it will define the height of the rows
234                //              in pixels. This can provide a significant performance advantage, since it
235                //              eliminates the need to measure row sizes during rendering, which is one
236                //              the primary bottlenecks in the DataGrid's performance.
237                rowHeight: 0,
238               
239                // autoRender: Boolean
240                //              If autoRender is true, grid will render itself after initialization.
241                autoRender: true,
242
243                // defaultHeight: String
244                //              default height of the grid, measured in any valid css unit.
245                defaultHeight: '15em',
246               
247                // height: String
248                //              explicit height of the grid, measured in any valid css unit.  This will be populated (and overridden)
249                //              if the height: css attribute exists on the source node.
250                height: '',
251
252                // structure: dojox.grid.__ViewDef|dojox.grid.__ViewDef[]|dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]]
253                //              View layout defintion.
254                structure: null,
255
256                // elasticView: Integer
257                //      Override defaults and make the indexed grid view elastic, thus filling available horizontal space.
258                elasticView: -1,
259
260                // singleClickEdit: boolean
261                //              Single-click starts editing. Default is double-click
262                singleClickEdit: false,
263
264                // selectionMode: String
265                //              Set the selection mode of grid's Selection.  Value must be 'single', 'multiple',
266                //              or 'extended'.  Default is 'extended'.
267                selectionMode: 'extended',
268
269                // rowSelector: Boolean|String
270                //              If set to true, will add a row selector view to this grid.  If set to a CSS width, will add
271                //              a row selector of that width to this grid.
272                rowSelector: '',
273
274                // columnReordering: Boolean
275                //              If set to true, will add drag and drop reordering to views with one row of columns.
276                columnReordering: false,
277
278                // headerMenu: dijit.Menu
279                //              If set to a dijit.Menu, will use this as a context menu for the grid headers.
280                headerMenu: null,
281
282                // placeholderLabel: String
283                //              Label of placeholders to search for in the header menu to replace with column toggling
284                //              menu items.
285                placeholderLabel: "GridColumns",
286               
287                // selectable: Boolean
288                //              Set to true if you want to be able to select the text within the grid.
289                selectable: false,
290               
291                // Used to store the last two clicks, to ensure double-clicking occurs based on the intended row
292                _click: null,
293               
294                // loadingMessage: String
295                //  Message that shows while the grid is loading
296                loadingMessage: "<span class='dojoxGridLoading'>${loadingState}</span>",
297
298                // errorMessage: String
299                //  Message that shows when the grid encounters an error loading
300                errorMessage: "<span class='dojoxGridError'>${errorState}</span>",
301
302                // noDataMessage: String
303                //  Message that shows if the grid has no data - wrap it in a
304                //  span with class 'dojoxGridNoData' if you want it to be
305                //  styled similar to the loading and error messages
306                noDataMessage: "",
307               
308                // escapeHTMLInData: Boolean
309                //              This will escape HTML brackets from the data to prevent HTML from
310                //              user-inputted data being rendered with may contain JavaScript and result in
311                //              XSS attacks. This is true by default, and it is recommended that it remain
312                //              true. Setting this to false will allow data to be displayed in the grid without
313                //              filtering, and should be only used if it is known that the data won't contain
314                //              malicious scripts. If HTML is needed in grid cells, it is recommended that
315                //              you use the formatter function to generate the HTML (the output of
316                //              formatter functions is not filtered, even with escapeHTMLInData set to true).
317                escapeHTMLInData: true,
318               
319                // formatterScope: Object
320                //              An object to execute format functions within.  If not set, the
321                //              format functions will execute within the scope of the cell that
322                //              has a format function.
323                formatterScope: null,
324               
325                // editable: boolean
326                // indicates if the grid contains editable cells, default is false
327                // set to true if editable cell encountered during rendering
328                editable: false,
329               
330                // private
331                sortInfo: 0,
332                themeable: true,
333                _placeholders: null,
334
335                // _layoutClass: Object
336                //      The class to use for our layout - can be overridden by grid subclasses
337                _layoutClass: _Layout,
338
339                // initialization
340                buildRendering: function(){
341                        this.inherited(arguments);
342                        if(!this.domNode.getAttribute('tabIndex')){
343                                this.domNode.tabIndex = "0";
344                        }
345                        this.createScroller();
346                        this.createLayout();
347                        this.createViews();
348                        this.createManagers();
349
350                        this.createSelection();
351
352                        this.connect(this.selection, "onSelected", "onSelected");
353                        this.connect(this.selection, "onDeselected", "onDeselected");
354                        this.connect(this.selection, "onChanged", "onSelectionChanged");
355
356                        metrics.initOnFontResize();
357                        this.connect(metrics, "onFontResize", "textSizeChanged");
358                        util.funnelEvents(this.domNode, this, 'doKeyEvent', util.keyEvents);
359                        if (this.selectionMode != "none") {
360                                this.domNode.setAttribute("aria-multiselectable", this.selectionMode == "single" ? "false" : "true");
361                        }
362
363                        html.addClass(this.domNode, this.classTag);
364                        if(!this.isLeftToRight()){
365                                html.addClass(this.domNode, this.classTag+"Rtl");
366                        }
367                },
368               
369                postMixInProperties: function(){
370                        this.inherited(arguments);
371                        var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
372                        this.loadingMessage = string.substitute(this.loadingMessage, messages);
373                        this.errorMessage = string.substitute(this.errorMessage, messages);
374                        if(this.srcNodeRef && this.srcNodeRef.style.height){
375                                this.height = this.srcNodeRef.style.height;
376                        }
377                        // Call this to update our autoheight to start out
378                        this._setAutoHeightAttr(this.autoHeight, true);
379                        this.lastScrollTop = this.scrollTop = 0;
380                },
381               
382                postCreate: function(){
383                        this._placeholders = [];
384                        this._setHeaderMenuAttr(this.headerMenu);
385                        this._setStructureAttr(this.structure);
386                        this._click = [];
387                        this.inherited(arguments);
388                        if(this.domNode && this.autoWidth && this.initialWidth){
389                                this.domNode.style.width = this.initialWidth;
390                        }
391                        if (this.domNode && !this.editable){
392                                // default value for aria-readonly is false, set to true if grid is not editable
393                                html.attr(this.domNode,"aria-readonly", "true");
394                        }
395                },
396
397                destroy: function(){
398                        this.domNode.onReveal = null;
399                        this.domNode.onSizeChange = null;
400
401                        // Fixes IE domNode leak
402                        delete this._click;
403
404                        if(this.scroller){
405                                this.scroller.destroy();
406                                delete this.scroller;
407                        }
408                        this.edit.destroy();
409                        delete this.edit;
410                        this.views.destroyViews();
411                        if(this.focus){
412                                this.focus.destroy();
413                                delete this.focus;
414                        }
415                        if(this.headerMenu&&this._placeholders.length){
416                                array.forEach(this._placeholders, function(p){ p.unReplace(true); });
417                                this.headerMenu.unBindDomNode(this.viewsHeaderNode);
418                        }
419                        this.inherited(arguments);
420                },
421
422                _setAutoHeightAttr: function(ah, skipRender){
423                        // Calculate our autoheight - turn it into a boolean or an integer
424                        if(typeof ah == "string"){
425                                if(!ah || ah == "false"){
426                                        ah = false;
427                                }else if (ah == "true"){
428                                        ah = true;
429                                }else{
430                                        ah = window.parseInt(ah, 10);
431                                }
432                        }
433                        if(typeof ah == "number"){
434                                if(isNaN(ah)){
435                                        ah = false;
436                                }
437                                // Autoheight must be at least 1, if it's a number.  If it's
438                                // less than 0, we'll take that to mean "all" rows (same as
439                                // autoHeight=true - if it is equal to zero, we'll take that
440                                // to mean autoHeight=false
441                                if(ah < 0){
442                                        ah = true;
443                                }else if (ah === 0){
444                                        ah = false;
445                                }
446                        }
447                        this.autoHeight = ah;
448                        if(typeof ah == "boolean"){
449                                this._autoHeight = ah;
450                        }else if(typeof ah == "number"){
451                                this._autoHeight = (ah >= this.get('rowCount'));
452                        }else{
453                                this._autoHeight = false;
454                        }
455                        if(this._started && !skipRender){
456                                this.render();
457                        }
458                },
459
460                _getRowCountAttr: function(){
461                        return this.updating && this.invalidated && this.invalidated.rowCount != undefined ?
462                                this.invalidated.rowCount : this.rowCount;
463                },
464               
465                textSizeChanged: function(){
466                        this.render();
467                },
468
469                sizeChange: function(){
470                        this.update();
471                },
472
473                createManagers: function(){
474                        // summary:
475                        //              create grid managers for various tasks including rows, focus, selection, editing
476
477                        // row manager
478                        this.rows = new _RowManager(this);
479                        // focus manager
480                        this.focus = new _FocusManager(this);
481                        // edit manager
482                        this.edit = new _EditManager(this);
483                },
484
485                createSelection: function(){
486                        // summary:     Creates a new Grid selection manager.
487
488                        // selection manager
489                        this.selection = new Selection(this);
490                },
491
492                createScroller: function(){
493                        // summary: Creates a new virtual scroller
494                        this.scroller = new _Scroller();
495                        this.scroller.grid = this;
496                        this.scroller.renderRow = lang.hitch(this, "renderRow");
497                        this.scroller.removeRow = lang.hitch(this, "rowRemoved");
498                },
499
500                createLayout: function(){
501                        // summary: Creates a new Grid layout
502                        this.layout = new this._layoutClass(this);
503                        this.connect(this.layout, "moveColumn", "onMoveColumn");
504                },
505
506                onMoveColumn: function(){
507                        this.render();
508                },
509               
510                onResizeColumn: function(/*int*/ cellIdx){
511                        // Called when a column is resized.
512                },
513
514                // views
515                createViews: function(){
516                        this.views = new _ViewManager(this);
517                        this.views.createView = lang.hitch(this, "createView");
518                },
519
520                createView: function(inClass, idx){
521                        var c = lang.getObject(inClass);
522                        var view = new c({ grid: this, index: idx });
523                        this.viewsNode.appendChild(view.domNode);
524                        this.viewsHeaderNode.appendChild(view.headerNode);
525                        this.views.addView(view);
526                        html.attr(this.domNode, "align", this.isLeftToRight() ? 'left' : 'right');
527                        return view;
528                },
529
530                buildViews: function(){
531                        for(var i=0, vs; (vs=this.layout.structure[i]); i++){
532                                this.createView(vs.type || dojox._scopeName + ".grid._View", i).setStructure(vs);
533                        }
534                        this.scroller.setContentNodes(this.views.getContentNodes());
535                },
536
537                _setStructureAttr: function(structure){
538                        var s = structure;
539                        if(s && lang.isString(s)){
540                                dojo.deprecated("dojox.grid._Grid.set('structure', 'objVar')", "use dojox.grid._Grid.set('structure', objVar) instead", "2.0");
541                                s=lang.getObject(s);
542                        }
543                        this.structure = s;
544                        if(!s){
545                                if(this.layout.structure){
546                                        s = this.layout.structure;
547                                }else{
548                                        return;
549                                }
550                        }
551                        this.views.destroyViews();
552                        this.focus.focusView = null;
553                        if(s !== this.layout.structure){
554                                this.layout.setStructure(s);
555                        }
556                        this._structureChanged();
557                },
558
559                setStructure: function(/* dojox.grid.__ViewDef|dojox.grid.__ViewDef[]|dojox.grid.__CellDef[]|Array[dojox.grid.__CellDef[]] */ inStructure){
560                        // summary:
561                        //              Install a new structure and rebuild the grid.
562                        dojo.deprecated("dojox.grid._Grid.setStructure(obj)", "use dojox.grid._Grid.set('structure', obj) instead.", "2.0");
563                        this._setStructureAttr(inStructure);
564                },
565               
566                getColumnTogglingItems: function(){
567                        // Summary: returns an array of dijit.CheckedMenuItem widgets that can be
568                        //              added to a menu for toggling columns on and off.
569                        var items, checkedItems = [];
570                        items = array.map(this.layout.cells, function(cell){
571                                if(!cell.menuItems){ cell.menuItems = []; }
572
573                                var self = this;
574                                var item = new CheckedMenuItem({
575                                        label: cell.name,
576                                        checked: !cell.hidden,
577                                        _gridCell: cell,
578                                        onChange: function(checked){
579                                                if(self.layout.setColumnVisibility(this._gridCell.index, checked)){
580                                                        var items = this._gridCell.menuItems;
581                                                        if(items.length > 1){
582                                                                array.forEach(items, function(item){
583                                                                        if(item !== this){
584                                                                                item.setAttribute("checked", checked);
585                                                                        }
586                                                                }, this);
587                                                        }
588                                                        checked = array.filter(self.layout.cells, function(c){
589                                                                if(c.menuItems.length > 1){
590                                                                        array.forEach(c.menuItems, "item.set('disabled', false);");
591                                                                }else{
592                                                                        c.menuItems[0].set('disabled', false);
593                                                                }
594                                                                return !c.hidden;
595                                                        });
596                                                        if(checked.length == 1){
597                                                                array.forEach(checked[0].menuItems, "item.set('disabled', true);");
598                                                        }
599                                                }
600                                        },
601                                        destroy: function(){
602                                                var index = array.indexOf(this._gridCell.menuItems, this);
603                                                this._gridCell.menuItems.splice(index, 1);
604                                                delete this._gridCell;
605                                                CheckedMenuItem.prototype.destroy.apply(this, arguments);
606                                        }
607                                });
608                                cell.menuItems.push(item);
609                                if(!cell.hidden) {
610                                        checkedItems.push(item);
611                                }
612                                return item;
613                        }, this); // dijit.CheckedMenuItem[]
614                        if(checkedItems.length == 1) {
615                                checkedItems[0].set('disabled', true);
616                        }
617                        return items;
618                },
619
620                _setHeaderMenuAttr: function(menu){
621                        if(this._placeholders && this._placeholders.length){
622                                array.forEach(this._placeholders, function(p){
623                                        p.unReplace(true);
624                                });
625                                this._placeholders = [];
626                        }
627                        if(this.headerMenu){
628                                this.headerMenu.unBindDomNode(this.viewsHeaderNode);
629                        }
630                        this.headerMenu = menu;
631                        if(!menu){ return; }
632
633                        this.headerMenu.bindDomNode(this.viewsHeaderNode);
634                        if(this.headerMenu.getPlaceholders){
635                                this._placeholders = this.headerMenu.getPlaceholders(this.placeholderLabel);
636                        }
637                },
638
639                setHeaderMenu: function(/* dijit.Menu */ menu){
640                        dojo.deprecated("dojox.grid._Grid.setHeaderMenu(obj)", "use dojox.grid._Grid.set('headerMenu', obj) instead.", "2.0");
641                        this._setHeaderMenuAttr(menu);
642                },
643               
644                setupHeaderMenu: function(){
645                        if(this._placeholders && this._placeholders.length){
646                                array.forEach(this._placeholders, function(p){
647                                        if(p._replaced){
648                                                p.unReplace(true);
649                                        }
650                                        p.replace(this.getColumnTogglingItems());
651                                }, this);
652                        }
653                },
654
655                _fetch: function(start){
656                        this.setScrollTop(0);
657                },
658
659                getItem: function(inRowIndex){
660                        return null;
661                },
662               
663                showMessage: function(message){
664                        if(message){
665                                this.messagesNode.innerHTML = message;
666                                this.messagesNode.style.display = "";
667                        }else{
668                                this.messagesNode.innerHTML = "";
669                                this.messagesNode.style.display = "none";
670                        }
671                },
672
673                _structureChanged: function() {
674                        this.buildViews();
675                        if(this.autoRender && this._started){
676                                this.render();
677                        }
678                },
679
680                hasLayout: function() {
681                        return this.layout.cells.length;
682                },
683
684                // sizing
685                resize: function(changeSize, resultSize){
686                        // summary:
687                        //              Update the grid's rendering dimensions and resize it
688                       
689                        // Calling sizeChange calls update() which calls _resize...so let's
690                        // save our input values, if any, and use them there when it gets
691                        // called.  This saves us an extra call to _resize(), which can
692                        // get kind of heavy.
693                       
694                        // fixes #11101, should ignore resize when in autoheight mode(IE) to avoid a deadlock
695                        // e.g when an autoheight editable grid put in dijit.form.Form or other similar containers,
696                        // grid switch to editing mode --> grid height change --> From height change
697                        // ---> Form call grid.resize() ---> grid height change  --> deaklock
698                        if(dojo.isIE && !changeSize && !resultSize && this._autoHeight){
699                                return;
700                        }
701                        this._pendingChangeSize = changeSize;
702                        this._pendingResultSize = resultSize;
703                        this.sizeChange();
704                },
705
706                _getPadBorder: function() {
707                        this._padBorder = this._padBorder || html._getPadBorderExtents(this.domNode);
708                        return this._padBorder;
709                },
710
711                _getHeaderHeight: function(){
712                        var vns = this.viewsHeaderNode.style, t = vns.display == "none" ? 0 : this.views.measureHeader();
713                        vns.height = t + 'px';
714                        // header heights are reset during measuring so must be normalized after measuring.
715                        this.views.normalizeHeaderNodeHeight();
716                        return t;
717                },
718               
719                _resize: function(changeSize, resultSize){
720                        // Restore our pending values, if any
721                        changeSize = changeSize || this._pendingChangeSize;
722                        resultSize = resultSize || this._pendingResultSize;
723                        delete this._pendingChangeSize;
724                        delete this._pendingResultSize;
725                        // if we have set up everything except the DOM, we cannot resize
726                        if(!this.domNode){ return; }
727                        var pn = this.domNode.parentNode;
728                        if(!pn || pn.nodeType != 1 || !this.hasLayout() || pn.style.visibility == "hidden" || pn.style.display == "none"){
729                                return;
730                        }
731                        // useful measurement
732                        var padBorder = this._getPadBorder();
733                        var hh = undefined;
734                        var h;
735                        // grid height
736                        if(this._autoHeight){
737                                this.domNode.style.height = 'auto';
738                        }else if(typeof this.autoHeight == "number"){
739                                h = hh = this._getHeaderHeight();
740                                h += (this.scroller.averageRowHeight * this.autoHeight);
741                                this.domNode.style.height = h + "px";
742                        }else if(this.domNode.clientHeight <= padBorder.h){
743                                if(pn == document.body){
744                                        this.domNode.style.height = this.defaultHeight;
745                                }else if(this.height){
746                                        this.domNode.style.height = this.height;
747                                }else{
748                                        this.fitTo = "parent";
749                                }
750                        }
751                        // if we are given dimensions, size the grid's domNode to those dimensions
752                        if(resultSize){
753                                changeSize = resultSize;
754                        }
755                        if(!this._autoHeight && changeSize){
756                                html.marginBox(this.domNode, changeSize);
757                                this.height = this.domNode.style.height;
758                                delete this.fitTo;
759                        }else if(this.fitTo == "parent"){
760                                h = this._parentContentBoxHeight = this._parentContentBoxHeight || html._getContentBox(pn).h;
761                                this.domNode.style.height = Math.max(0, h) + "px";
762                        }
763                       
764                        var hasFlex = array.some(this.views.views, function(v){ return v.flexCells; });
765
766                        if(!this._autoHeight && (h || html._getContentBox(this.domNode).h) === 0){
767                                // We need to hide the header, since the Grid is essentially hidden.
768                                this.viewsHeaderNode.style.display = "none";
769                        }else{
770                                // Otherwise, show the header and give it an appropriate height.
771                                this.viewsHeaderNode.style.display = "block";
772                                if(!hasFlex && hh === undefined){
773                                        hh = this._getHeaderHeight();
774                                }
775                        }
776                        if(hasFlex){
777                                hh = undefined;
778                        }
779
780                        // NOTE: it is essential that width be applied before height
781                        // Header height can only be calculated properly after view widths have been set.
782                        // This is because flex column width is naturally 0 in Firefox.
783                        // Therefore prior to width sizing flex columns with spaces are maximally wrapped
784                        // and calculated to be too tall.
785                        this.adaptWidth();
786                        this.adaptHeight(hh);
787
788                        this.postresize();
789                },
790
791                adaptWidth: function() {
792                        // private: sets width and position for views and update grid width if necessary
793                        var doAutoWidth = (!this.initialWidth && this.autoWidth);
794                        var w = doAutoWidth ? 0 : this.domNode.clientWidth || (this.domNode.offsetWidth - this._getPadBorder().w),
795                                vw = this.views.arrange(1, w);
796                        this.views.onEach("adaptWidth");
797                        if(doAutoWidth){
798                                this.domNode.style.width = vw + "px";
799                        }
800                },
801
802                adaptHeight: function(inHeaderHeight){
803                        // private: measures and normalizes header height, then sets view heights, and then updates scroller
804                        // content extent
805                        var t = inHeaderHeight === undefined ? this._getHeaderHeight() : inHeaderHeight;
806                        var h = (this._autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);
807                        this.views.onEach('setSize', [0, h]);
808                        this.views.onEach('adaptHeight');
809                        if(!this._autoHeight){
810                                var numScroll = 0, numNoScroll = 0;
811                                var noScrolls = array.filter(this.views.views, function(v){
812                                        var has = v.hasHScrollbar();
813                                        if(has){ numScroll++; }else{ numNoScroll++; }
814                                        return (!has);
815                                });
816                                if(numScroll > 0 && numNoScroll > 0){
817                                        array.forEach(noScrolls, function(v){
818                                                v.adaptHeight(true);
819                                        });
820                                }
821                        }
822                        if(this.autoHeight === true || h != -1 || (typeof this.autoHeight == "number" && this.autoHeight >= this.get('rowCount'))){
823                                this.scroller.windowHeight = h;
824                        }else{
825                                this.scroller.windowHeight = Math.max(this.domNode.clientHeight - t, 0);
826                        }
827                },
828
829                // startup
830                startup: function(){
831                        if(this._started){return;}
832                        this.inherited(arguments);
833                        if(this.autoRender){
834                                this.render();
835                        }
836                },
837
838                // render
839                render: function(){
840                        // summary:
841                        //      Render the grid, headers, and views. Edit and scrolling states are reset. To retain edit and
842                        // scrolling states, see Update.
843
844                        if(!this.domNode){return;}
845                        if(!this._started){return;}
846
847                        if(!this.hasLayout()) {
848                                this.scroller.init(0, this.keepRows, this.rowsPerPage);
849                                return;
850                        }
851                        //
852                        this.update = this.defaultUpdate;
853                        this._render();
854                },
855
856                _render: function(){
857                        this.scroller.init(this.get('rowCount'), this.keepRows, this.rowsPerPage);
858                        this.prerender();
859                        this.setScrollTop(0);
860                        this.postrender();
861                },
862
863                prerender: function(){
864                        // if autoHeight, make sure scroller knows not to virtualize; everything must be rendered.
865                        this.keepRows = this._autoHeight ? 0 : this.keepRows;
866                        this.scroller.setKeepInfo(this.keepRows);
867                        this.views.render();
868                        this._resize();
869                },
870
871                postrender: function(){
872                        this.postresize();
873                        this.focus.initFocusView();
874                        // make rows unselectable
875                        html.setSelectable(this.domNode, this.selectable);
876                },
877
878                postresize: function(){
879                        // views are position absolute, so they do not inflate the parent
880                        if(this._autoHeight){
881                                var size = Math.max(this.views.measureContent()) + 'px';
882                               
883                                this.viewsNode.style.height = size;
884                        }
885                },
886
887                renderRow: function(inRowIndex, inNodes){
888                        // summary: private, used internally to render rows
889                        this.views.renderRow(inRowIndex, inNodes, this._skipRowRenormalize);
890                },
891
892                rowRemoved: function(inRowIndex){
893                        // summary: private, used internally to remove rows
894                        this.views.rowRemoved(inRowIndex);
895                },
896
897                invalidated: null,
898
899                updating: false,
900
901                beginUpdate: function(){
902                        // summary:
903                        //              Use to make multiple changes to rows while queueing row updating.
904                        // NOTE: not currently supporting nested begin/endUpdate calls
905                        this.invalidated = [];
906                        this.updating = true;
907                },
908
909                endUpdate: function(){
910                        // summary:
911                        //              Use after calling beginUpdate to render any changes made to rows.
912                        this.updating = false;
913                        var i = this.invalidated, r;
914                        if(i.all){
915                                this.update();
916                        }else if(i.rowCount != undefined){
917                                this.updateRowCount(i.rowCount);
918                        }else{
919                                for(r in i){
920                                        this.updateRow(Number(r));
921                                }
922                        }
923                        this.invalidated = [];
924                },
925
926                // update
927                defaultUpdate: function(){
928                        // note: initial update calls render and subsequently this function.
929                        if(!this.domNode){return;}
930                        if(this.updating){
931                                this.invalidated.all = true;
932                                return;
933                        }
934                        //this.edit.saveState(inRowIndex);
935                        this.lastScrollTop = this.scrollTop;
936                        this.prerender();
937                        this.scroller.invalidateNodes();
938                        this.setScrollTop(this.lastScrollTop);
939                        this.postrender();
940                        //this.edit.restoreState(inRowIndex);
941                },
942
943                update: function(){
944                        // summary:
945                        //              Update the grid, retaining edit and scrolling states.
946                        this.render();
947                },
948
949                updateRow: function(inRowIndex){
950                        // summary:
951                        //              Render a single row.
952                        // inRowIndex: Integer
953                        //              Index of the row to render
954                        inRowIndex = Number(inRowIndex);
955                        if(this.updating){
956                                this.invalidated[inRowIndex]=true;
957                        }else{
958                                this.views.updateRow(inRowIndex);
959                                this.scroller.rowHeightChanged(inRowIndex);
960                        }
961                },
962
963                updateRows: function(startIndex, howMany){
964                        // summary:
965                        //              Render consecutive rows at once.
966                        // startIndex: Integer
967                        //              Index of the starting row to render
968                        // howMany: Integer
969                        //              How many rows to update.
970                        startIndex = Number(startIndex);
971                        howMany = Number(howMany);
972                        var i;
973                        if(this.updating){
974                                for(i=0; i<howMany; i++){
975                                        this.invalidated[i+startIndex]=true;
976                                }
977                        }else{
978                                for(i=0; i<howMany; i++){
979                                        this.views.updateRow(i+startIndex, this._skipRowRenormalize);
980                                }
981                                this.scroller.rowHeightChanged(startIndex);
982                        }
983                },
984
985                updateRowCount: function(inRowCount){
986                        //summary:
987                        //      Change the number of rows.
988                        // inRowCount: int
989                        //      Number of rows in the grid.
990                        if(this.updating){
991                                this.invalidated.rowCount = inRowCount;
992                        }else{
993                                this.rowCount = inRowCount;
994                                this._setAutoHeightAttr(this.autoHeight, true);
995                                if(this.layout.cells.length){
996                                        this.scroller.updateRowCount(inRowCount);
997                                }
998                                this._resize();
999                                if(this.layout.cells.length){
1000                                        this.setScrollTop(this.scrollTop);
1001                                }
1002                        }
1003                },
1004
1005                updateRowStyles: function(inRowIndex){
1006                        // summary:
1007                        //              Update the styles for a row after it's state has changed.
1008                        this.views.updateRowStyles(inRowIndex);
1009                },
1010                getRowNode: function(inRowIndex){
1011                        // summary:
1012                        //              find the rowNode that is not a rowSelector
1013                        if (this.focus.focusView && !(this.focus.focusView instanceof _RowSelector)){
1014                                        return this.focus.focusView.rowNodes[inRowIndex];
1015                        }else{ // search through views
1016                                for (var i = 0, cView; (cView = this.views.views[i]); i++) {
1017                                        if (!(cView instanceof _RowSelector)) {
1018                                                return cView.rowNodes[inRowIndex];
1019                                        }
1020                                }
1021                        }
1022                        return null;
1023                },
1024                rowHeightChanged: function(inRowIndex){
1025                        // summary:
1026                        //              Update grid when the height of a row has changed. Row height is handled automatically as rows
1027                        //              are rendered. Use this function only to update a row's height outside the normal rendering process.
1028                        // inRowIndex: Integer
1029                        //              index of the row that has changed height
1030
1031                        this.views.renormalizeRow(inRowIndex);
1032                        this.scroller.rowHeightChanged(inRowIndex);
1033                },
1034
1035                // fastScroll: Boolean
1036                //              flag modifies vertical scrolling behavior. Defaults to true but set to false for slower
1037                //              scroll performance but more immediate scrolling feedback
1038                fastScroll: true,
1039
1040                delayScroll: false,
1041
1042                // scrollRedrawThreshold: int
1043                //      pixel distance a user must scroll vertically to trigger grid scrolling.
1044                scrollRedrawThreshold: (has('ie') ? 100 : 50),
1045
1046                // scroll methods
1047                scrollTo: function(inTop){
1048                        // summary:
1049                        //              Vertically scroll the grid to a given pixel position
1050                        // inTop: Integer
1051                        //              vertical position of the grid in pixels
1052                        if(!this.fastScroll){
1053                                this.setScrollTop(inTop);
1054                                return;
1055                        }
1056                        var delta = Math.abs(this.lastScrollTop - inTop);
1057                        this.lastScrollTop = inTop;
1058                        if(delta > this.scrollRedrawThreshold || this.delayScroll){
1059                                this.delayScroll = true;
1060                                this.scrollTop = inTop;
1061                                this.views.setScrollTop(inTop);
1062                                if(this._pendingScroll){
1063                                        window.clearTimeout(this._pendingScroll);
1064                                }
1065                                var _this = this;
1066                                this._pendingScroll = window.setTimeout(function(){
1067                                        delete _this._pendingScroll;
1068                                        _this.finishScrollJob();
1069                                }, 200);
1070                        }else{
1071                                this.setScrollTop(inTop);
1072                        }
1073                },
1074
1075                finishScrollJob: function(){
1076                        this.delayScroll = false;
1077                        this.setScrollTop(this.scrollTop);
1078                },
1079
1080                setScrollTop: function(inTop){
1081                        this.scroller.scroll(this.views.setScrollTop(inTop));
1082                },
1083
1084                scrollToRow: function(inRowIndex){
1085                        // summary:
1086                        //              Scroll the grid to a specific row.
1087                        // inRowIndex: Integer
1088                        //              grid row index
1089                        this.setScrollTop(this.scroller.findScrollTop(inRowIndex) + 1);
1090                },
1091
1092                // styling (private, used internally to style individual parts of a row)
1093                styleRowNode: function(inRowIndex, inRowNode){
1094                        if(inRowNode){
1095                                this.rows.styleRowNode(inRowIndex, inRowNode);
1096                        }
1097                },
1098               
1099                // called when the mouse leaves the grid so we can deselect all hover rows
1100                _mouseOut: function(e){
1101                        this.rows.setOverRow(-2);
1102                },
1103       
1104                // cells
1105                getCell: function(inIndex){
1106                        // summary:
1107                        //              Retrieves the cell object for a given grid column.
1108                        // inIndex: Integer
1109                        //              Grid column index of cell to retrieve
1110                        // returns:
1111                        //              a grid cell
1112                        return this.layout.cells[inIndex];
1113                },
1114
1115                setCellWidth: function(inIndex, inUnitWidth){
1116                        this.getCell(inIndex).unitWidth = inUnitWidth;
1117                },
1118
1119                getCellName: function(inCell){
1120                        // summary: Returns the cell name of a passed cell
1121                        return "Cell " + inCell.index; // String
1122                },
1123
1124                // sorting
1125                canSort: function(inSortInfo){
1126                        // summary:
1127                        //              Determines if the grid can be sorted
1128                        // inSortInfo: Integer
1129                        //              Sort information, 1-based index of column on which to sort, positive for an ascending sort
1130                        //              and negative for a descending sort
1131                        // returns: Boolean
1132                        //              True if grid can be sorted on the given column in the given direction
1133                },
1134
1135                sort: function(){
1136                },
1137
1138                getSortAsc: function(inSortInfo){
1139                        // summary:
1140                        //              Returns true if grid is sorted in an ascending direction.
1141                        inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
1142                        return Boolean(inSortInfo > 0); // Boolean
1143                },
1144
1145                getSortIndex: function(inSortInfo){
1146                        // summary:
1147                        //              Returns the index of the column on which the grid is sorted
1148                        inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
1149                        return Math.abs(inSortInfo) - 1; // Integer
1150                },
1151
1152                setSortIndex: function(inIndex, inAsc){
1153                        // summary:
1154                        //              Sort the grid on a column in a specified direction
1155                        // inIndex: Integer
1156                        //              Column index on which to sort.
1157                        // inAsc: Boolean
1158                        //              If true, sort the grid in ascending order, otherwise in descending order
1159                        var si = inIndex +1;
1160                        if(inAsc != undefined){
1161                                si *= (inAsc ? 1 : -1);
1162                        } else if(this.getSortIndex() == inIndex){
1163                                si = -this.sortInfo;
1164                        }
1165                        this.setSortInfo(si);
1166                },
1167
1168                setSortInfo: function(inSortInfo){
1169                        if(this.canSort(inSortInfo)){
1170                                this.sortInfo = inSortInfo;
1171                                this.sort();
1172                                this.update();
1173                        }
1174                },
1175
1176                // DOM event handler
1177                doKeyEvent: function(e){
1178                        e.dispatch = 'do' + e.type;
1179                        this.onKeyEvent(e);
1180                },
1181
1182                // event dispatch
1183                //: protected
1184                _dispatch: function(m, e){
1185                        if(m in this){
1186                                return this[m](e);
1187                        }
1188                        return false;
1189                },
1190
1191                dispatchKeyEvent: function(e){
1192                        this._dispatch(e.dispatch, e);
1193                },
1194
1195                dispatchContentEvent: function(e){
1196                        this.edit.dispatchEvent(e) || e.sourceView.dispatchContentEvent(e) || this._dispatch(e.dispatch, e);
1197                },
1198
1199                dispatchHeaderEvent: function(e){
1200                        e.sourceView.dispatchHeaderEvent(e) || this._dispatch('doheader' + e.type, e);
1201                },
1202
1203                dokeydown: function(e){
1204                        this.onKeyDown(e);
1205                },
1206
1207                doclick: function(e){
1208                        if(e.cellNode){
1209                                this.onCellClick(e);
1210                        }else{
1211                                this.onRowClick(e);
1212                        }
1213                },
1214
1215                dodblclick: function(e){
1216                        if(e.cellNode){
1217                                this.onCellDblClick(e);
1218                        }else{
1219                                this.onRowDblClick(e);
1220                        }
1221                },
1222
1223                docontextmenu: function(e){
1224                        if(e.cellNode){
1225                                this.onCellContextMenu(e);
1226                        }else{
1227                                this.onRowContextMenu(e);
1228                        }
1229                },
1230
1231                doheaderclick: function(e){
1232                        if(e.cellNode){
1233                                this.onHeaderCellClick(e);
1234                        }else{
1235                                this.onHeaderClick(e);
1236                        }
1237                },
1238
1239                doheaderdblclick: function(e){
1240                        if(e.cellNode){
1241                                this.onHeaderCellDblClick(e);
1242                        }else{
1243                                this.onHeaderDblClick(e);
1244                        }
1245                },
1246
1247                doheadercontextmenu: function(e){
1248                        if(e.cellNode){
1249                                this.onHeaderCellContextMenu(e);
1250                        }else{
1251                                this.onHeaderContextMenu(e);
1252                        }
1253                },
1254
1255                // override to modify editing process
1256                doStartEdit: function(inCell, inRowIndex){
1257                        this.onStartEdit(inCell, inRowIndex);
1258                },
1259
1260                doApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){
1261                        this.onApplyCellEdit(inValue, inRowIndex, inFieldIndex);
1262                },
1263
1264                doCancelEdit: function(inRowIndex){
1265                        this.onCancelEdit(inRowIndex);
1266                },
1267
1268                doApplyEdit: function(inRowIndex){
1269                        this.onApplyEdit(inRowIndex);
1270                },
1271
1272                // row editing
1273                addRow: function(){
1274                        // summary:
1275                        //              Add a row to the grid.
1276                        this.updateRowCount(this.get('rowCount')+1);
1277                },
1278
1279                removeSelectedRows: function(){
1280                        // summary:
1281                        //              Remove the selected rows from the grid.
1282                        if(this.allItemsSelected){
1283                                this.updateRowCount(0);
1284                        }else{
1285                                this.updateRowCount(Math.max(0, this.get('rowCount') - this.selection.getSelected().length));
1286                        }
1287                        this.selection.clear();
1288                }
1289
1290        });
1291
1292        _Grid.markupFactory = function(props, node, ctor, cellFunc){
1293                var widthFromAttr = function(n){
1294                        var w = html.attr(n, "width")||"auto";
1295                        if((w != "auto")&&(w.slice(-2) != "em")&&(w.slice(-1) != "%")){
1296                                w = parseInt(w, 10)+"px";
1297                        }
1298                        return w;
1299                };
1300                // if(!props.store){ console.debug("no store!"); }
1301                // if a structure isn't referenced, do we have enough
1302                // data to try to build one automatically?
1303                if(     !props.structure &&
1304                        node.nodeName.toLowerCase() == "table"){
1305
1306                        // try to discover a structure
1307                        props.structure = query("> colgroup", node).map(function(cg){
1308                                var sv = html.attr(cg, "span");
1309                                var v = {
1310                                        noscroll: (html.attr(cg, "noscroll") == "true") ? true : false,
1311                                        __span: (!!sv ? parseInt(sv, 10) : 1),
1312                                        cells: []
1313                                };
1314                                if(html.hasAttr(cg, "width")){
1315                                        v.width = widthFromAttr(cg);
1316                                }
1317                                return v; // for vendetta
1318                        });
1319                        if(!props.structure.length){
1320                                props.structure.push({
1321                                        __span: Infinity,
1322                                        cells: [] // catch-all view
1323                                });
1324                        }
1325                        // check to see if we're gonna have more than one view
1326
1327                        // for each tr in our th, create a row of cells
1328                        query("thead > tr", node).forEach(function(tr, tr_idx){
1329                                var cellCount = 0;
1330                                var viewIdx = 0;
1331                                var lastViewIdx;
1332                                var cView = null;
1333                                query("> th", tr).map(function(th){
1334                                        // what view will this cell go into?
1335
1336                                        // NOTE:
1337                                        //              to prevent extraneous iteration, we start counters over
1338                                        //              for each row, incrementing over the surface area of the
1339                                        //              structure that colgroup processing generates and
1340                                        //              creating cell objects for each <th> to place into those
1341                                        //              cell groups.  There's a lot of state-keepking logic
1342                                        //              here, but it is what it has to be.
1343                                        if(!cView){ // current view book keeping
1344                                                lastViewIdx = 0;
1345                                                cView = props.structure[0];
1346                                        }else if(cellCount >= (lastViewIdx+cView.__span)){
1347                                                viewIdx++;
1348                                                // move to allocating things into the next view
1349                                                lastViewIdx += cView.__span;
1350                                                var lastView = cView;
1351                                                cView = props.structure[viewIdx];
1352                                        }
1353
1354                                        // actually define the cell from what markup hands us
1355                                        var cell = {
1356                                                name: lang.trim(html.attr(th, "name")||th.innerHTML),
1357                                                colSpan: parseInt(html.attr(th, "colspan")||1, 10),
1358                                                type: lang.trim(html.attr(th, "cellType")||""),
1359                                                id: lang.trim(html.attr(th,"id")||"")
1360                                        };
1361                                        cellCount += cell.colSpan;
1362                                        var rowSpan = html.attr(th, "rowspan");
1363                                        if(rowSpan){
1364                                                cell.rowSpan = rowSpan;
1365                                        }
1366                                        if(html.hasAttr(th, "width")){
1367                                                cell.width = widthFromAttr(th);
1368                                        }
1369                                        if(html.hasAttr(th, "relWidth")){
1370                                                cell.relWidth = window.parseInt(html.attr(th, "relWidth"), 10);
1371                                        }
1372                                        if(html.hasAttr(th, "hidden")){
1373                                                cell.hidden = (html.attr(th, "hidden") == "true" || html.attr(th, "hidden") === true/*always boolean true in Chrome*/);
1374                                        }
1375
1376                                        if(cellFunc){
1377                                                cellFunc(th, cell);
1378                                        }
1379
1380                                        cell.type = cell.type ? lang.getObject(cell.type) : dojox.grid.cells.Cell;
1381
1382                                        if(cell.type && cell.type.markupFactory){
1383                                                cell.type.markupFactory(th, cell);
1384                                        }
1385
1386                                        if(!cView.cells[tr_idx]){
1387                                                cView.cells[tr_idx] = [];
1388                                        }
1389                                        cView.cells[tr_idx].push(cell);
1390                                });
1391                        });
1392                }
1393
1394                return new ctor(props, node);
1395        };
1396
1397        return _Grid;
1398
1399});
Note: See TracBrowser for help on using the repository browser.