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