Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / examples / docs / source / DayBodyView.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 <div id="cls-Ext.calendar.DayBodyView"></div>/**S
16  * @class Ext.calendar.DayBodyView
17  * @extends Ext.calendar.CalendarView
18  * <p>This is the scrolling container within the day and week views where non-all-day events are displayed.
19  * Normally you should not need to use this class directly -- instead you should use {@link Ext.calendar.DayView DayView}
20  * which aggregates this class and the {@link Ext.calendar.DayHeaderView DayHeaderView} into the single unified view
21  * presented by {@link Ext.calendar.CalendarPanel CalendarPanel}.</p>
22  * @constructor
23  * @param {Object} config The config object
24  */
25 Ext.calendar.DayBodyView = Ext.extend(Ext.calendar.CalendarView, {
26     //private
27     dayColumnElIdDelimiter: '-day-col-',
28
29     //private
30     initComponent: function() {
31         Ext.calendar.DayBodyView.superclass.initComponent.call(this);
32
33         this.addEvents({
34             <div id="event-Ext.calendar.DayBodyView-eventresize"></div>/**
35              * @event eventresize
36              * Fires after the user drags the resize handle of an event to resize it
37              * @param {Ext.calendar.DayBodyView} this
38              * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was resized
39              * containing the updated start and end dates
40              */
41             eventresize: true,
42             <div id="event-Ext.calendar.DayBodyView-dayclick"></div>/**
43              * @event dayclick
44              * Fires after the user clicks within the day view container and not on an event element
45              * @param {Ext.calendar.DayBodyView} this
46              * @param {Date} dt The date/time that was clicked on
47              * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks within the 
48              * DayBodyView always return false for this param.
49              * @param {Ext.Element} el The Element that was clicked on
50              */
51             dayclick: true
52         });
53     },
54
55     //private
56     initDD: function() {
57         var cfg = {
58             createText: this.ddCreateEventText,
59             moveText: this.ddMoveEventText,
60             resizeText: this.ddResizeEventText
61         };
62
63         this.el.ddScrollConfig = {
64             // scrolling is buggy in IE/Opera for some reason.  A larger vthresh
65             // makes it at least functional if not perfect
66             vthresh: Ext.isIE || Ext.isOpera ? 100: 40,
67             hthresh: -1,
68             frequency: 50,
69             increment: 100,
70             ddGroup: 'DayViewDD'
71         };
72         this.dragZone = new Ext.calendar.DayViewDragZone(this.el, Ext.apply({
73             view: this,
74             containerScroll: true
75         },
76         cfg));
77
78         this.dropZone = new Ext.calendar.DayViewDropZone(this.el, Ext.apply({
79             view: this
80         },
81         cfg));
82     },
83
84     //private
85     refresh: function() {
86         var top = this.el.getScroll().top;
87         this.prepareData();
88         this.renderTemplate();
89         this.renderItems();
90
91         // skip this if the initial render scroll position has not yet been set.
92         // necessary since IE/Opera must be deferred, so the first refresh will
93         // override the initial position by default and always set it to 0.
94         if (this.scrollReady) {
95             this.scrollTo(top);
96         }
97     },
98
99     <div id="method-Ext.calendar.DayBodyView-scrollTo"></div>/**
100      * Scrolls the container to the specified vertical position. If the view is large enough that
101      * there is no scroll overflow then this method will have no affect.
102      * @param {Number} y The new vertical scroll position in pixels 
103      * @param {Boolean} defer (optional) <p>True to slightly defer the call, false to execute immediately.</p> 
104      * <p>This method will automatically defer itself for IE and Opera (even if you pass false) otherwise
105      * the scroll position will not update in those browsers. You can optionally pass true, however, to
106      * force the defer in all browsers, or use your own custom conditions to determine whether this is needed.</p>
107      * <p>Note that this method should not generally need to be called directly as scroll position is managed internally.</p>
108      */
109     scrollTo: function(y, defer) {
110         defer = defer || (Ext.isIE || Ext.isOpera);
111         if (defer) {
112             (function() {
113                 this.el.scrollTo('top', y);
114                 this.scrollReady = true;
115             }).defer(10, this);
116         }
117         else {
118             this.el.scrollTo('top', y);
119             this.scrollReady = true;
120         }
121     },
122
123     // private
124     afterRender: function() {
125         if (!this.tpl) {
126             this.tpl = new Ext.calendar.DayBodyTemplate({
127                 id: this.id,
128                 dayCount: this.dayCount,
129                 showTodayText: this.showTodayText,
130                 todayText: this.todayText,
131                 showTime: this.showTime
132             });
133         }
134         this.tpl.compile();
135
136         this.addClass('ext-cal-body-ct');
137
138         Ext.calendar.DayBodyView.superclass.afterRender.call(this);
139
140         // default scroll position to 7am:
141         this.scrollTo(7 * 42);
142     },
143
144     // private
145     forceSize: Ext.emptyFn,
146
147     // private
148     onEventResize: function(rec, data) {
149         var D = Ext.calendar.Date,
150         start = Ext.calendar.EventMappings.StartDate.name,
151         end = Ext.calendar.EventMappings.EndDate.name;
152
153         if (D.compare(rec.data[start], data.StartDate) === 0 &&
154         D.compare(rec.data[end], data.EndDate) === 0) {
155             // no changes
156             return;
157         }
158         rec.set(start, data.StartDate);
159         rec.set(end, data.EndDate);
160
161         this.fireEvent('eventresize', this, rec);
162     },
163
164     // inherited docs
165     getEventBodyMarkup: function() {
166         if (!this.eventBodyMarkup) {
167             this.eventBodyMarkup = ['{Title}',
168             '<tpl if="_isReminder">',
169             '<i class="ext-cal-ic ext-cal-ic-rem">&nbsp;</i>',
170             '</tpl>',
171             '<tpl if="_isRecurring">',
172             '<i class="ext-cal-ic ext-cal-ic-rcr">&nbsp;</i>',
173             '</tpl>'
174             //                '<tpl if="spanLeft">',
175             //                    '<i class="ext-cal-spl">&nbsp;</i>',
176             //                '</tpl>',
177             //                '<tpl if="spanRight">',
178             //                    '<i class="ext-cal-spr">&nbsp;</i>',
179             //                '</tpl>'
180             ].join('');
181         }
182         return this.eventBodyMarkup;
183     },
184
185     // inherited docs
186     getEventTemplate: function() {
187         if (!this.eventTpl) {
188             this.eventTpl = !(Ext.isIE || Ext.isOpera) ?
189             new Ext.XTemplate(
190             '<div id="{_elId}" class="{_selectorCls} {_colorCls} ext-cal-evt ext-cal-evr" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
191             '<div class="ext-evt-bd">', this.getEventBodyMarkup(), '</div>',
192             '<div class="ext-evt-rsz"><div class="ext-evt-rsz-h">&nbsp;</div></div>',
193             '</div>'
194             )
195             : new Ext.XTemplate(
196             '<div id="{_elId}" class="ext-cal-evt {_selectorCls} {_colorCls}-x" style="left: {_left}%; width: {_width}%; top: {_top}px;">',
197             '<div class="ext-cal-evb">&nbsp;</div>',
198             '<dl style="height: {_height}px;" class="ext-cal-evdm">',
199             '<dd class="ext-evt-bd">',
200             this.getEventBodyMarkup(),
201             '</dd>',
202             '<div class="ext-evt-rsz"><div class="ext-evt-rsz-h">&nbsp;</div></div>',
203             '</dl>',
204             '<div class="ext-cal-evb">&nbsp;</div>',
205             '</div>'
206             );
207             this.eventTpl.compile();
208         }
209         return this.eventTpl;
210     },
211
212     <div id="method-Ext.calendar.DayBodyView-getEventAllDayTemplate"></div>/**
213      * <p>Returns the XTemplate that is bound to the calendar's event store (it expects records of type
214      * {@link Ext.calendar.EventRecord}) to populate the calendar views with <strong>all-day</strong> events. 
215      * Internally this method by default generates different markup for browsers that support CSS border radius 
216      * and those that don't. This method can be overridden as needed to customize the markup generated.</p>
217      * <p>Note that this method calls {@link #getEventBodyMarkup} to retrieve the body markup for events separately
218      * from the surrounding container markup.  This provdes the flexibility to customize what's in the body without
219      * having to override the entire XTemplate. If you do override this method, you should make sure that your 
220      * overridden version also does the same.</p>
221      * @return {Ext.XTemplate} The event XTemplate
222      */
223     getEventAllDayTemplate: function() {
224         if (!this.eventAllDayTpl) {
225             var tpl,
226             body = this.getEventBodyMarkup();
227
228             tpl = !(Ext.isIE || Ext.isOpera) ?
229             new Ext.XTemplate(
230             '<div id="{_elId}" class="{_selectorCls} {_colorCls} {values.spanCls} ext-cal-evt ext-cal-evr" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
231             body,
232             '</div>'
233             )
234             : new Ext.XTemplate(
235             '<div id="{_elId}" class="ext-cal-evt" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
236             '<div class="{_selectorCls} {values.spanCls} {_colorCls} ext-cal-evo">',
237             '<div class="ext-cal-evm">',
238             '<div class="ext-cal-evi">',
239             body,
240             '</div>',
241             '</div>',
242             '</div></div>'
243             );
244             tpl.compile();
245             this.eventAllDayTpl = tpl;
246         }
247         return this.eventAllDayTpl;
248     },
249
250     // private
251     getTemplateEventData: function(evt) {
252         var selector = this.getEventSelectorCls(evt[Ext.calendar.EventMappings.EventId.name]),
253         data = {},
254         M = Ext.calendar.EventMappings;
255
256         this.getTemplateEventBox(evt);
257
258         data._selectorCls = selector;
259         data._colorCls = 'ext-color-' + evt[M.CalendarId.name] + (evt._renderAsAllDay ? '-ad': '');
260         data._elId = selector + (evt._weekIndex ? '-' + evt._weekIndex: '');
261         data._isRecurring = evt.Recurrence && evt.Recurrence != '';
262         data._isReminder = evt[M.Reminder.name] && evt[M.Reminder.name] != '';
263         var title = evt[M.Title.name];
264         data.Title = (evt[M.IsAllDay.name] ? '': evt[M.StartDate.name].format('g:ia ')) + (!title || title.length == 0 ? '(No title)': title);
265
266         return Ext.applyIf(data, evt);
267     },
268
269     // private
270     getTemplateEventBox: function(evt) {
271         var heightFactor = 0.7,
272             start = evt[Ext.calendar.EventMappings.StartDate.name],
273             end = evt[Ext.calendar.EventMappings.EndDate.name],
274             startMins = start.getHours() * 60 + start.getMinutes(),
275             endMins = end.getHours() * 60 + end.getMinutes(),
276             diffMins = endMins - startMins;
277
278         evt._left = 0;
279         evt._width = 100;
280         evt._top = Math.round(startMins * heightFactor) + 1;
281         evt._height = Math.max((diffMins * heightFactor) - 2, 15);
282     },
283
284     // private
285     renderItems: function() {
286         var day = 0,
287             evts = [],
288             ev,
289             d,
290             ct,
291             item,
292             i,
293             j,
294             l,
295             overlapCols,
296             prevCol,
297             colWidth,
298             evtWidth,
299             markup,
300             target;
301         for (; day < this.dayCount; day++) {
302             ev = emptyCells = skipped = 0;
303             d = this.eventGrid[0][day];
304             ct = d ? d.length: 0;
305
306             for (; ev < ct; ev++) {
307                 evt = d[ev];
308                 if (!evt) {
309                     continue;
310                 }
311                 item = evt.data || evt.event.data;
312                 if (item._renderAsAllDay) {
313                     continue;
314                 }
315                 Ext.apply(item, {
316                     cls: 'ext-cal-ev',
317                     _positioned: true
318                 });
319                 evts.push({
320                     data: this.getTemplateEventData(item),
321                     date: this.viewStart.add(Date.DAY, day)
322                 });
323             }
324         }
325
326         // overlapping event pre-processing loop
327         i = j = overlapCols = prevCol = 0;
328         l = evts.length;
329         for (; i < l; i++) {
330             evt = evts[i].data;
331             evt2 = null;
332             prevCol = overlapCols;
333             for (j = 0; j < l; j++) {
334                 if (i == j) {
335                     continue;
336                 }
337                 evt2 = evts[j].data;
338                 if (this.isOverlapping(evt, evt2)) {
339                     evt._overlap = evt._overlap == undefined ? 1: evt._overlap + 1;
340                     if (i < j) {
341                         if (evt._overcol === undefined) {
342                             evt._overcol = 0;
343                         }
344                         evt2._overcol = evt._overcol + 1;
345                         overlapCols = Math.max(overlapCols, evt2._overcol);
346                     }
347                 }
348             }
349         }
350
351         // rendering loop
352         for (i = 0; i < l; i++) {
353             evt = evts[i].data;
354             if (evt._overlap !== undefined) {
355                 colWidth = 100 / (overlapCols + 1);
356                 evtWidth = 100 - (colWidth * evt._overlap);
357
358                 evt._width = colWidth;
359                 evt._left = colWidth * evt._overcol;
360             }
361             markup = this.getEventTemplate().apply(evt);
362             target = this.id + '-day-col-' + evts[i].date.format('Ymd');
363
364             Ext.DomHelper.append(target, markup);
365         }
366
367         this.fireEvent('eventsrendered', this);
368     },
369
370     // private
371     getDayEl: function(dt) {
372         return Ext.get(this.getDayId(dt));
373     },
374
375     // private
376     getDayId: function(dt) {
377         if (Ext.isDate(dt)) {
378             dt = dt.format('Ymd');
379         }
380         return this.id + this.dayColumnElIdDelimiter + dt;
381     },
382
383     // private
384     getDaySize: function() {
385         var box = this.el.child('.ext-cal-day-col-inner').getBox();
386         return {
387             height: box.height,
388             width: box.width
389         };
390     },
391
392     // private
393     getDayAt: function(x, y) {
394         var sel = '.ext-cal-body-ct',
395         xoffset = this.el.child('.ext-cal-day-times').getWidth(),
396         viewBox = this.el.getBox(),
397         daySize = this.getDaySize(false),
398         relX = x - viewBox.x - xoffset,
399         dayIndex = Math.floor(relX / daySize.width),
400         // clicked col index
401         scroll = this.el.getScroll(),
402         row = this.el.child('.ext-cal-bg-row'),
403         // first avail row, just to calc size
404         rowH = row.getHeight() / 2,
405         // 30 minute increment since a row is 60 minutes
406         relY = y - viewBox.y - rowH + scroll.top,
407         rowIndex = Math.max(0, Math.ceil(relY / rowH)),
408         mins = rowIndex * 30,
409         dt = this.viewStart.add(Date.DAY, dayIndex).add(Date.MINUTE, mins),
410         el = this.getDayEl(dt),
411         timeX = x;
412
413         if (el) {
414             timeX = el.getLeft();
415         }
416
417         return {
418             date: dt,
419             el: el,
420             // this is the box for the specific time block in the day that was clicked on:
421             timeBox: {
422                 x: timeX,
423                 y: (rowIndex * 21) + viewBox.y - scroll.top,
424                 width: daySize.width,
425                 height: rowH
426             }
427         };
428     },
429
430     // private
431     onClick: function(e, t) {
432         if (this.dragPending || Ext.calendar.DayBodyView.superclass.onClick.apply(this, arguments)) {
433             // The superclass handled the click already so exit
434             return;
435         }
436         if (e.getTarget('.ext-cal-day-times', 3) !== null) {
437             // ignore clicks on the times-of-day gutter
438             return;
439         }
440         var el = e.getTarget('td', 3);
441         if (el) {
442             if (el.id && el.id.indexOf(this.dayElIdDelimiter) > -1) {
443                 var dt = this.getDateFromId(el.id, this.dayElIdDelimiter);
444                 this.fireEvent('dayclick', this, Date.parseDate(dt, 'Ymd'), true, Ext.get(this.getDayId(dt, true)));
445                 return;
446             }
447         }
448         var day = this.getDayAt(e.xy[0], e.xy[1]);
449         if (day && day.date) {
450             this.fireEvent('dayclick', this, day.date, false, null);
451         }
452     }
453 });
454
455 Ext.reg('daybodyview', Ext.calendar.DayBodyView);
456 </pre>    
457 </body>
458 </html>