source: Dev/trunk/src/client/dojox/calendar/MatrixView.js @ 485

Last change on this file since 485 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 61.6 KB
Line 
1define([
2"dojo/_base/declare",
3"dojo/_base/array",
4"dojo/_base/event",
5"dojo/_base/lang",
6"dojo/_base/sniff",
7"dojo/_base/fx",
8"dojo/_base/html",
9"dojo/on",
10"dojo/dom",
11"dojo/dom-class",
12"dojo/dom-style",
13"dojo/dom-geometry",
14"dojo/dom-construct",
15"dojo/query",
16"dojox/html/metrics",
17"dojo/i18n",
18"./ViewBase",
19"dojo/text!./templates/MatrixView.html",
20"dijit/_TemplatedMixin"],
21       
22function(
23        declare,
24        arr,
25        event,
26        lang,
27        has,
28        fx,
29        html,
30        on,
31        dom,
32        domClass,
33        domStyle,
34        domGeometry,
35        domConstruct,
36        query,
37        metrics,
38        i18n,
39        ViewBase,
40        template,
41        _TemplatedMixin){
42       
43        /*=====
44        var __HeaderClickEventArgs = {
45                // summary:
46                //              A column click event.
47                // index: Integer
48                //              The column index.
49                // date: Date
50                //              The date displayed by the column.
51                // triggerEvent: Event
52                //              The origin event.
53        };
54        =====*/
55       
56        /*=====
57        var __ExpandRendererClickEventArgs = {
58                // summary:
59                //              A expand renderer click event.
60                // columnIndex: Integer
61                //              The column index of the cell.
62                // rowIndex: Integer
63                //              The row index of the cell.
64                // date: Date
65                //              The date displayed by the cell.
66                // triggerEvent: Event
67                //              The origin event.
68        };
69        =====*/
70       
71        return declare("dojox.calendar.MatrixView", [ViewBase, _TemplatedMixin], {
72               
73                // summary:
74                //              The matrix view is a calendar view that displaying a matrix where each cell is a day.
75
76                templateString: template,
77       
78                baseClass: "dojoxCalendarMatrixView",
79               
80                _setTabIndexAttr: "domNode",
81               
82                // viewKind: String
83                //              Type of the view. Used by the calendar widget to determine how to configure the view.
84                //              This view kind is "matrix".
85                viewKind: "matrix",
86
87                // renderData: Object
88                //              The render data object contains all the data needed to render the widget.
89                renderData: null,
90               
91                // startDate: Date
92                //              The start date of the time interval displayed.
93                //              If not set at initialization time, will be set to current day.
94                startDate: null,
95               
96                // refStartTime: Date?
97                //              (Optional) Start of the time interval of interest.
98                //              It is used to style differently the displayed rows out of the
99                //              time interval of interest.             
100                refStartTime: null,
101               
102                // refStartTime: Date?
103                //              (Optional) End of the time interval of interest.
104                //              It is used to style differently the displayed rows out of the
105                //              time interval of interest. 
106                refEndTime: null,
107                               
108                // columnCount: Integer
109                //              The number of column to display (from the startDate).
110                columnCount: 7,
111               
112                // rowCount: Integer
113                //              The number of rows to display (from the startDate).
114                rowCount: 5,
115                       
116                // horizontalRenderer: Class
117                //              The class use to create horizontal renderers.
118                horizontalRenderer: null,
119               
120                // labelRenderer: Class
121                //              The class use to create label renderers.
122                labelRenderer: null,
123
124                // expandRenderer: Class
125                //              The class use to create drill down renderers.           
126                expandRenderer: null,
127               
128                // percentOverlap: Integer
129                //              The percentage of the renderer width used to superimpose one item renderers on another
130                //              when two events are overlapping. By default 0.
131                percentOverlap: 0,
132               
133                // verticalGap: Integer
134                //              The number of pixels between two item renderers that are overlapping each other if the percentOverlap property is 0.
135                verticalGap: 2,
136               
137                // horizontalRendererHeight: Integer
138                //              The height in pixels of the horizontal and label renderers that is applied by the layout.
139                horizontalRendererHeight: 17,
140               
141                // horizontalRendererHeight: Integer
142                //              The height in pixels of the horizontal and label renderers that is applied by the layout.
143                labelRendererHeight: 14,
144               
145                // expandRendererHeight: Integer
146                //              The height in pixels of the expand/collapse renderers that is applied by the layout.
147                expandRendererHeight: 15,
148               
149                // cellPaddingTop: Integer
150                //              The top offset in pixels of each cell applied by the layout.
151                cellPaddingTop: 16,
152               
153                // expandDuration: Integer
154                //              Duration of the animation when expanding or collapsing a row.
155                expandDuration: 300,
156               
157                // expandEasing: Function
158                //              Easing function of the animation when expanding or collapsing a row (null by default).
159                expandEasing: null,
160               
161                // layoutDuringResize: Boolean
162                //              Indicates if the item renderers' position and size is updated or if they are hidden during a resize of the widget.
163                layoutDuringResize: false,
164               
165                // roundToDay: Boolean
166                //              For horizontal renderers that are not filling entire days, whether fill the day or not.
167                roundToDay: true,
168               
169                // showCellLabel: Boolean
170                //              Whether display or not the grid cells label (usually the day of month).
171                showCellLabel: true,
172               
173                // scrollable: [private] Boolean
174                scrollable: false,
175
176                // resizeCursor: [private] Boolean
177                resizeCursor: "e-resize",
178               
179                constructor: function(){
180                        this.invalidatingProperties = ["columnCount", "rowCount", "startDate", "horizontalRenderer", "labelRenderer", "expandRenderer",
181                        "rowHeaderDatePattern", "columnHeaderLabelLength", "cellHeaderShortPattern", "cellHeaderLongPattern", "percentOverlap",
182                        "verticalGap", "horizontalRendererHeight", "labelRendererHeight", "expandRendererHeight", "cellPaddingTop",
183                        "roundToDay", "itemToRendererKindFunc", "layoutPriorityFunction", "formatItemTimeFunc", "textDir", "items"];
184                       
185                        this._ddRendererList = [];
186                        this._ddRendererPool = [];
187                        this._rowHeaderHandles = [];                   
188                },
189               
190                destroy: function(preserveDom){
191                        this._cleanupRowHeader();
192                        this.inherited(arguments);
193                },
194                       
195                postCreate: function(){
196                        this.inherited(arguments);
197                        this._initialized = true;
198                        if(!this.invalidRendering){
199                                this.refreshRendering();
200                        }                       
201                },
202                                               
203                _createRenderData: function(){
204                       
205                        var rd = {};
206                       
207                        rd.dateLocaleModule = this.dateLocaleModule;
208                        rd.dateClassObj = this.dateClassObj;
209                        rd.dateModule = this.dateModule; // arithmetics on Dates
210                       
211                        rd.dates = [];
212                       
213                        rd.columnCount = this.get("columnCount");
214                        rd.rowCount = this.get("rowCount");
215                       
216                        rd.sheetHeight = this.itemContainer.offsetHeight;
217                       
218                        this._computeRowsHeight(rd);
219                       
220                        var d = this.get("startDate");
221               
222                        if(d == null){
223                                d = new rd.dateClassObj();                             
224                        }
225
226                        d = this.floorToDay(d, false, rd);
227                       
228                        this.startDate = d;                                     
229                       
230                        for(var row = 0; row < rd.rowCount ; row++){
231                                rd.dates.push([]);
232                                for(var col = 0; col < rd.columnCount ; col++){
233                                        rd.dates[row].push(d);
234                                        d = rd.dateModule.add(d, "day", 1);
235                                        d = this.floorToDay(d, false, rd);                                     
236                                }
237                        }
238
239                        rd.startTime = this.newDate(rd.dates[0][0], rd);
240                        rd.endTime = this.newDate(rd.dates[rd.rowCount-1][rd.columnCount-1], rd);                       
241                        rd.endTime = rd.dateModule.add(rd.endTime, "day", 1);
242                        rd.endTime = this.floorToDay(rd.endTime, true);
243                       
244                        if(this.displayedItemsInvalidated && !this._isEditing){
245                                this.displayedItemsInvalidated = false;
246                                this._computeVisibleItems(rd);
247                                                               
248                        }else if(this.renderData){
249                                rd.items = this.renderData.items;
250                        }
251                       
252                        rd.rtl = !this.isLeftToRight();
253                       
254                        return rd;
255                },
256                               
257                _validateProperties: function(){
258                       
259                        this.inherited(arguments);
260                                               
261                        if(this.columnCount<1 || isNaN(this.columnCount)){
262                                this.columnCount = 1;
263                        }
264                       
265                        if(this.rowCount<1 || isNaN(this.rowCount)){
266                                this.rowCount = 1;
267                        }
268                       
269                        if(isNaN(this.percentOverlap) || this.percentOverlap < 0 || this.percentOverlap > 100){
270                                this.percentOverlap = 0;
271                        }
272                       
273                        if(isNaN(this.verticalGap) || this.verticalGap < 0){
274                                this.verticalGap = 2;
275                        }
276                       
277                        if(isNaN(this.horizontalRendererHeight) || this.horizontalRendererHeight < 1){
278                                this.horizontalRendererHeight = 17;
279                        }
280                       
281                        if(isNaN(this.labelRendererHeight) || this.labelRendererHeight < 1){
282                                this.labelRendererHeight = 14;
283                        }
284                       
285                        if(isNaN(this.expandRendererHeight) || this.expandRendererHeight < 1){
286                                this.expandRendererHeight = 15;
287                        }
288               
289                },
290               
291                _setStartDateAttr: function(value){
292                        this.displayedItemsInvalidated = true;
293                        this._set("startDate", value);
294                },
295               
296                _setColumnCountAttr: function(value){
297                        this.displayedItemsInvalidated = true;
298                        this._set("columnCount", value);
299                },
300               
301                _setRowCountAttr: function(value){
302                        this.displayedItemsInvalidated = true;
303                        this._set("rowCount", value);
304                },
305               
306                __fixEvt:function(e){
307                        e.sheet = "primary";
308                        e.source = this;
309                        return e;
310                },
311
312                //////////////////////////////////////////
313                //
314                // Formatting functions
315                //
316                //////////////////////////////////////////
317
318                _formatRowHeaderLabel: function(/*Date*/d){
319                        // summary:
320                        //              Computes the row header label for the specified time of day.
321                        //              By default the getWeekNumberLabel() function is called.
322                        //              The rowHeaderDatePattern property can be used to set a
323                        //              custom date pattern to the formatter.
324                        // d: Date
325                        //              The date to format     
326                        // tags:
327                        //              protected
328
329                        if(this.rowHeaderDatePattern){
330                                return this.renderData.dateLocaleModule.format(d, {
331                                        selector: 'date',
332                                        datePattern: this.rowHeaderDatePattern
333                                });
334                        }else{
335                                return this.getWeekNumberLabel(d);
336                        }
337
338                },
339       
340                _formatColumnHeaderLabel: function(d){
341                        // summary:
342                        //              Computes the column header label for the specified date.
343                        //              By default a formatter is used, optionally the <code>columnHeaderLabelLength</code>
344                        //              property can be used to specify the length of the string.
345                        // d: Date
346                        //              The date to format
347                        // tags:
348                        //              protected
349
350                        return this.renderData.dateLocaleModule.getNames('days', this.columnHeaderLabelLength ? this.columnHeaderLabelLength : 'wide', 'standAlone')[d.getDay()];
351                },
352               
353                _formatGridCellLabel: function(d, row, col){
354                        // summary:
355                        //              Computes the column header label for the specified date.
356                        //              By default a formatter is used, optionally the <code>cellHeaderLongPattern</code> and <code>cellHeaderShortPattern</code>
357                        //              properties can be used to set a custom date pattern to the formatter.
358                        // d: Date
359                        //              The date to format.
360                        // row: Integer
361                        //              The row that displays the current date.
362                        // col: Integer
363                        //              The column that displays the current date.
364                        // tags:
365                        //              protected
366
367
368                        var isFirstDayOfMonth = row == 0 && col == 0 || d.getDate() == 1;
369                        var format, rb;
370                        if(isFirstDayOfMonth){
371                                if(this.cellHeaderLongPattern){
372                                        format = this.cellHeaderLongPattern;
373                                }else{
374                                        rb = i18n.getLocalization("dojo.cldr", this._calendar);
375                                        format = rb["dateFormatItem-MMMd"];
376                                }
377                        }else{
378                                if(this.cellHeaderShortPattern){
379                                        format = this.cellHeaderShortPattern;
380                                }else{
381                                        rb = i18n.getLocalization("dojo.cldr", this._calendar);
382                                        format = rb["dateFormatItem-d"];
383                                }
384                        }
385                        return this.renderData.dateLocaleModule.format(d, {
386                                selector: 'date',
387                                datePattern: format
388                        });
389                },
390               
391                ////////////////////////////////////////////
392                //
393                // HTML structure management
394                //
395                ///////////////////////////////////////////
396       
397                refreshRendering: function(){
398                        this.inherited(arguments);
399                       
400                        if(!this.domNode){
401                                return;
402                        }
403                                               
404                        this._validateProperties();
405
406                        var oldRd = this.renderData;
407                        this.renderData = this._createRenderData();
408
409                        this._createRendering(this.renderData, oldRd);
410                       
411                        this._layoutRenderers(this.renderData);                                         
412                },
413               
414                _createRendering: function(renderData, oldRenderData){
415                        // summary:
416                        //              Creates the HTML structure (grid, place holders, headers, etc)
417                        // renderData: Object
418                        //              The new render data
419                        // oldRenderData: Object
420                        //              The previous render data
421                        // tags:
422                        //              private
423                       
424                        if(renderData.rowHeight <= 0){
425                                renderData.columnCount = 1;
426                                renderData.rowCount = 1;
427                                renderData.invalidRowHeight = true;
428                                return;
429                        }
430                       
431                        if(oldRenderData){
432                                // make sure to have correct rowCount
433                                if(this.itemContainerTable){
434                                        var rows = query(".dojoxCalendarItemContainerRow", this.itemContainerTable);                                   
435                                        oldRenderData.rowCount = rows.length;
436                                }
437                        }
438                       
439                        this._buildColumnHeader(renderData, oldRenderData);
440                        this._buildRowHeader(renderData, oldRenderData);
441                        this._buildGrid(renderData, oldRenderData);
442                        this._buildItemContainer(renderData, oldRenderData);
443                       
444                        if(this.buttonContainer && this.owner != null && this.owner.currentView == this){
445                                domStyle.set(this.buttonContainer, {"right":0, "left":0});
446                        }
447                },     
448               
449                _buildColumnHeader: function(/*Object*/ renderData, /*Object*/oldRenderData){
450                        // summary:
451                        //              Creates incrementally the HTML structure of the column header and configures its content.
452                        //
453                        // renderData:
454                        //              The render data to display.
455                        //
456                        // oldRenderData:
457                        //              The previously render data displayed, if any.
458                        // tags:
459                        //              private
460                        var table = this.columnHeaderTable;
461                       
462                        if(!table){
463                                return;
464                        }       
465                                               
466                        var count = renderData.columnCount - (oldRenderData ? oldRenderData.columnCount : 0);
467                       
468                        if(has("ie") == 8){
469                                // workaround Internet Explorer 8 bug.
470                                // if on the table, width: 100% and table-layout: fixed are set
471                                // and columns are removed, width of remaining columns is not
472                                // recomputed: must rebuild all.
473                                if(this._colTableSave == null){
474                                        this._colTableSave = lang.clone(table);
475                                }else if(count < 0){
476                                        this.columnHeader.removeChild(table);
477                                        domConstruct.destroy(table);
478                                        table = lang.clone(this._colTableSave);
479                                        this.columnHeaderTable = table;
480                                        this.columnHeader.appendChild(table);
481                                        count = renderData.columnCount;
482                                }
483                               
484                        } // else incremental dom add/remove for real browsers.
485               
486                        var tbodies = query("tbody", table);
487                        var trs = query("tr", table);
488                        var tbody, tr, td;
489                       
490                        if(tbodies.length == 1){
491                                tbody = tbodies[0];
492                        }else{
493                                tbody = html.create("tbody", null, table);
494                        }
495                       
496                        if(trs.length == 1){
497                                tr = trs[0];
498                        }else{
499                                tr = domConstruct.create("tr", null, tbody);
500                        }                       
501                                                 
502                        // Build HTML structure (incremental)
503                        if(count > 0){ // creation                             
504                                for(var i=0; i < count; i++){
505                                        td = domConstruct.create("td", null, tr);
506                                }
507                        }else{ // deletion
508                                count = -count;
509                                for(var i=0; i < count; i++){
510                                        tr.removeChild(tr.lastChild);
511                                }
512                        }
513                       
514                        // fill & configure             
515                        query("td", table).forEach(function(td, i){
516                                td.className = "";
517                                var d = renderData.dates[0][i];
518                                this._setText(td, this._formatColumnHeaderLabel(d));
519                                if(i == 0){
520                                        domClass.add(td, "first-child");
521                                }else if(i == this.renderData.columnCount-1){
522                                        domClass.add(td, "last-child");
523                                }
524                                this.styleColumnHeaderCell(td, d, renderData);
525                        }, this);
526                       
527                        if(this.yearColumnHeaderContent){
528                                var d = renderData.dates[0][0];
529                                        this._setText(this.yearColumnHeaderContent, renderData.dateLocaleModule.format(d,
530                                                {selector: "date", datePattern:"yyyy"}));
531                        }
532                },
533               
534                styleColumnHeaderCell: function(node, date, renderData){
535                        // summary:
536                        //              Styles the CSS classes to the node that displays a column header cell.
537                        //              By default this method is setting the "dojoxCalendarWeekend" if the day of week represents a weekend.
538                        // node: Node
539                        //              The DOM node that displays the column in the grid.
540                        // date: Date
541                        //              The date displayed by this column
542                        // renderData: Object                   
543                        //              The render data.
544                        // tags:
545                        //              protected
546                       
547                        domClass.add(node, this._cssDays[date.getDay()]);
548
549                        if(this.isWeekEnd(date)){
550                                domClass.add(node, "dojoxCalendarWeekend");
551                        }       
552                },             
553               
554                _rowHeaderHandles: null,
555               
556                _cleanupRowHeader: function(){
557                        // tags:
558                        //              private
559
560                        while(this._rowHeaderHandles.length > 0){
561                                var list = this._rowHeaderHandles.pop();
562                                while(list.length>0){
563                                        list.pop().remove();
564                                }
565                        }
566                },
567
568               
569                _rowHeaderClick: function(e){
570                        // tags:
571                        //              private
572
573                        var index = query("td", this.rowHeaderTable).indexOf(e.currentTarget);
574                        this._onRowHeaderClick({
575                                index: index,
576                                date: this.renderData.dates[index][0],
577                                triggerEvent: e
578                        });
579                },
580                 
581                _buildRowHeader: function(renderData, oldRenderData){
582                       
583                        // summary:
584                        //              Creates incrementally the HTML structure of the row header and configures its content.                 
585                        //
586                        // renderData:
587                        //              The render data to display.
588                        //
589                        // oldRenderData:
590                        //              The previously render data displayed, if any.
591                        // tags:
592                        //              private
593                       
594                        var rowHeaderTable = this.rowHeaderTable;
595                       
596                        if(!rowHeaderTable){
597                                return;
598                        }
599
600                        var tbodies = query("tbody", rowHeaderTable);                   
601                        var tbody, tr, td;
602                       
603                        if(tbodies.length == 1){
604                                tbody = tbodies[0];
605                        }else{
606                                tbody = domConstruct.create("tbody", null, rowHeaderTable);
607                        }                               
608                       
609                        var count = renderData.rowCount - (oldRenderData ? oldRenderData.rowCount : 0);
610                       
611                        // Build HTML structure
612                        if(count>0){ // creation
613                                for(var i=0; i < count; i++){
614                                        tr = domConstruct.create("tr", null, tbody);
615                                        td = domConstruct.create("td", null, tr);
616                                       
617                                        var h = [];
618                                               
619                                        h.push(on(td, "click", lang.hitch(this, this._rowHeaderClick)));
620                                       
621                                        if(!has("touch")){
622                                                h.push(on(td, "mousedown", function(e){
623                                                        domClass.add(e.currentTarget, "Active");
624                                                }));
625                                               
626                                                h.push(on(td, "mouseup", function(e){
627                                                        domClass.remove(e.currentTarget, "Active");
628                                                }));
629                                               
630                                                h.push(on(td, "mouseover", function(e){
631                                                        domClass.add(e.currentTarget, "Hover");
632                                                }));
633                                               
634                                                h.push(on(td, "mouseout", function(e){
635                                                        domClass.remove(e.currentTarget, "Hover");
636                                                }));
637                                        }
638                                        this._rowHeaderHandles.push(h);
639                                }
640                        }else{
641                                count = -count;
642                                // deletion of existing nodes
643                                for(var i=0; i < count; i++){
644                                        tbody.removeChild(tbody.lastChild);
645                                        var list = this._rowHeaderHandles.pop();
646                                        while(list.length>0){
647                                                list.pop().remove();
648                                        }
649                                }
650                        }
651
652                        // fill labels
653
654                        query("tr", rowHeaderTable).forEach(function(tr, i){
655
656                                domStyle.set(tr, "height", this._getRowHeight(i) + "px");
657                               
658                                var d = renderData.dates[i][0];
659                               
660                                var td = query("td", tr)[0];
661                                td.className = "";
662                                if(i == 0){
663                                        domClass.add(td, "first-child");
664                                }       
665                                if(i == this.renderData.rowCount-1){
666                                        domClass.add(td, "last-child");
667                                }
668                                                               
669                                this.styleRowHeaderCell(td, d, renderData);
670
671                                this._setText(td, this._formatRowHeaderLabel(d));
672                        }, this);
673
674                },             
675               
676                styleRowHeaderCell: function(node, date, renderData){
677                        // summary:
678                        //              Styles the CSS classes to the node that displays a row header cell.
679                        //              By default this method is doing nothing.
680                        // node: Node
681                        //              The DOM node that displays the column in the grid.
682                        // date: Date
683                        //              The date in the week.
684                        // renderData: Object
685                        //              The render data.
686                        // tags:
687                        //              protected
688
689                               
690                },
691       
692                _buildGrid: function (renderData, oldRenderData){
693                        // summary:
694                        //              Creates incrementally the HTML structure of the grid and configures its content.
695                        //
696                        // renderData:
697                        //              The render data to display.
698                        //
699                        // oldRenderData:
700                        //              The previously render data displayed, if any.
701                        // tags:
702                        //              private
703
704                        var table = this.gridTable;
705                       
706                        if(!table){
707                                return;
708                        }
709                       
710                        var currentTR = query("tr", table);
711
712                        var rowDiff = renderData.rowCount - currentTR.length;
713                        var addRows = rowDiff > 0;
714                       
715                        var colDiff  = renderData.columnCount - (oldRenderData ? oldRenderData.columnCount : 0);
716                       
717                        if(has("ie") == 8){
718                                // workaround Internet Explorer 8 bug.
719                                // if on the table, width: 100% and table-layout: fixed are set
720                                // and columns are removed, width of remaining columns is not
721                                // recomputed: must rebuild all.
722                                if(this._gridTableSave == null){
723                                        this._gridTableSave = lang.clone(table);
724                                }else if(colDiff < 0){                                 
725                                        this.grid.removeChild(table);
726                                        domConstruct.destroy(table);
727                                        table = lang.clone(this._gridTableSave);
728                                        this.gridTable = table;
729                                        this.grid.appendChild(table);
730                                        colDiff = renderData.columnCount;
731                                        rowDiff = renderData.rowCount;
732                                        addRows = true;
733                                }                               
734                        }
735                       
736                        var tbodies = query("tbody", table);
737                        var tbody;
738
739                        if(tbodies.length == 1){
740                                tbody = tbodies[0];
741                        }else{
742                                tbody = domConstruct.create("tbody", null, table);
743                        }
744
745                        // Build rows HTML structure (incremental)
746                        if(addRows){ // creation
747                                for(var i=0; i<rowDiff; i++){
748                                        domConstruct.create("tr", null, tbody);
749                                }               
750                        }else{ // deletion               
751                                rowDiff = -rowDiff;
752                                for(var i=0; i<rowDiff; i++){
753                                        tbody.removeChild(tbody.lastChild);
754                                }
755                        }
756
757                        var rowIndex = renderData.rowCount - rowDiff;
758                       
759                        var addCols = addRows || colDiff >0;
760                        colDiff = addCols ? colDiff : -colDiff;
761                       
762                        query("tr", table).forEach(function(tr, i){
763                               
764                                if(addCols){ // creation
765                                        var len = i >= rowIndex ? renderData.columnCount : colDiff;
766                                        for(var i=0; i<len; i++){
767                                                var td = domConstruct.create("td", null, tr);
768                                                domConstruct.create("span", null, td);
769                                        }
770                                }else{ // deletion
771                                        for(var i=0; i<colDiff; i++){
772                                                tr.removeChild(tr.lastChild);
773                                        }
774                                }
775                        });
776
777                        // Set the CSS classes
778
779                        query("tr", table).forEach(function (tr, row){
780                               
781                                domStyle.set(tr, "height", this._getRowHeight(row) + "px");
782                               
783                                tr.className = "";
784                                // compatibility layer for IE7 & 8 that does not support :first-child and :last-child pseudo selectors
785                                if(row == 0){
786                                        domClass.add(tr, "first-child");
787                                }
788                                if(row == renderData.rowCount-1){
789                                        domClass.add(tr, "last-child");
790                                }
791
792                                query("td", tr).forEach(function (td, col){
793                                       
794                                        td.className = "";
795                                       
796                                        if(col == 0){
797                                                domClass.add(td, "first-child");
798                                        }
799                                       
800                                        if(col == renderData.columnCount-1){
801                                                domClass.add(td, "last-child");
802                                        }
803                                       
804                                        var d = renderData.dates[row][col];
805                                       
806                                        var span = query("span", td)[0];
807                                        this._setText(span, this.showCellLabel ? this._formatGridCellLabel(d, row, col): null);
808                                       
809                                        this.styleGridCell(td, d, renderData);
810                                }, this);
811                        }, this);
812
813                },
814               
815                // styleGridCellFunc: Function
816                //              Custom function to customize the appearance of a grid cell by installing custom CSS class on the node.
817                //              The signature of the function must be the same then the styleGridCell one.
818                //              By default the defaultStyleGridCell function is used.
819                styleGridCellFunc: null,
820               
821                defaultStyleGridCell: function(node, date, renderData){
822                        // summary:
823                        //              Styles the CSS classes to the node that displays a cell.
824                        //              By default this method is setting the following CSS classes:
825                        //              - "dojoxCalendarToday" class name if the date displayed is the current date,
826                        //              - "dojoxCalendarWeekend" if the date represents a weekend or
827                        //              - "dojoxCalendarDayDisabled" if the date is out of the [refStartTime, refEndTime] interval.
828                        //              - the CSS class corresponding of the displayed day of week ("Sun", "Mon" and so on).                   
829                        // node: Node
830                        //              The DOM node that displays the cell in the grid.
831                        // date: Date
832                        //              The date displayed by this cell.
833                        // renderData: Object
834                        //              The render data.
835                        // tags:
836                        //              protected
837                       
838                        domClass.add(node, this._cssDays[date.getDay()]);
839                       
840                        var cal = this.dateModule;
841                        if(this.isToday(date)){                         
842                                domClass.add(node, "dojoxCalendarToday");
843                        }else if(this.refStartTime != null && this.refEndTime != null &&
844                                                                (cal.compare(date, this.refEndTime) >= 0 ||
845                                                                 cal.compare(cal.add(date, "day", 1), this.refStartTime) <= 0)){
846                                domClass.add(node, "dojoxCalendarDayDisabled");
847                        }else if(this.isWeekEnd(date)){
848                                domClass.add(node, "dojoxCalendarWeekend");
849                        }       
850                },
851               
852                styleGridCell: function(node, date, renderData){
853                        // summary:
854                        //              Styles the CSS classes to the node that displays a cell.
855                        //              Delegates to styleGridCellFunc if defined or defaultStyleGridCell otherwise.
856                        // node: Node
857                        //              The DOM node that displays the cell in the grid.
858                        // date: Date
859                        //              The date displayed by this cell.
860                        // renderData: Object
861                        //              The render data.
862                        // tags:
863                        //              protected
864                        if(this.styleGridCellFunc){
865                                this.styleGridCellFunc(node, date, renderData);
866                        }else{
867                                this.defaultStyleGridCell(node, date, renderData);
868                        }
869                },
870
871                _buildItemContainer: function(renderData, oldRenderData){
872                        // summary:
873                        //              Creates the HTML structure of the item container and configures its content.
874                        //
875                        // renderData:
876                        //              The render data to display.
877                        //
878                        // oldRenderData:
879                        //              The previously render data displayed, if any.
880                        // tags:
881                        //              private
882                       
883                        var table = this.itemContainerTable;
884                       
885                        if(!table){
886                                return;
887                        }
888                       
889                        var rows = [];
890       
891                        var count = renderData.rowCount - (oldRenderData ? oldRenderData.rowCount : 0)
892                       
893                        if(has("ie") == 8){
894                                // workaround Internet Explorer 8 bug.
895                                // if on the table, width: 100% and table-layout: fixed are set
896                                // and columns are removed, width of remaining columns is not
897                                // recomputed: must rebuild all.
898                                if(this._itemTableSave == null){
899                                        this._itemTableSave = lang.clone(table);
900                                }else if(count < 0){
901                                        this.itemContainer.removeChild(table);
902                                        this._recycleItemRenderers(true);
903                                        this._recycleExpandRenderers(true);
904                                        domConstruct.destroy(table);
905                                        table = lang.clone(this._itemTableSave);
906                                        this.itemContainerTable = table;
907                                        this.itemContainer.appendChild(table);
908                                        count = renderData.columnCount;
909                                }
910                               
911                        } // else incremental dom add/remove for real browsers.
912                       
913                        var tbodies = query("tbody", table);
914                        var tbody, tr, td, div;
915                       
916                        if(tbodies.length == 1){
917                                tbody = tbodies[0];
918                        }else{
919                                tbody = domConstruct.create("tbody", null, table);
920                        }
921                       
922                        // Build HTML structure (incremental)
923                        if(count>0){ // creation
924                                for(var i=0; i < count; i++){
925                                        tr = domConstruct.create("tr", null, tbody);
926                                        domClass.add(tr, "dojoxCalendarItemContainerRow");
927                                        td = domConstruct.create("td", null, tr);       
928                                        div = domConstruct.create("div", null, td);
929                                        domClass.add(div, "dojoxCalendarContainerRow");
930                                }
931                        }else{ // deletion               
932                                count = -count;
933                                for(var i=0; i < count; i++){
934                                        tbody.removeChild(tbody.lastChild);
935                                }
936                        }
937
938                        query(".dojoxCalendarItemContainerRow", table).forEach(function(tr, i){
939                                domStyle.set(tr, "height", this._getRowHeight(i) + "px");
940                                rows.push(tr.childNodes[0].childNodes[0]);
941                        }, this);
942
943                        renderData.cells = rows;
944                },
945               
946                resize: function(changeSize){
947                        this.inherited(arguments);
948                        this._resizeHandler(null, false);
949                },
950
951                _resizeHandler: function(e, apply){
952                        // summary:
953                        //              Refreshes and apply the row height according to the widget height.
954                        // e: Event
955                        //              The resize event (optional)
956                        // apply: Boolean
957                        //              Whether take into account the layoutDuringResize flag to relayout item while resizing or not.
958                        // tags:
959                        //              private
960
961                        var rd = this.renderData;
962                       
963                        if(rd == null){
964                                this.refreshRendering();
965                                return;
966                        }
967
968                        if(rd.sheetHeight != this.itemContainer.offsetHeight){
969                                // refresh values
970                                rd.sheetHeight = this.itemContainer.offsetHeight;
971                                var expRow = this.getExpandedRowIndex();
972                                if(expRow == -1){
973                                        this._computeRowsHeight();
974                                        this._resizeRows();
975                                }else{
976                                        this.expandRow(rd.expandedRow, rd.expandedRowCol, 0, null, true);
977                                }
978                                if(rd.invalidRowHeight){
979                                        // complete recompute
980                                        delete rd.invalidRowHeight;
981                                        this.renderData = null;
982                                        this.displayedItemsInvalidated = true;
983                                        this.refreshRendering();
984                                        return;
985                                }
986                        }
987                       
988                        if(this.layoutDuringResize || apply){
989                                // Use a time for FF (at least). In FF the cell size and position info are not ready yet.
990                                setTimeout(lang.hitch(this, function(){
991                                        this._layoutRenderers(this.renderData);
992                                  }), 20);
993                                                               
994                        }else{
995                                domStyle.set(this.itemContainer, "opacity", 0);
996                                this._recycleItemRenderers();
997                                this._recycleExpandRenderers();
998                                if(this._resizeTimer != undefined){
999                                        clearTimeout(this._resizeTimer);
1000                                }
1001                                this._resizeTimer = setTimeout(lang.hitch(this, function(){
1002                                        delete this._resizeTimer;
1003                                        this._resizeRowsImpl(this.itemContainer, "tr");
1004                                        this._layoutRenderers(this.renderData);
1005                                        if(this.resizeAnimationDuration == 0){
1006                                                domStyle.set(this.itemContainer, "opacity", 1);
1007                                        }else{
1008                                                fx.fadeIn({node:this.itemContainer, curve:[0, 1]}).play(this.resizeAnimationDuration);
1009                                        }                                       
1010                                }), 200);
1011                        }
1012
1013                },
1014               
1015                // resizeAnimationDuration: Integer
1016                //              Duration, in milliseconds, of the fade animation showing the item renderers after a widget resize.
1017                resizeAnimationDuration: 0,
1018               
1019                /////////////////////////////////////////////
1020                //
1021                // Row height management
1022                //
1023                //////////////////////////////////////////////
1024               
1025                getExpandedRowIndex: function(){
1026                        // summary:
1027                        //              Returns the index of the expanded row or -1 if there's no row expanded.
1028                        return this.renderData.expandedRow == null ? -1 : this.renderData.expandedRow;
1029                },
1030               
1031                collapseRow: function(duration, easing, apply){
1032                        // summary:
1033                        //              Collapses the expanded row, if any.
1034                        // duration: Integer
1035                        //              Duration in milliseconds of the optional animation.
1036                        // easing: Function
1037                        //              Easing function of the optional animation.
1038                       
1039                        var rd = this.renderData;
1040                       
1041                        if(apply == undefined){
1042                                apply = true;
1043                        }
1044                        if(duration == undefined){
1045                                duration = this.expandDuration;
1046                        }
1047                       
1048                        if(rd && rd.expandedRow != null && rd.expandedRow != -1){
1049                                if(apply && duration){
1050                                        var index = rd.expandedRow;
1051                                        var oldSize = rd.expandedRowHeight;
1052                                        delete rd.expandedRow;
1053                                        this._computeRowsHeight(rd);
1054                                        var size = this._getRowHeight(index);
1055                                        rd.expandedRow = index;
1056                                       
1057                                        this._recycleExpandRenderers();
1058                                        this._recycleItemRenderers();
1059                                        domStyle.set(this.itemContainer, "display", "none");
1060                                       
1061                                        this._expandAnimation = new fx.Animation({
1062                                                curve: [oldSize, size],
1063                                                duration: duration,
1064                                                easing: easing,
1065                                                onAnimate: lang.hitch(this, function(size) {
1066                                                        this._expandRowImpl(Math.floor(size));
1067                                                }),
1068                                                onEnd: lang.hitch(this, function(size) {
1069                                                        this._expandAnimation = null;
1070                                                        this._collapseRowImpl(false);
1071                                                        this._resizeRows();
1072                                                        domStyle.set(this.itemContainer, "display", "block");
1073                                                        setTimeout(lang.hitch(this, function(){                                                         
1074                                                                this._layoutRenderers(rd);                                                             
1075                                                        }), 100);
1076                                                        this.onExpandAnimationEnd(false);
1077                                                })
1078                                        });
1079                                                                       
1080                                        this._expandAnimation.play();
1081                                }else{
1082                                        this._collapseRowImpl(apply);
1083                                }                               
1084                        }
1085                },
1086               
1087                _collapseRowImpl: function(apply){
1088                        // tags:
1089                        //              private
1090
1091                        var rd = this.renderData;
1092                        delete rd.expandedRow;
1093                        delete rd.expandedRowHeight;
1094                        this._computeRowsHeight(rd);
1095                        if(apply == undefined || apply){
1096                                this._resizeRows();
1097                                this._layoutRenderers(rd);
1098                        }
1099                },
1100               
1101                expandRow: function(rowIndex, colIndex, duration, easing, apply){
1102                        // summary:
1103                        //              Expands the specified row.
1104                        // rowIndex: Integer
1105                        //              The index of the row to expand.
1106                        // colIndex: Integer?
1107                        //              The column index of the expand renderer that triggers the action, optional.
1108                        // duration: Integer?
1109                        //              Duration in milliseconds of the optional animation.
1110                        // easing: Function?
1111                        //              Easing function of the optional animation.
1112                       
1113                        var rd = this.renderData;
1114                        if(!rd || rowIndex < 0 || rowIndex >= rd.rowCount){
1115                                return -1;
1116                        }
1117                        if(colIndex == undefined || colIndex < 0 || colIndex >= rd.columnCount){
1118                                colIndex = -1; // ignore invalid values
1119                        }
1120                        if(apply == undefined){
1121                                apply = true;
1122                        }
1123                        if(duration == undefined){
1124                                duration = this.expandDuration;
1125                        }
1126                        if(easing == undefined){
1127                                easing = this.expandEasing;
1128                        }
1129                       
1130                        var oldSize = this._getRowHeight(rowIndex);
1131                        var size = rd.sheetHeight - Math.ceil(this.cellPaddingTop * (rd.rowCount-1));
1132
1133                        rd.expandedRow = rowIndex;
1134                        rd.expandedRowCol = colIndex;
1135                        rd.expandedRowHeight = size;
1136
1137                        if(apply){
1138                                if(duration){
1139                                        //debugger;
1140                                        this._recycleExpandRenderers();
1141                                        this._recycleItemRenderers();
1142                                        domStyle.set(this.itemContainer, "display", "none");
1143                                       
1144                                        this._expandAnimation = new fx.Animation({
1145                                                curve: [oldSize, size],
1146                                                duration: duration,
1147                                                delay:50,
1148                                                easing: easing,
1149                                                onAnimate: lang.hitch(this, function(size) {
1150                                                        this._expandRowImpl(Math.floor(size));
1151                                                }),
1152                                                onEnd: lang.hitch(this, function(){
1153                                                        this._expandAnimation = null;
1154                                                        domStyle.set(this.itemContainer, "display", "block");
1155                                                        setTimeout(lang.hitch(this, function(){
1156                                                                this._expandRowImpl(size, true);
1157                                                        }), 100);
1158                                                        this.onExpandAnimationEnd(true);
1159                                                })
1160                                        });
1161                                        this._expandAnimation.play();
1162                                }else{
1163                                        this._expandRowImpl(size, true);
1164                                }
1165                        }                       
1166                },
1167               
1168                _expandRowImpl: function(size, layout){
1169                        // tags:
1170                        //              private
1171
1172                        var rd = this.renderData;
1173                        rd.expandedRowHeight = size;
1174                        this._computeRowsHeight(rd, rd.sheetHeight-size);
1175                        this._resizeRows();
1176                        if(layout){
1177                                this._layoutRenderers(rd);
1178                        }
1179                },
1180               
1181                onExpandAnimationEnd: function(expand){
1182                        // summary:
1183                        //              Event dispatched at the end of an expand or collapse animation.
1184                        // expand: Boolean
1185                        //              Whether the finished animation was an expand or a collapse animation.
1186                        // tags:
1187                        //              callback
1188
1189                },
1190               
1191                _resizeRows: function(){
1192                        // summary:
1193                        //              Refreshes the height of the underlying HTML objects.
1194                        // tags:
1195                        //              private
1196                       
1197                        if(this._getRowHeight(0) <= 0){
1198                                return;
1199                        }
1200                       
1201                        if(this.rowHeaderTable){
1202                                this._resizeRowsImpl(this.rowHeaderTable, "tr");
1203                        }
1204                        if(this.gridTable){
1205                                this._resizeRowsImpl(this.gridTable, "tr");
1206                        }
1207                        if(this.itemContainerTable){
1208                                this._resizeRowsImpl(this.itemContainerTable, "tr");
1209                        }
1210                },
1211               
1212                _computeRowsHeight:function(renderData, max){
1213                        // summary:
1214                        //              1. Determine if it's better to add or remove pixels
1215                        //              2. distribute added/removed pixels on first and last rows.
1216                        //              if rows are not too small, it is not noticeable.
1217                        // tags:
1218                        //              private
1219
1220                        var rd = renderData == null ? this.renderData : renderData;
1221                       
1222                        max = max || rd.sheetHeight;
1223                       
1224                        max--;
1225                       
1226                        if(has("ie") == 7){
1227                                max -= rd.rowCount;
1228                        }
1229                       
1230                        if(rd.rowCount == 1){
1231                                rd.rowHeight = max;
1232                                rd.rowHeightFirst = max;
1233                                rd.rowHeightLast = max;
1234                                return;
1235                        }
1236                                                               
1237                        var count = rd.expandedRow == null ? rd.rowCount : rd.rowCount-1;
1238                        var rhx = max / count;
1239                        var rhf, rhl, rh;
1240                       
1241                        var diffMin = max - (Math.floor(rhx) * count);
1242                        var diffMax = Math.abs(max - (Math.ceil(rhx) * count));
1243                        var diff;
1244                       
1245                        var sign = 1;
1246                        if(diffMin < diffMax){
1247                                rh = Math.floor(rhx);
1248                                diff = diffMin;
1249                        }else{
1250                                sign = -1;
1251                                rh = Math.ceil(rhx);
1252                                diff = diffMax;
1253                        }
1254                        rhf = rh + sign * Math.floor(diff/2);
1255                        rhl = rhf + sign * (diff%2);
1256
1257                        rd.rowHeight = rh;
1258                        rd.rowHeightFirst = rhf;
1259                        rd.rowHeightLast = rhl;
1260                },
1261
1262                _getRowHeight: function(index){
1263                        // tags:
1264                        //              private
1265
1266                        var rd = this.renderData;
1267                        if(index == rd.expandedRow){
1268                                return rd.expandedRowHeight;
1269                        } else if(rd.expandedRow == 0 && index == 1 || index == 0){
1270                                return rd.rowHeightFirst;
1271                        } else if(rd.expandedRow == this.renderData.rowCount-1 &&
1272                                                                index == this.renderData.rowCount-2 ||
1273                                                                index == this.renderData.rowCount-1){
1274                                return rd.rowHeightLast;
1275                        }else{
1276                                return rd.rowHeight;
1277                        }
1278                },
1279
1280                _resizeRowsImpl: function(tableNode, query){
1281                        // tags:
1282                        //              private
1283                        dojo.query(query, tableNode).forEach(function(tr, i){
1284                                domStyle.set(tr, "height", this._getRowHeight(i)+"px");
1285                        }, this);
1286                },
1287
1288                ////////////////////////////////////////////
1289                //
1290                // Item renderers
1291                //
1292                ///////////////////////////////////////////
1293                               
1294                _setHorizontalRendererAttr: function(value){
1295                        this._destroyRenderersByKind("horizontal");
1296                        this._set("horizontalRenderer", value);                 
1297                },
1298               
1299                _setLabelRendererAttr: function(value){
1300                        this._destroyRenderersByKind("label");                 
1301                        this._set("labelRenderer", value);
1302                },
1303               
1304                _destroyExpandRenderer: function(renderer){
1305                        // summary:
1306                        //              Destroys the expand renderer.
1307                        // renderer: dojox/calendar/_RendererMixin
1308                        //              The item renderer to destroy.
1309                        // tags:
1310                        //              protected
1311                       
1312                        if(renderer["destroyRecursive"]){
1313                                renderer.destroyRecursive();
1314                        }
1315                       
1316                        html.destroy(renderer.domNode);
1317                },
1318               
1319                _setExpandRendererAttr: function(value){
1320                        while(this._ddRendererList.length>0){
1321                                this._destroyExpandRenderer(this._ddRendererList.pop());
1322                        }                       
1323                                               
1324                        var pool = this._ddRendererPool;
1325                        if(pool){
1326                                while(pool.length > 0){
1327                                        this._destroyExpandRenderer(pool.pop());
1328                                }
1329                        }                                                       
1330                        this._set("expandRenderer", value);
1331                },                             
1332               
1333                _ddRendererList: null,
1334                _ddRendererPool: null,
1335
1336                _getExpandRenderer: function(date, items, rowIndex, colIndex, expanded){
1337                        // tags:
1338                        //              private
1339                       
1340                        if(this.expandRenderer == null){
1341                                return null;
1342                        }
1343                       
1344                        var ir = this._ddRendererPool.pop();
1345                        if(ir == null){
1346                                ir = new this.expandRenderer();
1347                        }
1348                       
1349                        this._ddRendererList.push(ir);
1350                       
1351                        ir.set("owner", this);
1352                        ir.set("date", date);
1353                        ir.set("items", items);
1354                        ir.set("rowIndex", rowIndex);
1355                        ir.set("columnIndex", colIndex);
1356                        ir.set("expanded", expanded);
1357                        return ir;
1358                },
1359               
1360                _recycleExpandRenderers: function(remove){
1361                        // tags:
1362                        //              private
1363                       
1364                        for(var i=0; i<this._ddRendererList.length; i++){
1365                                var ir = this._ddRendererList[i];
1366                                ir.set("Up", false);
1367                                ir.set("Down", false);
1368                                if(remove){
1369                                        ir.domNode.parentNode.removeChild(ir.domNode);
1370                                }
1371                                domStyle.set(ir.domNode, "display", "none");
1372                        }
1373                        this._ddRendererPool = this._ddRendererPool.concat(this._ddRendererList);
1374                        this._ddRendererList = [];
1375                },
1376
1377                _defaultItemToRendererKindFunc:function(item){
1378                        // tags:
1379                        //              private
1380                        var dur = Math.abs(this.renderData.dateModule.difference(item.startTime, item.endTime, "minute"));
1381                        return dur >= 1440 ? "horizontal" : "label";
1382                },
1383               
1384                ////////////////////////////////////////////
1385                //
1386                // Layout
1387                //
1388                ///////////////////////////////////////////
1389               
1390                // naturalRowHeight: Integer[]
1391                //              After an item layout has been done, contains for each row the natural height of the row.
1392                //              Ie. the height, in pixels, needed to display all the item renderers.
1393                naturalRowsHeight: null,
1394               
1395                _roundItemToDay: function(item){
1396                        // tags:
1397                        //              private
1398                       
1399                        var s = item.startTime, e = item.endTime;
1400                       
1401                        if(!this.isStartOfDay(s)){
1402                                s = this.floorToDay(s, false, this.renderData);
1403                        }
1404                        if(!this.isStartOfDay(e)){
1405                                e = this.renderData.dateModule.add(e, "day", 1);
1406                                e = this.floorToDay(e, true);
1407                        }
1408                        return {startTime:s, endTime:e};
1409                },
1410               
1411                _sortItemsFunction: function(a, b){
1412                        // tags:
1413                        //              private
1414                       
1415                        if(this.roundToDay){
1416                                a = this._roundItemToDay(a);
1417                                b = this._roundItemToDay(b);
1418                        }
1419                        var res = this.dateModule.compare(a.startTime, b.startTime);
1420                        if(res == 0){
1421                                res = -1 * this.dateModule.compare(a.endTime, b.endTime);
1422                        }
1423                        return res;
1424                },
1425               
1426                _overlapLayoutPass3: function(lanes){
1427                        // summary:
1428                        //              Third pass of the overlap layout (optional). Compute the number of lanes used by sub interval.
1429                        // lanes: Object[]
1430                        //              The array of lanes.
1431                        // tags:
1432                        //              private
1433
1434                        var pos=0, posEnd=0;
1435                        var res = [];
1436                       
1437                        var refPos = domGeometry.position(this.gridTable).x;
1438                       
1439                        for(var col=0; col<this.renderData.columnCount; col++){
1440                               
1441                                var stop = false;
1442                                var colPos = domGeometry.position(this._getCellAt(0, col));
1443                                pos = colPos.x - refPos;
1444                                posEnd = pos + colPos.w;
1445                               
1446                                for(var lane=lanes.length-1; lane>=0 && !stop; lane--){
1447                                        for (var i=0; i<lanes[lane].length; i++){
1448                                                var item = lanes[lane][i];
1449                                                stop = item.start < posEnd && pos < item.end;
1450                                                if(stop){
1451                                                        res[col] = lane + 1;
1452                                                        break;
1453                                                }
1454                                        }
1455                                }
1456                               
1457                                if(!stop){
1458                                        res[col] = 0;
1459                                }
1460                        }
1461                       
1462                        return res;
1463                },
1464               
1465                applyRendererZIndex: function(item, renderer, hovered, selected, edited, focused){
1466                        // summary:
1467                        //              Applies the z-index to the renderer based on the state of the item.
1468                        //              This methods is setting a z-index of 20 is the item is selected or edited
1469                        //              and the current lane value computed by the overlap layout (i.e. the renderers
1470                        //              are stacked according to their lane).
1471                        // item: Object
1472                        //              The render item.
1473                        // renderer: Object
1474                        //              A renderer associated with the render item.
1475                        // hovered: Boolean
1476                        //              Whether the item is hovered or not.
1477                        // selected: Boolean
1478                        //              Whether the item is selected or not.
1479                        // edited: Boolean
1480                        //              Whether the item is being edited not not.
1481                        // focused: Boolean
1482                        //              Whether the item is focused not not.
1483                        // tags:
1484                        //              private
1485                                               
1486                        domStyle.set(renderer.container, {"zIndex": edited || selected ? renderer.renderer.mobile ? 100 : 0: item.lane == undefined ? 1 : item.lane+1});
1487                },
1488
1489                _layoutRenderers: function(renderData){
1490                        // tags:
1491                        //              private
1492                        if(renderData == null || renderData.items == null || renderData.rowHeight <= 0){
1493                                return;
1494                        }                                       
1495                       
1496                        if(!this.gridTable || this._expandAnimation != null ||
1497                                (this.horizontalRenderer == null && this.labelRenderer == null)){
1498                                this._recycleItemRenderers();
1499                                return;
1500                        }
1501                       
1502                        this.renderData.gridTablePosX = domGeometry.position(this.gridTable).x;         
1503                        this._layoutStep = renderData.columnCount;
1504                        this._recycleExpandRenderers();
1505                        this._hiddenItems = [];
1506                        this._offsets = [];
1507                        this.naturalRowsHeight = [];
1508                       
1509                        this.inherited(arguments);
1510                },
1511
1512                _offsets: null,
1513
1514                _layoutInterval: function(/*Object*/renderData, /*Integer*/index, /*Date*/start, /*Date*/end, /*Object[]*/items){
1515                        // tags:
1516                        //              private
1517                       
1518                        if(this.renderData.cells == null){
1519                                return;
1520                        }
1521                        var horizontalItems = [];
1522                        var labelItems = [];
1523
1524                        for(var i=0; i<items.length; i++){
1525                                var item = items[i];
1526                                var kind = this._itemToRendererKind(item);
1527                                if(kind == "horizontal"){
1528                                        horizontalItems.push(item);
1529                                }else if(kind == "label"){
1530                                        labelItems.push(item);
1531                                }
1532                        }
1533                       
1534                        var expIndex = this.getExpandedRowIndex();
1535                       
1536                        if(expIndex != -1 && expIndex != index){
1537                                return; // when row is expanded, layout only expanded row
1538                        }
1539                       
1540                        var offsets;
1541                       
1542                        var hiddenItems = [];
1543                       
1544                        var hItems = null;
1545                        var hOffsets = [];
1546                        if(horizontalItems.length > 0 && this.horizontalRenderer){
1547                                var hItems = this._createHorizontalLayoutItems(index, start, end, horizontalItems);
1548                                var hOverlapLayout = this._computeHorizontalOverlapLayout(hItems, hOffsets);
1549                        }
1550                       
1551                        var lItems;
1552                        var lOffsets = [];
1553                        if(labelItems.length > 0 && this.labelRenderer){
1554                                lItems = this._createLabelLayoutItems(index, start, end, labelItems);
1555                                this._computeLabelOffsets(lItems, lOffsets);
1556                        }
1557                       
1558                        var hasHiddenItems = this._computeColHasHiddenItems(index, hOffsets, lOffsets);
1559                       
1560                        if(hItems != null){
1561                                this._layoutHorizontalItemsImpl(index, hItems, hOverlapLayout, hasHiddenItems, hiddenItems);
1562                        }
1563                       
1564                        if(lItems != null){
1565                                this._layoutLabelItemsImpl(index, lItems, hasHiddenItems, hiddenItems, hOffsets);
1566                        }
1567                       
1568                        this._layoutExpandRenderers(index, hasHiddenItems, hiddenItems);
1569                       
1570                        this._hiddenItems[index] = hiddenItems;
1571                },
1572
1573                _createHorizontalLayoutItems: function(/*Integer*/index, /*Date*/startTime, /*Date*/endTime, /*Object[]*/items){
1574                        // tags:
1575                        //              private
1576                       
1577                        if(this.horizontalRenderer == null){
1578                                return;
1579                        }
1580
1581                        var rd = this.renderData;
1582                        var cal = rd.dateModule;
1583                        var sign = rd.rtl ? -1 : 1;
1584                        var layoutItems = [];
1585
1586                        // step 1: compute projected position and size
1587                        for(var i = 0; i < items.length; i++){
1588                               
1589                                var item = items[i];
1590                                var overlap = this.computeRangeOverlap(rd, item.startTime, item.endTime, startTime, endTime);
1591                               
1592                                var startOffset = cal.difference(startTime, this.floorToDay(overlap[0], false, rd), "day");
1593                                var dayStart = rd.dates[index][startOffset];
1594                               
1595                                var celPos = domGeometry.position(this._getCellAt(index, startOffset, false));
1596                                var start = celPos.x - rd.gridTablePosX;
1597                                if(rd.rtl){
1598                                        start += celPos.w;
1599                                }
1600                               
1601                                if(!this.roundToDay && !item.allDay){
1602                                        start += sign * this.computeProjectionOnDate(rd, dayStart, overlap[0], celPos.w);
1603                                }
1604                               
1605                                start = Math.ceil(start);
1606                               
1607                                var endOffset = cal.difference(startTime, this.floorToDay(overlap[1], false, rd), "day");
1608                               
1609                                var end;
1610                                if(endOffset > rd.columnCount-1){
1611                                        celPos = domGeometry.position(this._getCellAt(index, rd.columnCount-1, false));
1612                                        if(rd.rtl){
1613                                                end = celPos.x - rd.gridTablePosX;                                             
1614                                        }else{
1615                                                end = celPos.x - rd.gridTablePosX + celPos.w;
1616                                        }                               
1617                                }else{
1618                                        dayStart = rd.dates[index][endOffset];
1619                                        celPos = domGeometry.position(this._getCellAt(index, endOffset, false));
1620                                        end = celPos.x - rd.gridTablePosX;
1621                                       
1622                                        if(rd.rtl){
1623                                                end += celPos.w;
1624                                        }
1625                                       
1626                                        if(this.roundToDay){
1627                                                if(!this.isStartOfDay(overlap[1])){
1628                                                        end += sign * celPos.w;
1629                                                }
1630                                        }else{
1631                                                end += sign * this.computeProjectionOnDate(rd, dayStart, overlap[1], celPos.w);
1632                                        }
1633                                }
1634                               
1635                                end = Math.floor(end);
1636                               
1637                                if(rd.rtl){
1638                                        var t = end;
1639                                        end = start;
1640                                        start = t;
1641                                }
1642                               
1643                                if(end > start){ // invalid items are not displayed
1644                                        var litem = lang.mixin({
1645                                                start: start,
1646                                                end: end,
1647                                                range: overlap,
1648                                                item: item,
1649                                                startOffset: startOffset,
1650                                                endOffset: endOffset
1651                                        }, item);
1652                                        layoutItems.push(litem);
1653                                }
1654                        }
1655                        return layoutItems;
1656                },
1657               
1658                _computeHorizontalOverlapLayout: function(layoutItems, offsets){
1659                        // tags:
1660                        //              private
1661                       
1662                        var rd = this.renderData;
1663                        var irHeight = this.horizontalRendererHeight;
1664                        var overlapLayoutRes = this.computeOverlapping(layoutItems, this._overlapLayoutPass3);
1665                        var vOverlap = this.percentOverlap / 100;
1666               
1667                        for(var i=0; i<rd.columnCount; i++){
1668                                var numLanes = overlapLayoutRes.addedPassRes[i];
1669                                var index = rd.rtl ? rd.columnCount - i - 1 : i;                               
1670                                if(vOverlap == 0){
1671                                        offsets[index] = numLanes == 0 ? 0 : numLanes == 1 ? irHeight : irHeight + (numLanes-1) * (irHeight + this.verticalGap);
1672                                }else{
1673                                        offsets[index] = numLanes == 0 ? 0 : numLanes * irHeight - (numLanes-1) * (vOverlap * irHeight) + this.verticalGap;
1674                                }
1675                                offsets[index] += this.cellPaddingTop;
1676                        }
1677                        return overlapLayoutRes;
1678                },
1679               
1680                _createLabelLayoutItems: function(/*Integer*/index, /*Date*/startTime, /*Date*/endTime, /*Object[]*/items){
1681                        // tags:
1682                        //              private
1683                       
1684                        if(this.labelRenderer == null){
1685                                return;
1686                        }
1687                       
1688                        var d;
1689                        var rd = this.renderData;
1690                        var cal = rd.dateModule;
1691                       
1692                        var layoutItems = [];
1693                       
1694                        for(var i = 0; i < items.length; i++){
1695                                var item = items[i];
1696                               
1697                                d = this.floorToDay(item.startTime, false, rd);
1698                                                               
1699                                var comp = this.dateModule.compare;
1700                               
1701                                // iterate on columns overlapped by this item to create one item per column
1702                                //while(d < item.endTime && d < rd.endTime){
1703                                while(comp(d, item.endTime) == -1 && comp(d, endTime) == -1){
1704                                       
1705                                        var dayEnd = cal.add(d, "day", 1);
1706                                        dayEnd = this.floorToDay(dayEnd, true);
1707                                       
1708                                        var overlap = this.computeRangeOverlap(rd, item.startTime, item.endTime, d, dayEnd);
1709                                        var startOffset = cal.difference(startTime, this.floorToDay(overlap[0], false, rd), "day");
1710                                                                               
1711                                        if(startOffset >= this.columnCount){
1712                                                // If the offset is greater than the column count
1713                                                // the item will be processed in another row.
1714                                                break;
1715                                        }
1716                                       
1717                                        if(startOffset >= 0){                                   
1718                                                var list = layoutItems[startOffset];
1719                                                if(list == null){
1720                                                        list = [];
1721                                                        layoutItems[startOffset] = list;
1722                                                }
1723                                               
1724                                                list.push(lang.mixin(
1725                                                        {       startOffset: startOffset,
1726                                                                range: overlap,
1727                                                                item: item
1728                                                        }, item));
1729                                        }
1730                                       
1731                                        d = cal.add(d, "day", 1);
1732                                        this.floorToDay(d, true);
1733                                }
1734                        }
1735                        return layoutItems;
1736                },
1737
1738                _computeLabelOffsets: function(layoutItems, offsets){
1739                        // tags:
1740                        //              private
1741                       
1742                        for(var i=0; i<this.renderData.columnCount; i++){
1743                                offsets[i] = layoutItems[i] == null ? 0 : layoutItems[i].length * (this.labelRendererHeight + this.verticalGap);
1744                        }
1745                },                     
1746
1747                _computeColHasHiddenItems: function(index, hOffsets, lOffsets){
1748                        // tags:
1749                        //              private
1750                       
1751                        var res = [];
1752                        var cellH = this._getRowHeight(index);
1753                        var h;
1754                        var maxH = 0;
1755                        for(var i=0; i<this.renderData.columnCount; i++){
1756                                h = hOffsets == null || hOffsets[i] == null ? this.cellPaddingTop : hOffsets[i];
1757                                h += lOffsets == null || lOffsets[i] == null ? 0 : lOffsets[i];
1758                                if(h > maxH){
1759                                        maxH = h;
1760                                }
1761                                res[i] = h > cellH;
1762                        }
1763                       
1764                        this.naturalRowsHeight[index] = maxH;
1765                        return res;
1766                },
1767
1768                _layoutHorizontalItemsImpl: function(index, layoutItems, hOverlapLayout, hasHiddenItems, hiddenItems){
1769                       
1770                        // tags:
1771                        //              private
1772                       
1773                        var rd = this.renderData;
1774                        var cell = rd.cells[index];
1775                        var cellH = this._getRowHeight(index);
1776                        var irHeight = this.horizontalRendererHeight;
1777                        var vOverlap = this.percentOverlap / 100;
1778
1779                        for(var i=0; i<layoutItems.length; i++){
1780
1781                                var item = layoutItems[i];
1782                                var lane = item.lane;
1783
1784                                var posY = this.cellPaddingTop;
1785
1786                                if(vOverlap == 0) {
1787                                        //no overlap and a padding between each event
1788                                        posY += lane * (irHeight + this.verticalGap);
1789                                } else {
1790                                        // an overlap   
1791                                        posY += lane * (irHeight - vOverlap * irHeight);
1792                                }
1793                               
1794                                var exp = false;
1795                                var maxH = cellH;
1796                                if(this.expandRenderer){                               
1797                                        for(var off=item.startOffset; off<=item.endOffset; off++){
1798                                                if(hasHiddenItems[off]){
1799                                                        exp = true;
1800                                                        break;
1801                                                }
1802                                        }
1803                                        maxH = exp ? cellH - this.expandRendererHeight : cellH;
1804                                }
1805                               
1806                                if(posY + irHeight <= maxH){
1807
1808                                        var ir = this._createRenderer(item, "horizontal", this.horizontalRenderer, "dojoxCalendarHorizontal");
1809       
1810                                        var fullHeight = this.isItemBeingEdited(item) && !this.liveLayout && this._isEditing;
1811                                        var h = fullHeight ? cellH - this.cellPaddingTop : irHeight;
1812                                        var w = item.end - item.start;
1813                                        if (has("ie") >= 9 && item.start + w < this.itemContainer.offsetWidth) {
1814                                                w++;
1815                                        }
1816
1817                                        domStyle.set(ir.container, {
1818                                                "top": (fullHeight ? this.cellPaddingTop : posY) + "px",
1819                                                "left": item.start + "px",
1820                                                "width": w + "px",
1821                                                "height": h + "px"
1822                                        });
1823
1824                                        this._applyRendererLayout(item, ir, cell, w, h, "horizontal");
1825
1826                                }else{
1827                                        // The items does not fit in view, fill hidden items per column
1828                                        for(var d=item.startOffset;d<item.endOffset;d++){
1829                                                if(hiddenItems[d] == null){
1830                                                        hiddenItems[d] = [item.item];
1831                                                }else{
1832                                                        hiddenItems[d].push(item.item);
1833                                                }
1834                                        }
1835                                }
1836                        }
1837                },
1838               
1839                _layoutLabelItemsImpl: function(index, layoutItems, hasHiddenItems, hiddenItems, hOffsets){
1840                        // tags:
1841                        //              private
1842                        var list, posY;
1843                        var rd = this.renderData;
1844                        var cell = rd.cells[index];
1845                        var cellH = this._getRowHeight(index);
1846                        var irHeight = this.labelRendererHeight;
1847                        var maxW = domGeometry.getMarginBox(this.itemContainer).w;
1848
1849                        for(var i=0; i<layoutItems.length; i++){
1850                                list = layoutItems[i];
1851                               
1852                                if(list != null){
1853                                       
1854                                        var maxH = this.expandRenderer ? (hasHiddenItems[i] ? cellH - this.expandRendererHeight: cellH) : cellH;
1855                                        posY = hOffsets == null || hOffsets[i] == null ? this.cellPaddingTop : hOffsets[i] + this.verticalGap;
1856                                        var celPos = domGeometry.position(this._getCellAt(index, i));
1857                                        var left = celPos.x - rd.gridTablePosX;
1858                                       
1859                                        for(var j=0; j<list.length; j++){
1860                                               
1861                                                if(posY + irHeight + this.verticalGap <= maxH){
1862                                                        var item = list[j];
1863                                                       
1864                                                        lang.mixin(item, {
1865                                                                start: left,
1866                                                                end: left + celPos.w
1867                                                        });
1868                                                       
1869                                                        var ir = this._createRenderer(item, "label", this.labelRenderer, "dojoxCalendarLabel");
1870                                                               
1871                                                        var fullHeight = this.isItemBeingEdited(item) && !this.liveLayout && this._isEditing;
1872                                                        var h = fullHeight ? this._getRowHeight(index) - this.cellPaddingTop : irHeight;
1873                                                       
1874                                                        if(rd.rtl){
1875                                                                item.start = maxW - item.end;
1876                                                                item.end = item.start + celPos.w;
1877                                                        }
1878                                                                 
1879                                                        domStyle.set(ir.container, {
1880                                                                "top": (fullHeight ? this.cellPaddingTop : posY) + "px",
1881                                                                "left": item.start + "px",
1882                                                                "width": celPos.w + "px",
1883                                                                "height": h + "px"
1884                                                        });
1885                                                       
1886                                                        this._applyRendererLayout(item, ir, cell, celPos.w, h, "label");
1887                                               
1888                                                }else{
1889                                                        break;
1890                                                }
1891                                                posY += irHeight + this.verticalGap;
1892                                        }
1893                                       
1894                                        for(var j; j<list.length; j++){
1895                                                if(hiddenItems[i] == null){
1896                                                        hiddenItems[i] = [list[j]];
1897                                                }else{
1898                                                        hiddenItems[i].push(list[j]);
1899                                                }
1900                                        }
1901                                }
1902                        }
1903                },
1904               
1905                _applyRendererLayout: function(item, ir, cell, w, h, kind){
1906                        // tags:
1907                        //              private
1908                       
1909                        var edited = this.isItemBeingEdited(item);
1910                        var selected = this.isItemSelected(item);
1911                        var hovered = this.isItemHovered(item);
1912                        var focused = this.isItemFocused(item);
1913                       
1914                        var renderer = ir.renderer;                     
1915
1916                        renderer.set("hovered", hovered);
1917                        renderer.set("selected", selected);
1918                        renderer.set("edited", edited);
1919                        renderer.set("focused", this.showFocus ? focused : false);
1920                        renderer.set("moveEnabled", this.isItemMoveEnabled(item._item, kind));
1921                        renderer.set("storeState", this.getItemStoreState(item));
1922                       
1923                        if(kind != "label"){
1924                                renderer.set("resizeEnabled", this.isItemResizeEnabled(item, kind));
1925                        }
1926
1927                        this.applyRendererZIndex(item, ir, hovered, selected, edited, focused);
1928
1929                        if(renderer.updateRendering){
1930                                renderer.updateRendering(w, h);
1931                        }
1932                                                                               
1933                        domConstruct.place(ir.container, cell);
1934                        domStyle.set(ir.container, "display", "block");
1935                },
1936               
1937                _getCellAt: function(rowIndex, columnIndex, rtl){
1938                        // tags:
1939                        //              private
1940                       
1941                        if((rtl == undefined || rtl == true) && !this.isLeftToRight()){
1942                                columnIndex = this.renderData.columnCount -1 - columnIndex;
1943                        }
1944                        return this.gridTable.childNodes[0].childNodes[rowIndex].childNodes[columnIndex];
1945                },
1946       
1947                _layoutExpandRenderers: function(index, hasHiddenItems, hiddenItems){
1948                        // tags:
1949                        //              private
1950                       
1951                        if(!this.expandRenderer){
1952                                return;
1953                        }
1954                        var rd = this.renderData;
1955                        if(rd.expandedRow == index){
1956                                if(rd.expandedRowCol != null && rd.expandedRowCol != -1){
1957                                        this._layoutExpandRendererImpl(rd.expandedRow, rd.expandedRowCol, null, true);
1958                                }
1959                        }else{
1960                                if(rd.expandedRow == null){
1961                                        for(var i=0; i<rd.columnCount; i++){
1962                                                if(hasHiddenItems[i]){
1963                                                        this._layoutExpandRendererImpl(index, rd.rtl ? rd.columnCount -1 -i: i, hiddenItems[i], false);
1964                                                }
1965                                        }
1966                                }
1967                        }
1968                },
1969               
1970                _layoutExpandRendererImpl: function(rowIndex, colIndex, items, expanded){
1971                        // tags:
1972                        //              private
1973                       
1974                        var rd = this.renderData;
1975                        var d = lang.clone(rd.dates[rowIndex][colIndex]);
1976                        var ir = null;
1977                        var cell = rd.cells[rowIndex];                                 
1978                       
1979                        ir = this._getExpandRenderer(d, items, rowIndex, colIndex, expanded);
1980                               
1981                        var dim = domGeometry.position(this._getCellAt(rowIndex, colIndex));
1982                        dim.x -= rd.gridTablePosX;
1983                        this.layoutExpandRenderer(ir, d, items, dim, this.expandRendererHeight);
1984                        domConstruct.place(ir.domNode, cell);
1985                        domStyle.set(ir.domNode, "display", "block");
1986                },
1987               
1988                layoutExpandRenderer: function(renderer, date, items, cellPosition, height){
1989                        // summary:
1990                        //              Computes and sets the position of the expand/collapse renderers.
1991                        //              By default the renderer is set to take the width of the cell and is placed at the bottom of the cell.
1992                        //              The renderer DOM node is in a row that takes all the grid width.
1993                        // renderer: Object
1994                        //              The renderer used in specified cell that indicates that some items cannot be displayed.
1995                        // date: Date
1996                        //              The date displayed by the cell.
1997                        // items: Object[]
1998                        //              The list of non visible items.
1999                        // cellPosition: Object
2000                        //              An object that contains the position (x and y properties) and size of the cell (w and h properties).
2001                        // tags:
2002                        //              private
2003                        domStyle.set(renderer.domNode, {
2004                                "left": cellPosition.x + "px",
2005                                "width": cellPosition.w + "px",
2006                                "height": height + "px",
2007                                "top":  (cellPosition.h - height -1) + "px"
2008                        });
2009                },
2010               
2011                /////////////////////////////////////////////
2012                //
2013                // Editing
2014                //
2015                //////////////////////////////////////////////
2016               
2017                _onItemEditBeginGesture: function(e){
2018                        // tags:
2019                        //              private
2020                        var p = this._edProps;
2021                       
2022                        var item = p.editedItem;
2023                        var dates = e.dates;
2024                       
2025                        var refTime = this.newDate(p.editKind == "resizeEnd" ? item.endTime : item.startTime);
2026                       
2027                        if(p.rendererKind == "label"){
2028                                // noop
2029                        }else if(e.editKind == "move" && (item.allDay || this.roundToDay)){                                                     
2030                                var cal = this.renderData.dateModule;
2031                                p.dayOffset = cal.difference(
2032                                        this.floorToDay(dates[0], false, this.renderData),
2033                                        refTime, "day");
2034                        } // else managed in super
2035                       
2036                        this.inherited(arguments);
2037                },
2038               
2039                _computeItemEditingTimes: function(item, editKind, rendererKind, times, eventSource){
2040                        // tags:
2041                        //              private
2042                        var cal = this.renderData.dateModule;
2043                        var p = this._edProps;
2044                       
2045                        if(rendererKind == "label"){ // noop
2046                        }else   if(item.allDay || this.roundToDay){             
2047                                var isStartOfDay = this.isStartOfDay(times[0]);
2048                                switch(editKind){
2049                                        case "resizeEnd":
2050                                                if(!isStartOfDay && item.allDay){
2051                                                        times[0] = cal.add(times[0], "day", 1); // no break;
2052                                                }
2053                                        case "resizeStart":
2054                                                if(!isStartOfDay){
2055                                                        times[0] = this.floorToDay(times[0], true);
2056                                                }
2057                                                break;
2058                                        case "move":
2059                                                times[0] = cal.add(times[0], "day", p.dayOffset);
2060                                                break;
2061                                        case "resizeBoth":
2062                                                if(!isStartOfDay){
2063                                                        times[0] = this.floorToDay(times[0], true);
2064                                                }
2065                                                if(!this.isStartOfDay(times[1])){
2066                                                        times[1] = this.floorToDay(cal.add(times[1], "day", 1), true);
2067                                                }
2068                                                break;
2069                                }       
2070                               
2071                        }else{
2072                                times = this.inherited(arguments);
2073                        }                       
2074                       
2075                        return times;                   
2076                },
2077                       
2078               
2079                /////////////////////////////////////////////
2080                //
2081                // Pixel to Time projection
2082                //
2083                //////////////////////////////////////////////
2084               
2085                getTime: function(e, x, y, touchIndex){
2086                        // summary:
2087                        //              Returns the time displayed at the specified point by this component.
2088                        // e: Event
2089                        //              Optional mouse event.
2090                        // x: Number
2091                        //              Position along the x-axis with respect to the sheet container used if event is not defined.
2092                        // y: Number
2093                        //              Position along the y-axis with respect to the sheet container (scroll included) used if event is not defined.
2094                        // touchIndex: Integer
2095                        //              If parameter 'e' is not null and a touch event, the index of the touch to use.
2096                        // returns: Date
2097                       
2098                        var rd = this.renderData;
2099                       
2100                        if(e != null){                         
2101                                var refPos = domGeometry.position(this.itemContainer, true);
2102                               
2103                                if(e.touches){
2104
2105                                        touchIndex = touchIndex==undefined ? 0 : touchIndex;
2106
2107                                        x = e.touches[touchIndex].pageX - refPos.x;
2108                                        y = e.touches[touchIndex].pageY - refPos.y;
2109                                       
2110                                }else{
2111
2112                                        x = e.pageX - refPos.x;
2113                                        y = e.pageY - refPos.y;
2114                                }
2115                        }
2116                       
2117                        var r = domGeometry.getContentBox(this.itemContainer);
2118                       
2119                        if(x < 0){
2120                                x = 0;
2121                        }else if(x > r.w){
2122                                x = r.w-1;
2123                        }
2124                       
2125                        if(y < 0){
2126                                y = 0;
2127                        }else if(y > r.h){
2128                                y = r.h-1;
2129                        }
2130
2131                        // compute the date from column the time in day instead of time from start date of row to prevent DST hour offset.
2132                       
2133                        var w = domGeometry.getMarginBox(this.itemContainer).w;
2134                        var colW = w / rd.columnCount;
2135                         
2136                        var row;
2137                        if(rd.expandedRow == null){
2138                                row = Math.floor(y / (domGeometry.getMarginBox(this.itemContainer).h / rd.rowCount));
2139                        }else{
2140                                row = rd.expandedRow; //other rows are not usable
2141                        }
2142                       
2143                        var r = domGeometry.getContentBox(this.itemContainer);
2144                       
2145                        if(rd.rtl){
2146                                x = r.w - x;
2147                        }
2148                       
2149                        var col = Math.floor(x / colW);
2150                       
2151                        var tm = Math.floor((x-(col*colW)) * 1440 / colW);
2152                       
2153                        var date = null;
2154                        if(row < rd.dates.length && col < this.renderData.dates[row].length){
2155                                date = this.newDate(this.renderData.dates[row][col]);
2156                                date = this.renderData.dateModule.add(date, "minute", tm);
2157                        }
2158                       
2159                        return date;
2160                },
2161               
2162                /////////////////////////////////////////////
2163                //
2164                // Event management
2165                //
2166                //////////////////////////////////////////////
2167               
2168                _onGridMouseUp: function(e){
2169                        // tags:
2170                        //              private
2171                       
2172                        this.inherited(arguments);
2173                       
2174                        if(this._gridMouseDown){
2175                                this._gridMouseDown = false;
2176                               
2177                                this._onGridClick({
2178                                        date: this.getTime(e),
2179                                        triggerEvent: e
2180                                });
2181                        }                       
2182                },
2183               
2184                _onGridTouchEnd: function(e){
2185                        // tags:
2186                        //              private
2187                        this.inherited(arguments);
2188
2189                        var g = this._gridProps;
2190                       
2191                        if(g){
2192                               
2193                                if(!this._isEditing){
2194                                               
2195                                        // touched on grid and on touch start editing was ongoing.
2196                                        if(!g.fromItem && !g.editingOnStart){
2197                                                this.selectFromEvent(e, null, null, true);
2198                                        }                       
2199                                       
2200                                        if(!g.fromItem){
2201                                       
2202                                                if(this._pendingDoubleTap && this._pendingDoubleTap.grid){
2203                                                                                                               
2204                                                        this._onGridDoubleClick({
2205                                                                date: this.getTime(this._gridProps.event),
2206                                                                triggerEvent: this._gridProps.event
2207                                                        });
2208                                                       
2209                                                        clearTimeout(this._pendingDoubleTap.timer);
2210                                       
2211                                                        delete this._pendingDoubleTap;
2212                                                       
2213                                                }else{
2214
2215                                                        this._onGridClick({
2216                                                                date: this.getTime(this._gridProps.event),
2217                                                                triggerEvent: this._gridProps.event
2218                                                        });
2219                                                       
2220                                                        this._pendingDoubleTap = {
2221                                                                grid: true,
2222                                                                timer: setTimeout(lang.hitch(this, function(){
2223                                                                                delete this._pendingDoubleTap;
2224                                                                }), this.doubleTapDelay)
2225                                                        };
2226                                                }
2227                                        }       
2228                                }
2229                               
2230                                this._gridProps = null;
2231                        }                                       
2232                },
2233               
2234                               
2235                /////////////////////////////////////////////
2236                //
2237                // Events
2238                //
2239                //////////////////////////////////////////////
2240               
2241                _onRowHeaderClick: function(e){
2242                        this._dispatchCalendarEvt(e, "onRowHeaderClick");
2243                        // tags:
2244                        //              private
2245                },
2246               
2247                onRowHeaderClick: function(e){
2248                        // summary:
2249                        //              Event dispatched when a row header cell is clicked.
2250                        // e: __HeaderClickEventArgs
2251                        //              Header click event.
2252                        // tags:
2253                        //              callback
2254                },
2255               
2256                expandRendererClickHandler: function(e, renderer){
2257                        // summary:
2258                        //              Default action when an expand renderer is clicked.
2259                        // e: Event
2260                        //              The mouse event.
2261                        // renderer: Object
2262                        //              The expand renderer.
2263                        // tags:
2264                        //              protected
2265                       
2266                        event.stop(e);
2267                       
2268                        var ri = renderer.get("rowIndex");
2269                        var ci = renderer.get("columnIndex");
2270                       
2271                        this._onExpandRendererClick(lang.mixin(this._createItemEditEvent(), {
2272                                rowIndex: ri,
2273                                columnIndex: ci,
2274                                renderer: renderer,
2275                                triggerEvent: e,
2276                                date: this.renderData.dates[ri][ci]
2277                        }));
2278                },
2279               
2280                onExpandRendererClick: function(e){
2281                        // summary:
2282                        //              Event dispatched when an expand renderer is clicked.
2283                        // e: __ExpandRendererClickEventArgs
2284                        //              Expand renderer click event.
2285                        // tags:
2286                        //              callback
2287                },
2288               
2289                _onExpandRendererClick: function(e){
2290                       
2291                        this._dispatchCalendarEvt(e, "onExpandRendererClick");
2292                       
2293                        if(!e.isDefaultPrevented()){
2294                       
2295                                if(this.getExpandedRowIndex() != -1){
2296                                        this.collapseRow();
2297                                }else{
2298                                        this.expandRow(e.rowIndex, e.columnIndex);
2299                                }
2300                        }
2301                },
2302               
2303               
2304                ////////////////////////////////////////////
2305                //
2306                // Editing
2307                //
2308                ///////////////////////////////////////////
2309                                                               
2310                snapUnit: "minute",
2311                snapSteps: 15,
2312                minDurationUnit: "minute",
2313                minDurationSteps: 15,
2314                triggerExtent: 3,
2315                liveLayout: false,
2316                stayInView: true,
2317                allowStartEndSwap: true,
2318                allowResizeLessThan24H: false           
2319
2320        });
2321});
Note: See TracBrowser for help on using the repository browser.