3 * Copyright(c) 2006-2010 Sencha Inc.
5 * http://www.sencha.com/license
8 * @class Ext.calendar.DayBodyView
9 * @extends Ext.calendar.CalendarView
10 * <p>This is the scrolling container within the day and week views where non-all-day events are displayed.
11 * Normally you should not need to use this class directly -- instead you should use {@link Ext.calendar.DayView DayView}
12 * which aggregates this class and the {@link Ext.calendar.DayHeaderView DayHeaderView} into the single unified view
13 * presented by {@link Ext.calendar.CalendarPanel CalendarPanel}.</p>
15 * @param {Object} config The config object
17 Ext.calendar.DayBodyView = Ext.extend(Ext.calendar.CalendarView, {
19 dayColumnElIdDelimiter: '-day-col-',
22 initComponent: function() {
23 Ext.calendar.DayBodyView.superclass.initComponent.call(this);
28 * Fires after the user drags the resize handle of an event to resize it
29 * @param {Ext.calendar.DayBodyView} this
30 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was resized
31 * containing the updated start and end dates
36 * Fires after the user clicks within the day view container and not on an event element
37 * @param {Ext.calendar.DayBodyView} this
38 * @param {Date} dt The date/time that was clicked on
39 * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks within the
40 * DayBodyView always return false for this param.
41 * @param {Ext.Element} el The Element that was clicked on
50 createText: this.ddCreateEventText,
51 moveText: this.ddMoveEventText,
52 resizeText: this.ddResizeEventText
55 this.el.ddScrollConfig = {
56 // scrolling is buggy in IE/Opera for some reason. A larger vthresh
57 // makes it at least functional if not perfect
58 vthresh: Ext.isIE || Ext.isOpera ? 100: 40,
64 this.dragZone = new Ext.calendar.DayViewDragZone(this.el, Ext.apply({
70 this.dropZone = new Ext.calendar.DayViewDropZone(this.el, Ext.apply({
78 var top = this.el.getScroll().top;
80 this.renderTemplate();
83 // skip this if the initial render scroll position has not yet been set.
84 // necessary since IE/Opera must be deferred, so the first refresh will
85 // override the initial position by default and always set it to 0.
86 if (this.scrollReady) {
92 * Scrolls the container to the specified vertical position. If the view is large enough that
93 * there is no scroll overflow then this method will have no affect.
94 * @param {Number} y The new vertical scroll position in pixels
95 * @param {Boolean} defer (optional) <p>True to slightly defer the call, false to execute immediately.</p>
96 * <p>This method will automatically defer itself for IE and Opera (even if you pass false) otherwise
97 * the scroll position will not update in those browsers. You can optionally pass true, however, to
98 * force the defer in all browsers, or use your own custom conditions to determine whether this is needed.</p>
99 * <p>Note that this method should not generally need to be called directly as scroll position is managed internally.</p>
101 scrollTo: function(y, defer) {
102 defer = defer || (Ext.isIE || Ext.isOpera);
105 this.el.scrollTo('top', y);
106 this.scrollReady = true;
110 this.el.scrollTo('top', y);
111 this.scrollReady = true;
116 afterRender: function() {
118 this.tpl = new Ext.calendar.DayBodyTemplate({
120 dayCount: this.dayCount,
121 showTodayText: this.showTodayText,
122 todayText: this.todayText,
123 showTime: this.showTime
128 this.addClass('ext-cal-body-ct');
130 Ext.calendar.DayBodyView.superclass.afterRender.call(this);
132 // default scroll position to 7am:
133 this.scrollTo(7 * 42);
137 forceSize: Ext.emptyFn,
140 onEventResize: function(rec, data) {
141 var D = Ext.calendar.Date,
142 start = Ext.calendar.EventMappings.StartDate.name,
143 end = Ext.calendar.EventMappings.EndDate.name;
145 if (D.compare(rec.data[start], data.StartDate) === 0 &&
146 D.compare(rec.data[end], data.EndDate) === 0) {
150 rec.set(start, data.StartDate);
151 rec.set(end, data.EndDate);
153 this.fireEvent('eventresize', this, rec);
157 getEventBodyMarkup: function() {
158 if (!this.eventBodyMarkup) {
159 this.eventBodyMarkup = ['{Title}',
160 '<tpl if="_isReminder">',
161 '<i class="ext-cal-ic ext-cal-ic-rem"> </i>',
163 '<tpl if="_isRecurring">',
164 '<i class="ext-cal-ic ext-cal-ic-rcr"> </i>',
166 // '<tpl if="spanLeft">',
167 // '<i class="ext-cal-spl"> </i>',
169 // '<tpl if="spanRight">',
170 // '<i class="ext-cal-spr"> </i>',
174 return this.eventBodyMarkup;
178 getEventTemplate: function() {
179 if (!this.eventTpl) {
180 this.eventTpl = !(Ext.isIE || Ext.isOpera) ?
182 '<div id="{_elId}" class="{_selectorCls} {_colorCls} ext-cal-evt ext-cal-evr" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
183 '<div class="ext-evt-bd">', this.getEventBodyMarkup(), '</div>',
184 '<div class="ext-evt-rsz"><div class="ext-evt-rsz-h"> </div></div>',
188 '<div id="{_elId}" class="ext-cal-evt {_selectorCls} {_colorCls}-x" style="left: {_left}%; width: {_width}%; top: {_top}px;">',
189 '<div class="ext-cal-evb"> </div>',
190 '<dl style="height: {_height}px;" class="ext-cal-evdm">',
191 '<dd class="ext-evt-bd">',
192 this.getEventBodyMarkup(),
194 '<div class="ext-evt-rsz"><div class="ext-evt-rsz-h"> </div></div>',
196 '<div class="ext-cal-evb"> </div>',
199 this.eventTpl.compile();
201 return this.eventTpl;
205 * <p>Returns the XTemplate that is bound to the calendar's event store (it expects records of type
206 * {@link Ext.calendar.EventRecord}) to populate the calendar views with <strong>all-day</strong> events.
207 * Internally this method by default generates different markup for browsers that support CSS border radius
208 * and those that don't. This method can be overridden as needed to customize the markup generated.</p>
209 * <p>Note that this method calls {@link #getEventBodyMarkup} to retrieve the body markup for events separately
210 * from the surrounding container markup. This provdes the flexibility to customize what's in the body without
211 * having to override the entire XTemplate. If you do override this method, you should make sure that your
212 * overridden version also does the same.</p>
213 * @return {Ext.XTemplate} The event XTemplate
215 getEventAllDayTemplate: function() {
216 if (!this.eventAllDayTpl) {
218 body = this.getEventBodyMarkup();
220 tpl = !(Ext.isIE || Ext.isOpera) ?
222 '<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;">',
227 '<div id="{_elId}" class="ext-cal-evt" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
228 '<div class="{_selectorCls} {values.spanCls} {_colorCls} ext-cal-evo">',
229 '<div class="ext-cal-evm">',
230 '<div class="ext-cal-evi">',
237 this.eventAllDayTpl = tpl;
239 return this.eventAllDayTpl;
243 getTemplateEventData: function(evt) {
244 var selector = this.getEventSelectorCls(evt[Ext.calendar.EventMappings.EventId.name]),
246 M = Ext.calendar.EventMappings;
248 this.getTemplateEventBox(evt);
250 data._selectorCls = selector;
251 data._colorCls = 'ext-color-' + evt[M.CalendarId.name] + (evt._renderAsAllDay ? '-ad': '');
252 data._elId = selector + (evt._weekIndex ? '-' + evt._weekIndex: '');
253 data._isRecurring = evt.Recurrence && evt.Recurrence != '';
254 data._isReminder = evt[M.Reminder.name] && evt[M.Reminder.name] != '';
255 var title = evt[M.Title.name];
256 data.Title = (evt[M.IsAllDay.name] ? '': evt[M.StartDate.name].format('g:ia ')) + (!title || title.length == 0 ? '(No title)': title);
258 return Ext.applyIf(data, evt);
262 getTemplateEventBox: function(evt) {
263 var heightFactor = 0.7,
264 start = evt[Ext.calendar.EventMappings.StartDate.name],
265 end = evt[Ext.calendar.EventMappings.EndDate.name],
266 startMins = start.getHours() * 60 + start.getMinutes(),
267 endMins = end.getHours() * 60 + end.getMinutes(),
268 diffMins = endMins - startMins;
272 evt._top = Math.round(startMins * heightFactor) + 1;
273 evt._height = Math.max((diffMins * heightFactor) - 2, 15);
277 renderItems: function() {
293 for (; day < this.dayCount; day++) {
294 ev = emptyCells = skipped = 0;
295 d = this.eventGrid[0][day];
296 ct = d ? d.length: 0;
298 for (; ev < ct; ev++) {
303 item = evt.data || evt.event.data;
304 if (item._renderAsAllDay) {
312 data: this.getTemplateEventData(item),
313 date: this.viewStart.add(Date.DAY, day)
318 // overlapping event pre-processing loop
319 i = j = overlapCols = prevCol = 0;
324 prevCol = overlapCols;
325 for (j = 0; j < l; j++) {
330 if (this.isOverlapping(evt, evt2)) {
331 evt._overlap = evt._overlap == undefined ? 1: evt._overlap + 1;
333 if (evt._overcol === undefined) {
336 evt2._overcol = evt._overcol + 1;
337 overlapCols = Math.max(overlapCols, evt2._overcol);
344 for (i = 0; i < l; i++) {
346 if (evt._overlap !== undefined) {
347 colWidth = 100 / (overlapCols + 1);
348 evtWidth = 100 - (colWidth * evt._overlap);
350 evt._width = colWidth;
351 evt._left = colWidth * evt._overcol;
353 markup = this.getEventTemplate().apply(evt);
354 target = this.id + '-day-col-' + evts[i].date.format('Ymd');
356 Ext.DomHelper.append(target, markup);
359 this.fireEvent('eventsrendered', this);
363 getDayEl: function(dt) {
364 return Ext.get(this.getDayId(dt));
368 getDayId: function(dt) {
369 if (Ext.isDate(dt)) {
370 dt = dt.format('Ymd');
372 return this.id + this.dayColumnElIdDelimiter + dt;
376 getDaySize: function() {
377 var box = this.el.child('.ext-cal-day-col-inner').getBox();
385 getDayAt: function(x, y) {
386 var sel = '.ext-cal-body-ct',
387 xoffset = this.el.child('.ext-cal-day-times').getWidth(),
388 viewBox = this.el.getBox(),
389 daySize = this.getDaySize(false),
390 relX = x - viewBox.x - xoffset,
391 dayIndex = Math.floor(relX / daySize.width),
393 scroll = this.el.getScroll(),
394 row = this.el.child('.ext-cal-bg-row'),
395 // first avail row, just to calc size
396 rowH = row.getHeight() / 2,
397 // 30 minute increment since a row is 60 minutes
398 relY = y - viewBox.y - rowH + scroll.top,
399 rowIndex = Math.max(0, Math.ceil(relY / rowH)),
400 mins = rowIndex * 30,
401 dt = this.viewStart.add(Date.DAY, dayIndex).add(Date.MINUTE, mins),
402 el = this.getDayEl(dt),
406 timeX = el.getLeft();
412 // this is the box for the specific time block in the day that was clicked on:
415 y: (rowIndex * 21) + viewBox.y - scroll.top,
416 width: daySize.width,
423 onClick: function(e, t) {
424 if (this.dragPending || Ext.calendar.DayBodyView.superclass.onClick.apply(this, arguments)) {
425 // The superclass handled the click already so exit
428 if (e.getTarget('.ext-cal-day-times', 3) !== null) {
429 // ignore clicks on the times-of-day gutter
432 var el = e.getTarget('td', 3);
434 if (el.id && el.id.indexOf(this.dayElIdDelimiter) > -1) {
435 var dt = this.getDateFromId(el.id, this.dayElIdDelimiter);
436 this.fireEvent('dayclick', this, Date.parseDate(dt, 'Ymd'), true, Ext.get(this.getDayId(dt, true)));
440 var day = this.getDayAt(e.xy[0], e.xy[1]);
441 if (day && day.date) {
442 this.fireEvent('dayclick', this, day.date, false, null);
447 Ext.reg('daybodyview', Ext.calendar.DayBodyView);