]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @class Ext.calendar.view.AbstractCalendar\r | |
3 | * @extends Ext.BoxComponent\r | |
4 | * <p>This is an abstract class that serves as the base for other calendar views. This class is not\r | |
5 | * intended to be directly instantiated.</p>\r | |
6 | * <p>When extending this class to create a custom calendar view, you must provide an implementation\r | |
7 | * for the <code>renderItems</code> method, as there is no default implementation for rendering events\r | |
8 | * The rendering logic is totally dependent on how the UI structures its data, which\r | |
9 | * is determined by the underlying UI template (this base class does not have a template).</p>\r | |
10 | * @constructor\r | |
11 | * @param {Object} config The config object\r | |
12 | */\r | |
13 | Ext.define('Ext.calendar.view.AbstractCalendar', {\r | |
14 | extend: 'Ext.Component',\r | |
15 | alias: 'widget.calendarview',\r | |
16 | requires: [\r | |
17 | 'Ext.calendar.util.Date',\r | |
18 | 'Ext.calendar.data.EventMappings'\r | |
19 | ],\r | |
20 | /**\r | |
21 | * @cfg {Number} startDay\r | |
22 | * The 0-based index for the day on which the calendar week begins (0=Sunday, which is the default)\r | |
23 | */\r | |
24 | startDay: 0,\r | |
25 | /**\r | |
26 | * @cfg {Boolean} spansHavePriority\r | |
27 | * Allows switching between two different modes of rendering events that span multiple days. When true,\r | |
28 | * span events are always sorted first, possibly at the expense of start dates being out of order (e.g., \r | |
29 | * a span event that starts at 11am one day and spans into the next day would display before a non-spanning \r | |
30 | * event that starts at 10am, even though they would not be in date order). This can lead to more compact\r | |
31 | * layouts when there are many overlapping events. If false (the default), events will always sort by start date\r | |
32 | * first which can result in a less compact, but chronologically consistent layout.\r | |
33 | */\r | |
34 | spansHavePriority: false,\r | |
35 | /**\r | |
36 | * @cfg {Boolean} trackMouseOver\r | |
37 | * Whether or not the view tracks and responds to the browser mouseover event on contained elements (defaults to\r | |
38 | * true). If you don't need mouseover event highlighting you can disable this.\r | |
39 | */\r | |
40 | trackMouseOver: true,\r | |
41 | /**\r | |
42 | * @cfg {Boolean} enableFx\r | |
43 | * Determines whether or not visual effects for CRUD actions are enabled (defaults to true). If this is false\r | |
44 | * it will override any values for {@link #enableAddFx}, {@link #enableUpdateFx} or {@link enableRemoveFx} and\r | |
45 | * all animations will be disabled.\r | |
46 | */\r | |
47 | enableFx: true,\r | |
48 | /**\r | |
49 | * @cfg {Boolean} enableAddFx\r | |
50 | * True to enable a visual effect on adding a new event (the default), false to disable it. Note that if \r | |
51 | * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the\r | |
52 | * {@link #doAddFx} method.\r | |
53 | */\r | |
54 | enableAddFx: true,\r | |
55 | /**\r | |
56 | * @cfg {Boolean} enableUpdateFx\r | |
57 | * True to enable a visual effect on updating an event, false to disable it (the default). Note that if \r | |
58 | * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the\r | |
59 | * {@link #doUpdateFx} method.\r | |
60 | */\r | |
61 | enableUpdateFx: false,\r | |
62 | /**\r | |
63 | * @cfg {Boolean} enableRemoveFx\r | |
64 | * True to enable a visual effect on removing an event (the default), false to disable it. Note that if \r | |
65 | * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the\r | |
66 | * {@link #doRemoveFx} method.\r | |
67 | */\r | |
68 | enableRemoveFx: true,\r | |
69 | /**\r | |
70 | * @cfg {Boolean} enableDD\r | |
71 | * True to enable drag and drop in the calendar view (the default), false to disable it\r | |
72 | */\r | |
73 | enableDD: true,\r | |
74 | /**\r | |
75 | * @cfg {Boolean} monitorResize\r | |
76 | * True to monitor the browser's resize event (the default), false to ignore it. If the calendar view is rendered\r | |
77 | * into a fixed-size container this can be set to false. However, if the view can change dimensions (e.g., it's in \r | |
78 | * fit layout in a viewport or some other resizable container) it is very important that this config is true so that\r | |
79 | * any resize event propagates properly to all subcomponents and layouts get recalculated properly.\r | |
80 | */\r | |
81 | monitorResize: true,\r | |
82 | /**\r | |
83 | * @cfg {String} ddCreateEventText\r | |
84 | * The text to display inside the drag proxy while dragging over the calendar to create a new event (defaults to \r | |
85 | * 'Create event for {0}' where {0} is a date range supplied by the view)\r | |
86 | */\r | |
87 | ddCreateEventText: 'Create event for {0}',\r | |
88 | /**\r | |
89 | * @cfg {String} ddMoveEventText\r | |
90 | * The text to display inside the drag proxy while dragging an event to reposition it (defaults to \r | |
91 | * 'Move event to {0}' where {0} is the updated event start date/time supplied by the view)\r | |
92 | */\r | |
93 | ddMoveEventText: 'Move event to {0}',\r | |
94 | /**\r | |
95 | * @cfg {String} ddResizeEventText\r | |
96 | * The string displayed to the user in the drag proxy while dragging the resize handle of an event (defaults to \r | |
97 | * 'Update event to {0}' where {0} is the updated event start-end range supplied by the view). Note that \r | |
98 | * this text is only used in views\r | |
99 | * that allow resizing of events.\r | |
100 | */\r | |
101 | ddResizeEventText: 'Update event to {0}',\r | |
102 | \r | |
103 | //private properties -- do not override:\r | |
104 | weekCount: 1,\r | |
105 | dayCount: 1,\r | |
106 | eventSelector: '.ext-cal-evt',\r | |
107 | eventOverClass: 'ext-evt-over',\r | |
108 | eventElIdDelimiter: '-evt-',\r | |
109 | dayElIdDelimiter: '-day-',\r | |
110 | \r | |
111 | /**\r | |
112 | * Returns a string of HTML template markup to be used as the body portion of the event template created\r | |
113 | * by {@link #getEventTemplate}. This provdes the flexibility to customize what's in the body without\r | |
114 | * having to override the entire XTemplate. This string can include any valid {@link Ext.Template} code, and\r | |
115 | * any data tokens accessible to the containing event template can be referenced in this string.\r | |
116 | * @return {String} The body template string\r | |
117 | */\r | |
118 | getEventBodyMarkup: Ext.emptyFn,\r | |
119 | // must be implemented by a subclass\r | |
120 | /**\r | |
121 | * <p>Returns the XTemplate that is bound to the calendar's event store (it expects records of type\r | |
122 | * {@link Ext.calendar.EventRecord}) to populate the calendar views with events. Internally this method\r | |
123 | * by default generates different markup for browsers that support CSS border radius and those that don't.\r | |
124 | * This method can be overridden as needed to customize the markup generated.</p>\r | |
125 | * <p>Note that this method calls {@link #getEventBodyMarkup} to retrieve the body markup for events separately\r | |
126 | * from the surrounding container markup. This provdes the flexibility to customize what's in the body without\r | |
127 | * having to override the entire XTemplate. If you do override this method, you should make sure that your \r | |
128 | * overridden version also does the same.</p>\r | |
129 | * @return {Ext.XTemplate} The event XTemplate\r | |
130 | */\r | |
131 | getEventTemplate: Ext.emptyFn,\r | |
132 | \r | |
133 | /**\r | |
134 | * @event eventsrendered\r | |
135 | * Fires after events are finished rendering in the view\r | |
136 | * @param {Ext.calendar.view.AbstractCalendar} this \r | |
137 | */\r | |
138 | \r | |
139 | /**\r | |
140 | * @event eventclick\r | |
141 | * Fires after the user clicks on an event element\r | |
142 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
143 | * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was clicked on\r | |
144 | * @param {HTMLNode} el The DOM node that was clicked on\r | |
145 | */\r | |
146 | \r | |
147 | /**\r | |
148 | * @event eventover\r | |
149 | * Fires anytime the mouse is over an event element\r | |
150 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
151 | * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor is over\r | |
152 | * @param {HTMLNode} el The DOM node that is being moused over\r | |
153 | */\r | |
154 | \r | |
155 | /**\r | |
156 | * @event eventout\r | |
157 | * Fires anytime the mouse exits an event element\r | |
158 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
159 | * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor exited\r | |
160 | * @param {HTMLNode} el The DOM node that was exited\r | |
161 | */\r | |
162 | \r | |
163 | /**\r | |
164 | * @event datechange\r | |
165 | * Fires after the start date of the view changes\r | |
166 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
167 | * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}\r | |
168 | * @param {Date} viewStart The first displayed date in the view\r | |
169 | * @param {Date} viewEnd The last displayed date in the view\r | |
170 | */\r | |
171 | \r | |
172 | /**\r | |
173 | * @event rangeselect\r | |
174 | * Fires after the user drags on the calendar to select a range of dates/times in which to create an event\r | |
175 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
176 | * @param {Object} dates An object containing the start (StartDate property) and end (EndDate property) dates selected\r | |
177 | * @param {Function} callback A callback function that MUST be called after the event handling is complete so that\r | |
178 | * the view is properly cleaned up (shim elements are persisted in the view while the user is prompted to handle the\r | |
179 | * range selection). The callback is already created in the proper scope, so it simply needs to be executed as a standard\r | |
180 | * function call (e.g., callback()).\r | |
181 | */\r | |
182 | \r | |
183 | /**\r | |
184 | * @event eventmove\r | |
185 | * Fires after an event element is dragged by the user and dropped in a new position\r | |
186 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
187 | * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was moved with\r | |
188 | * updated start and end dates\r | |
189 | */\r | |
190 | \r | |
191 | /**\r | |
192 | * @event initdrag\r | |
193 | * Fires when a drag operation is initiated in the view\r | |
194 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
195 | */\r | |
196 | \r | |
197 | /**\r | |
198 | * @event dayover\r | |
199 | * Fires while the mouse is over a day element \r | |
200 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
201 | * @param {Date} dt The date that is being moused over\r | |
202 | * @param {Ext.core.Element} el The day Element that is being moused over\r | |
203 | */\r | |
204 | \r | |
205 | /**\r | |
206 | * @event dayout\r | |
207 | * Fires when the mouse exits a day element \r | |
208 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
209 | * @param {Date} dt The date that is exited\r | |
210 | * @param {Ext.core.Element} el The day Element that is exited\r | |
211 | */\r | |
212 | \r | |
213 | /*\r | |
214 | * @event eventdelete\r | |
215 | * Fires after an event element is deleted by the user. Not currently implemented directly at the view level -- currently \r | |
216 | * deletes only happen from one of the forms.\r | |
217 | * @param {Ext.calendar.view.AbstractCalendar} this\r | |
218 | * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was deleted\r | |
219 | */\r | |
220 | \r | |
221 | // must be implemented by a subclass\r | |
222 | // private\r | |
223 | initComponent: function() {\r | |
224 | this.setStartDate(this.startDate || new Date());\r | |
225 | \r | |
226 | this.callParent(arguments);\r | |
227 | },\r | |
228 | \r | |
229 | // private\r | |
230 | afterRender: function() {\r | |
231 | this.callParent(arguments);\r | |
232 | \r | |
233 | this.renderTemplate();\r | |
234 | \r | |
235 | if (this.store) {\r | |
236 | this.setStore(this.store, true);\r | |
237 | }\r | |
238 | \r | |
239 | this.el.on({\r | |
240 | 'mouseover': this.onMouseOver,\r | |
241 | 'mouseout': this.onMouseOut,\r | |
242 | 'click': this.onClick,\r | |
243 | scope: this\r | |
244 | });\r | |
245 | \r | |
246 | this.el.unselectable();\r | |
247 | \r | |
248 | if (this.enableDD && this.initDD) {\r | |
249 | this.initDD();\r | |
250 | }\r | |
251 | \r | |
252 | this.on('eventsrendered', this.forceSize);\r | |
253 | Ext.defer(this.forceSize, 100, this);\r | |
254 | \r | |
255 | },\r | |
256 | \r | |
257 | // private\r | |
258 | forceSize: function() {\r | |
259 | if (this.el && this.el.down) {\r | |
260 | var hd = this.el.down('.ext-cal-hd-ct'),\r | |
261 | bd = this.el.down('.ext-cal-body-ct');\r | |
262 | \r | |
263 | if (bd==null || hd==null) {\r | |
264 | return;\r | |
265 | }\r | |
266 | \r | |
267 | var headerHeight = hd.getHeight(),\r | |
268 | sz = this.el.parent().getSize();\r | |
269 | \r | |
270 | bd.setHeight(sz.height-headerHeight);\r | |
271 | }\r | |
272 | },\r | |
273 | \r | |
274 | refresh: function() {\r | |
275 | this.prepareData();\r | |
276 | this.renderTemplate();\r | |
277 | this.renderItems();\r | |
278 | },\r | |
279 | \r | |
280 | getWeekCount: function() {\r | |
281 | var days = Ext.calendar.util.Date.diffDays(this.viewStart, this.viewEnd);\r | |
282 | return Math.ceil(days / this.dayCount);\r | |
283 | },\r | |
284 | \r | |
285 | // private\r | |
286 | prepareData: function() {\r | |
287 | var lastInMonth = Ext.Date.getLastDateOfMonth(this.startDate),\r | |
288 | w = 0, d,\r | |
289 | dt = Ext.Date.clone(this.viewStart),\r | |
290 | weeks = this.weekCount < 1 ? 6: this.weekCount;\r | |
291 | \r | |
292 | this.eventGrid = [[]];\r | |
293 | this.allDayGrid = [[]];\r | |
294 | this.evtMaxCount = [];\r | |
295 | \r | |
296 | var evtsInView = this.store.queryBy(function(rec) {\r | |
297 | return this.isEventVisible(rec.data);\r | |
298 | },\r | |
299 | this);\r | |
300 | \r | |
301 | for (; w < weeks; w++) {\r | |
302 | this.evtMaxCount[w] = 0;\r | |
303 | if (this.weekCount == -1 && dt > lastInMonth) {\r | |
304 | //current week is fully in next month so skip\r | |
305 | break;\r | |
306 | }\r | |
307 | this.eventGrid[w] = this.eventGrid[w] || [];\r | |
308 | this.allDayGrid[w] = this.allDayGrid[w] || [];\r | |
309 | \r | |
310 | for (d = 0; d < this.dayCount; d++) {\r | |
311 | if (evtsInView.getCount() > 0) {\r | |
312 | var evts = evtsInView.filterBy(function(rec) {\r | |
313 | var startDt = Ext.Date.clearTime(rec.data[Ext.calendar.data.EventMappings.StartDate.name], true),\r | |
314 | startsOnDate = dt.getTime() == startDt.getTime(),\r | |
315 | spansFromPrevView = (w == 0 && d == 0 && (dt > rec.data[Ext.calendar.data.EventMappings.StartDate.name]));\r | |
316 | \r | |
317 | return startsOnDate || spansFromPrevView;\r | |
318 | },\r | |
319 | this);\r | |
320 | \r | |
321 | this.sortEventRecordsForDay(evts);\r | |
322 | this.prepareEventGrid(evts, w, d);\r | |
323 | }\r | |
324 | dt = Ext.calendar.util.Date.add(dt, {days: 1});\r | |
325 | }\r | |
326 | }\r | |
327 | this.currentWeekCount = w;\r | |
328 | },\r | |
329 | \r | |
330 | // private\r | |
331 | prepareEventGrid: function(evts, w, d) {\r | |
332 | var me = this,\r | |
333 | row = 0,\r | |
334 | max = me.maxEventsPerDay ? me.maxEventsPerDay: 999;\r | |
335 | \r | |
336 | evts.each(function(evt) {\r | |
337 | var M = Ext.calendar.data.EventMappings,\r | |
338 | days = Ext.calendar.util.Date.diffDays(\r | |
339 | Ext.calendar.util.Date.max(me.viewStart, evt.data[M.StartDate.name]),\r | |
340 | Ext.calendar.util.Date.min(me.viewEnd, evt.data[M.EndDate.name])) + 1;\r | |
341 | \r | |
342 | if (days > 1 || Ext.calendar.util.Date.diffDays(evt.data[M.StartDate.name], evt.data[M.EndDate.name]) > 1) {\r | |
343 | me.prepareEventGridSpans(evt, me.eventGrid, w, d, days);\r | |
344 | me.prepareEventGridSpans(evt, me.allDayGrid, w, d, days, true);\r | |
345 | } else {\r | |
346 | row = me.findEmptyRowIndex(w, d);\r | |
347 | me.eventGrid[w][d] = me.eventGrid[w][d] || [];\r | |
348 | me.eventGrid[w][d][row] = evt;\r | |
349 | \r | |
350 | if (evt.data[M.IsAllDay.name]) {\r | |
351 | row = me.findEmptyRowIndex(w, d, true);\r | |
352 | me.allDayGrid[w][d] = me.allDayGrid[w][d] || [];\r | |
353 | me.allDayGrid[w][d][row] = evt;\r | |
354 | }\r | |
355 | }\r | |
356 | \r | |
357 | if (me.evtMaxCount[w] < me.eventGrid[w][d].length) {\r | |
358 | me.evtMaxCount[w] = Math.min(max + 1, me.eventGrid[w][d].length);\r | |
359 | }\r | |
360 | return true;\r | |
361 | });\r | |
362 | },\r | |
363 | \r | |
364 | // private\r | |
365 | prepareEventGridSpans: function(evt, grid, w, d, days, allday) {\r | |
366 | // this event spans multiple days/weeks, so we have to preprocess\r | |
367 | // the events and store special span events as placeholders so that\r | |
368 | // the render routine can build the necessary TD spans correctly.\r | |
369 | var w1 = w,\r | |
370 | d1 = d,\r | |
371 | row = this.findEmptyRowIndex(w, d, allday),\r | |
372 | dt = Ext.Date.clone(this.viewStart);\r | |
373 | \r | |
374 | var start = {\r | |
375 | event: evt,\r | |
376 | isSpan: true,\r | |
377 | isSpanStart: true,\r | |
378 | spanLeft: false,\r | |
379 | spanRight: (d == 6)\r | |
380 | };\r | |
381 | grid[w][d] = grid[w][d] || [];\r | |
382 | grid[w][d][row] = start;\r | |
383 | \r | |
384 | while (--days) {\r | |
385 | dt = Ext.calendar.util.Date.add(dt, {days: 1});\r | |
386 | if (dt > this.viewEnd) {\r | |
387 | break;\r | |
388 | }\r | |
389 | if (++d1 > 6) {\r | |
390 | // reset counters to the next week\r | |
391 | d1 = 0;\r | |
392 | w1++;\r | |
393 | row = this.findEmptyRowIndex(w1, 0);\r | |
394 | }\r | |
395 | grid[w1] = grid[w1] || [];\r | |
396 | grid[w1][d1] = grid[w1][d1] || [];\r | |
397 | \r | |
398 | grid[w1][d1][row] = {\r | |
399 | event: evt,\r | |
400 | isSpan: true,\r | |
401 | isSpanStart: (d1 == 0),\r | |
402 | spanLeft: (w1 > w) && (d1 % 7 == 0),\r | |
403 | spanRight: (d1 == 6) && (days > 1)\r | |
404 | };\r | |
405 | }\r | |
406 | },\r | |
407 | \r | |
408 | // private\r | |
409 | findEmptyRowIndex: function(w, d, allday) {\r | |
410 | var grid = allday ? this.allDayGrid: this.eventGrid,\r | |
411 | day = grid[w] ? grid[w][d] || [] : [],\r | |
412 | i = 0,\r | |
413 | ln = day.length;\r | |
414 | \r | |
415 | for (; i < ln; i++) {\r | |
416 | if (day[i] == null) {\r | |
417 | return i;\r | |
418 | }\r | |
419 | }\r | |
420 | return ln;\r | |
421 | },\r | |
422 | \r | |
423 | // private\r | |
424 | renderTemplate: function() {\r | |
425 | if (this.tpl) {\r | |
426 | this.el.select('*').destroy();\r | |
427 | this.tpl.overwrite(this.el, this.getParams());\r | |
428 | this.lastRenderStart = Ext.Date.clone(this.viewStart);\r | |
429 | this.lastRenderEnd = Ext.Date.clone(this.viewEnd);\r | |
430 | }\r | |
431 | },\r | |
432 | \r | |
433 | disableStoreEvents: function() {\r | |
434 | this.monitorStoreEvents = false;\r | |
435 | },\r | |
436 | \r | |
437 | enableStoreEvents: function(refresh) {\r | |
438 | this.monitorStoreEvents = true;\r | |
439 | if (refresh === true) {\r | |
440 | this.refresh();\r | |
441 | }\r | |
442 | },\r | |
443 | \r | |
444 | // private\r | |
445 | onResize: function() {\r | |
446 | this.callParent(arguments);\r | |
447 | this.refresh();\r | |
448 | },\r | |
449 | \r | |
450 | // private\r | |
451 | onInitDrag: function() {\r | |
452 | this.fireEvent('initdrag', this);\r | |
453 | },\r | |
454 | \r | |
455 | // private\r | |
456 | onEventDrop: function(rec, dt) {\r | |
457 | if (Ext.calendar.util.Date.compare(rec.data[Ext.calendar.data.EventMappings.StartDate.name], dt) === 0) {\r | |
458 | // no changes\r | |
459 | return;\r | |
460 | }\r | |
461 | var diff = dt.getTime() - rec.data[Ext.calendar.data.EventMappings.StartDate.name].getTime();\r | |
462 | rec.set(Ext.calendar.data.EventMappings.StartDate.name, dt);\r | |
463 | rec.set(Ext.calendar.data.EventMappings.EndDate.name, Ext.calendar.util.Date.add(rec.data[Ext.calendar.data.EventMappings.EndDate.name], {millis: diff}));\r | |
464 | \r | |
465 | this.fireEvent('eventmove', this, rec);\r | |
466 | },\r | |
467 | \r | |
468 | // private\r | |
469 | onCalendarEndDrag: function(start, end, onComplete) {\r | |
470 | if (start && end) {\r | |
471 | // set this flag for other event handlers that might conflict while we're waiting\r | |
472 | this.dragPending = true;\r | |
473 | \r | |
474 | // have to wait for the user to save or cancel before finalizing the dd interation\r | |
475 | var o = {};\r | |
476 | o[Ext.calendar.data.EventMappings.StartDate.name] = start;\r | |
477 | o[Ext.calendar.data.EventMappings.EndDate.name] = end;\r | |
478 | \r | |
479 | this.fireEvent('rangeselect', this, o, Ext.bind(this.onCalendarEndDragComplete, this, [onComplete]));\r | |
480 | }\r | |
481 | },\r | |
482 | \r | |
483 | // private\r | |
484 | onCalendarEndDragComplete: function(onComplete) {\r | |
485 | // callback for the drop zone to clean up\r | |
486 | onComplete();\r | |
487 | // clear flag for other events to resume normally\r | |
488 | this.dragPending = false;\r | |
489 | },\r | |
490 | \r | |
491 | // private\r | |
492 | onUpdate: function(ds, rec, operation) {\r | |
493 | if (this.monitorStoreEvents === false) {\r | |
494 | return;\r | |
495 | }\r | |
496 | if (operation == Ext.data.Record.COMMIT) {\r | |
497 | this.refresh();\r | |
498 | if (this.enableFx && this.enableUpdateFx) {\r | |
499 | this.doUpdateFx(this.getEventEls(rec.data[Ext.calendar.data.EventMappings.EventId.name]), {\r | |
500 | scope: this\r | |
501 | });\r | |
502 | }\r | |
503 | }\r | |
504 | },\r | |
505 | \r | |
506 | \r | |
507 | doUpdateFx: function(els, o) {\r | |
508 | this.highlightEvent(els, null, o);\r | |
509 | },\r | |
510 | \r | |
511 | // private\r | |
512 | onAdd: function(ds, records, index) {\r | |
513 | if (this.monitorStoreEvents === false) {\r | |
514 | return;\r | |
515 | }\r | |
516 | var rec = records[0];\r | |
517 | this.tempEventId = rec.id;\r | |
518 | this.refresh();\r | |
519 | \r | |
520 | if (this.enableFx && this.enableAddFx) {\r | |
521 | this.doAddFx(this.getEventEls(rec.data[Ext.calendar.data.EventMappings.EventId.name]), {\r | |
522 | scope: this\r | |
523 | });\r | |
524 | }\r | |
525 | },\r | |
526 | \r | |
527 | doAddFx: function(els, o) {\r | |
528 | els.fadeIn(Ext.apply(o, {\r | |
529 | duration: 2000\r | |
530 | }));\r | |
531 | },\r | |
532 | \r | |
533 | // private\r | |
534 | onRemove: function(ds, recs) {\r | |
535 | var name = Ext.calendar.data.EventMappings.EventId.name,\r | |
536 | i, len, rec, els;\r | |
537 | \r | |
538 | if (this.monitorStoreEvents === false) {\r | |
539 | return;\r | |
540 | }\r | |
541 | \r | |
542 | for (i = 0, len = recs.length; i < len; i++) {\r | |
543 | rec = recs[i];\r | |
544 | \r | |
545 | if (this.enableFx && this.enableRemoveFx) {\r | |
546 | els = this.getEventEls(rec.get(name));\r | |
547 | \r | |
548 | if (els.getCount() > 0) {\r | |
549 | this.doRemoveFx(els, {\r | |
550 | remove: true,\r | |
551 | scope: this,\r | |
552 | callback: this.refresh\r | |
553 | });\r | |
554 | }\r | |
555 | }\r | |
556 | else {\r | |
557 | this.getEventEls(rec.get(name)).remove();\r | |
558 | this.refresh();\r | |
559 | }\r | |
560 | }\r | |
561 | },\r | |
562 | \r | |
563 | doRemoveFx: function(els, o) {\r | |
564 | els.fadeOut(o);\r | |
565 | },\r | |
566 | \r | |
567 | /**\r | |
568 | * Visually highlights an event using {@link Ext.Fx#highlight} config options.\r | |
569 | * If {@link #highlightEventActions} is false this method will have no effect.\r | |
570 | * @param {Ext.CompositeElement} els The element(s) to highlight\r | |
571 | * @param {Object} color (optional) The highlight color. Should be a 6 char hex \r | |
572 | * color without the leading # (defaults to yellow: 'ffff9c')\r | |
573 | * @param {Object} o (optional) Object literal with any of the {@link Ext.Fx} config \r | |
574 | * options. See {@link Ext.Fx#highlight} for usage examples.\r | |
575 | */\r | |
576 | highlightEvent: function(els, color, o) {\r | |
577 | if (this.enableFx) {\r | |
578 | var c;\r | |
579 | ! (Ext.isIE || Ext.isOpera) ?\r | |
580 | els.highlight(color, o) :\r | |
581 | // Fun IE/Opera handling:\r | |
582 | els.each(function(el) {\r | |
583 | el.highlight(color, Ext.applyIf({\r | |
584 | attr: 'color'\r | |
585 | },\r | |
586 | o));\r | |
587 | c = el.down('.ext-cal-evm');\r | |
588 | if (c) {\r | |
589 | c.highlight(color, o);\r | |
590 | }\r | |
591 | },\r | |
592 | this);\r | |
593 | }\r | |
594 | },\r | |
595 | \r | |
596 | /**\r | |
597 | * Retrieve an Event object's id from its corresponding node in the DOM.\r | |
598 | * @param {String/Element/HTMLElement} el An {@link Ext.core.Element}, DOM node or id\r | |
599 | */\r | |
600 | getEventIdFromEl: function(el) {\r | |
601 | el = Ext.get(el);\r | |
602 | var id = el.id.split(this.eventElIdDelimiter)[1],\r | |
603 | lastHypen = id.lastIndexOf('-');\r | |
604 | \r | |
605 | // MUST look for last hyphen because autogenned record IDs can contain hyphens\r | |
606 | if (lastHypen > -1) {\r | |
607 | //This id has the index of the week it is rendered in as the suffix.\r | |
608 | //This allows events that span across weeks to still have reproducibly-unique DOM ids.\r | |
609 | id = id.substr(0, lastHypen);\r | |
610 | }\r | |
611 | return id;\r | |
612 | },\r | |
613 | \r | |
614 | // private\r | |
615 | getEventId: function(eventId) {\r | |
616 | if (eventId === undefined && this.tempEventId) {\r | |
617 | eventId = this.tempEventId;\r | |
618 | }\r | |
619 | return eventId;\r | |
620 | },\r | |
621 | \r | |
622 | /**\r | |
623 | * \r | |
624 | * @param {String} eventId\r | |
625 | * @param {Boolean} forSelect\r | |
626 | * @return {String} The selector class\r | |
627 | */\r | |
628 | getEventSelectorCls: function(eventId, forSelect) {\r | |
629 | var prefix = forSelect ? '.': '';\r | |
630 | return prefix + this.id + this.eventElIdDelimiter + this.getEventId(eventId);\r | |
631 | },\r | |
632 | \r | |
633 | /**\r | |
634 | * \r | |
635 | * @param {String} eventId\r | |
636 | * @return {Ext.CompositeElement} The matching CompositeElement of nodes\r | |
637 | * that comprise the rendered event. Any event that spans across a view \r | |
638 | * boundary will contain more than one internal Element.\r | |
639 | */\r | |
640 | getEventEls: function(eventId) {\r | |
641 | var els = Ext.select(this.getEventSelectorCls(this.getEventId(eventId), true), false, this.el.dom);\r | |
642 | return new Ext.CompositeElement(els);\r | |
643 | },\r | |
644 | \r | |
645 | /**\r | |
646 | * Returns true if the view is currently displaying today's date, else false.\r | |
647 | * @return {Boolean} True or false\r | |
648 | */\r | |
649 | isToday: function() {\r | |
650 | var today = Ext.Date.clearTime(new Date()).getTime();\r | |
651 | return this.viewStart.getTime() <= today && this.viewEnd.getTime() >= today;\r | |
652 | },\r | |
653 | \r | |
654 | // private\r | |
655 | onDataChanged: function(store) {\r | |
656 | this.refresh();\r | |
657 | },\r | |
658 | \r | |
659 | // private\r | |
660 | isEventVisible: function(evt) {\r | |
661 | var M = Ext.calendar.data.EventMappings,\r | |
662 | data = evt.data || evt,\r | |
663 | start = this.viewStart.getTime(),\r | |
664 | end = this.viewEnd.getTime(),\r | |
665 | evStart = data[M.StartDate.name].getTime(),\r | |
666 | evEnd = data[M.EndDate.name].getTime();\r | |
667 | evEnd = Ext.calendar.util.Date.add(data[M.EndDate.name], {seconds: -1}).getTime();\r | |
668 | \r | |
669 | return this.rangesOverlap(start, end, evStart, evEnd);\r | |
670 | },\r | |
671 | \r | |
672 | rangesOverlap: function(start1, end1, start2, end2) {\r | |
673 | var startsInRange = (start1 >= start2 && start1 <= end2),\r | |
674 | endsInRange = (end1 >= start2 && end1 <= end2),\r | |
675 | spansRange = (start1 <= start2 && end1 >= end2);\r | |
676 | \r | |
677 | return (startsInRange || endsInRange || spansRange);\r | |
678 | },\r | |
679 | \r | |
680 | // private\r | |
681 | isOverlapping: function(evt1, evt2) {\r | |
682 | var ev1 = evt1.data ? evt1.data: evt1,\r | |
683 | ev2 = evt2.data ? evt2.data: evt2,\r | |
684 | M = Ext.calendar.data.EventMappings,\r | |
685 | start1 = ev1[M.StartDate.name].getTime(),\r | |
686 | end1 = Ext.calendar.util.Date.add(ev1[M.EndDate.name], {seconds: -1}).getTime(),\r | |
687 | start2 = ev2[M.StartDate.name].getTime(),\r | |
688 | end2 = Ext.calendar.util.Date.add(ev2[M.EndDate.name], {seconds: -1}).getTime();\r | |
689 | \r | |
690 | if (end1 < start1) {\r | |
691 | end1 = start1;\r | |
692 | }\r | |
693 | if (end2 < start2) {\r | |
694 | end2 = start2;\r | |
695 | }\r | |
696 | \r | |
697 | return (start1 <= end2 && end1 >= start2);\r | |
698 | },\r | |
699 | \r | |
700 | getDayEl: function(dt) {\r | |
701 | return Ext.get(this.getDayId(dt));\r | |
702 | },\r | |
703 | \r | |
704 | getDayId: function(dt) {\r | |
705 | if (Ext.isDate(dt)) {\r | |
706 | dt = Ext.Date.format(dt, 'Ymd');\r | |
707 | }\r | |
708 | return this.id + this.dayElIdDelimiter + dt;\r | |
709 | },\r | |
710 | \r | |
711 | /**\r | |
712 | * Returns the start date of the view, as set by {@link #setStartDate}. Note that this may not \r | |
713 | * be the first date displayed in the rendered calendar -- to get the start and end dates displayed\r | |
714 | * to the user use {@link #getViewBounds}.\r | |
715 | * @return {Date} The start date\r | |
716 | */\r | |
717 | getStartDate: function() {\r | |
718 | return this.startDate;\r | |
719 | },\r | |
720 | \r | |
721 | /**\r | |
722 | * Sets the start date used to calculate the view boundaries to display. The displayed view will be the \r | |
723 | * earliest and latest dates that match the view requirements and contain the date passed to this function.\r | |
724 | * @param {Date} dt The date used to calculate the new view boundaries\r | |
725 | */\r | |
726 | setStartDate: function(start, refresh) {\r | |
727 | this.startDate = Ext.Date.clearTime(start);\r | |
728 | this.setViewBounds(start);\r | |
729 | this.store.load({\r | |
730 | params: {\r | |
731 | start: Ext.Date.format(this.viewStart, 'm-d-Y'),\r | |
732 | end: Ext.Date.format(this.viewEnd, 'm-d-Y')\r | |
733 | }\r | |
734 | });\r | |
735 | if (refresh === true) {\r | |
736 | this.refresh();\r | |
737 | }\r | |
738 | this.fireEvent('datechange', this, this.startDate, this.viewStart, this.viewEnd);\r | |
739 | },\r | |
740 | \r | |
741 | // private\r | |
742 | setViewBounds: function(startDate) {\r | |
743 | var start = startDate || this.startDate,\r | |
744 | offset = start.getDay() - this.startDay,\r | |
745 | Dt = Ext.calendar.util.Date;\r | |
746 | \r | |
747 | switch (this.weekCount) {\r | |
748 | case 0:\r | |
749 | case 1:\r | |
750 | this.viewStart = this.dayCount < 7 ? start: Dt.add(start, {days: -offset, clearTime: true});\r | |
751 | this.viewEnd = Dt.add(this.viewStart, {days: this.dayCount || 7});\r | |
752 | this.viewEnd = Dt.add(this.viewEnd, {seconds: -1});\r | |
753 | return;\r | |
754 | \r | |
755 | case - 1:\r | |
756 | // auto by month\r | |
757 | start = Ext.Date.getFirstDateOfMonth(start);\r | |
758 | offset = start.getDay() - this.startDay;\r | |
759 | \r | |
760 | this.viewStart = Dt.add(start, {days: -offset, clearTime: true});\r | |
761 | \r | |
762 | // start from current month start, not view start:\r | |
763 | var end = Dt.add(start, {months: 1, seconds: -1});\r | |
764 | // fill out to the end of the week:\r | |
765 | this.viewEnd = Dt.add(end, {days: 6 - end.getDay()});\r | |
766 | return;\r | |
767 | \r | |
768 | default:\r | |
769 | this.viewStart = Dt.add(start, {days: -offset, clearTime: true});\r | |
770 | this.viewEnd = Dt.add(this.viewStart, {days: this.weekCount * 7, seconds: -1});\r | |
771 | }\r | |
772 | },\r | |
773 | \r | |
774 | // private\r | |
775 | getViewBounds: function() {\r | |
776 | return {\r | |
777 | start: this.viewStart,\r | |
778 | end: this.viewEnd\r | |
779 | };\r | |
780 | },\r | |
781 | \r | |
782 | /* private\r | |
783 | * Sort events for a single day for display in the calendar. This sorts allday\r | |
784 | * events first, then non-allday events are sorted either based on event start\r | |
785 | * priority or span priority based on the value of {@link #spansHavePriority} \r | |
786 | * (defaults to event start priority).\r | |
787 | * @param {MixedCollection} evts A {@link Ext.util.MixedCollection MixedCollection} \r | |
788 | * of {@link #Ext.calendar.EventRecord EventRecord} objects\r | |
789 | */\r | |
790 | sortEventRecordsForDay: function(evts) {\r | |
791 | if (evts.length < 2) {\r | |
792 | return;\r | |
793 | }\r | |
794 | evts.sortBy(Ext.bind(function(evtA, evtB) {\r | |
795 | var a = evtA.data,\r | |
796 | b = evtB.data,\r | |
797 | M = Ext.calendar.data.EventMappings;\r | |
798 | \r | |
799 | // Always sort all day events before anything else\r | |
800 | if (a[M.IsAllDay.name]) {\r | |
801 | return - 1;\r | |
802 | }\r | |
803 | else if (b[M.IsAllDay.name]) {\r | |
804 | return 1;\r | |
805 | }\r | |
806 | if (this.spansHavePriority) {\r | |
807 | // This logic always weights span events higher than non-span events\r | |
808 | // (at the possible expense of start time order). This seems to\r | |
809 | // be the approach used by Google calendar and can lead to a more\r | |
810 | // visually appealing layout in complex cases, but event order is\r | |
811 | // not guaranteed to be consistent.\r | |
812 | var diff = Ext.calendar.util.Date.diffDays;\r | |
813 | if (diff(a[M.StartDate.name], a[M.EndDate.name]) > 0) {\r | |
814 | if (diff(b[M.StartDate.name], b[M.EndDate.name]) > 0) {\r | |
815 | // Both events are multi-day\r | |
816 | if (a[M.StartDate.name].getTime() == b[M.StartDate.name].getTime()) {\r | |
817 | // If both events start at the same time, sort the one\r | |
818 | // that ends later (potentially longer span bar) first\r | |
819 | return b[M.EndDate.name].getTime() - a[M.EndDate.name].getTime();\r | |
820 | }\r | |
821 | return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();\r | |
822 | }\r | |
823 | return - 1;\r | |
824 | }\r | |
825 | else if (diff(b[M.StartDate.name], b[M.EndDate.name]) > 0) {\r | |
826 | return 1;\r | |
827 | }\r | |
828 | return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();\r | |
829 | }\r | |
830 | else {\r | |
831 | // Doing this allows span and non-span events to intermingle but\r | |
832 | // remain sorted sequentially by start time. This seems more proper\r | |
833 | // but can make for a less visually-compact layout when there are\r | |
834 | // many such events mixed together closely on the calendar.\r | |
835 | return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();\r | |
836 | }\r | |
837 | }, this));\r | |
838 | },\r | |
839 | \r | |
840 | /**\r | |
841 | * Updates the view to contain the passed date\r | |
842 | * @param {Date} dt The date to display\r | |
843 | * @return {Date} The new view start date\r | |
844 | */\r | |
845 | moveTo: function(dt, noRefresh) {\r | |
846 | if (Ext.isDate(dt)) {\r | |
847 | this.setStartDate(dt);\r | |
848 | if (noRefresh !== false) {\r | |
849 | this.refresh();\r | |
850 | }\r | |
851 | return this.startDate;\r | |
852 | }\r | |
853 | return dt;\r | |
854 | },\r | |
855 | \r | |
856 | /**\r | |
857 | * Updates the view to the next consecutive date(s)\r | |
858 | * @return {Date} The new view start date\r | |
859 | */\r | |
860 | moveNext: function(noRefresh) {\r | |
861 | return this.moveTo(Ext.calendar.util.Date.add(this.viewEnd, {days: 1}));\r | |
862 | },\r | |
863 | \r | |
864 | /**\r | |
865 | * Updates the view to the previous consecutive date(s)\r | |
866 | * @return {Date} The new view start date\r | |
867 | */\r | |
868 | movePrev: function(noRefresh) {\r | |
869 | var days = Ext.calendar.util.Date.diffDays(this.viewStart, this.viewEnd) + 1;\r | |
870 | return this.moveDays( - days, noRefresh);\r | |
871 | },\r | |
872 | \r | |
873 | /**\r | |
874 | * Shifts the view by the passed number of months relative to the currently set date\r | |
875 | * @param {Number} value The number of months (positive or negative) by which to shift the view\r | |
876 | * @return {Date} The new view start date\r | |
877 | */\r | |
878 | moveMonths: function(value, noRefresh) {\r | |
879 | return this.moveTo(Ext.calendar.util.Date.add(this.startDate, {months: value}), noRefresh);\r | |
880 | },\r | |
881 | \r | |
882 | /**\r | |
883 | * Shifts the view by the passed number of weeks relative to the currently set date\r | |
884 | * @param {Number} value The number of weeks (positive or negative) by which to shift the view\r | |
885 | * @return {Date} The new view start date\r | |
886 | */\r | |
887 | moveWeeks: function(value, noRefresh) {\r | |
888 | return this.moveTo(Ext.calendar.util.Date.add(this.startDate, {days: value * 7}), noRefresh);\r | |
889 | },\r | |
890 | \r | |
891 | /**\r | |
892 | * Shifts the view by the passed number of days relative to the currently set date\r | |
893 | * @param {Number} value The number of days (positive or negative) by which to shift the view\r | |
894 | * @return {Date} The new view start date\r | |
895 | */\r | |
896 | moveDays: function(value, noRefresh) {\r | |
897 | return this.moveTo(Ext.calendar.util.Date.add(this.startDate, {days: value}), noRefresh);\r | |
898 | },\r | |
899 | \r | |
900 | /**\r | |
901 | * Updates the view to show today\r | |
902 | * @return {Date} Today's date\r | |
903 | */\r | |
904 | moveToday: function(noRefresh) {\r | |
905 | return this.moveTo(new Date(), noRefresh);\r | |
906 | },\r | |
907 | \r | |
908 | /**\r | |
909 | * Sets the event store used by the calendar to display {@link Ext.calendar.EventRecord events}.\r | |
910 | * @param {Ext.data.Store} store\r | |
911 | */\r | |
912 | setStore: function(store, initial) {\r | |
913 | if (!initial && this.store) {\r | |
914 | this.store.un("datachanged", this.onDataChanged, this);\r | |
915 | this.store.un("add", this.onAdd, this);\r | |
916 | this.store.un("remove", this.onRemove, this);\r | |
917 | this.store.un("update", this.onUpdate, this);\r | |
918 | this.store.un("clear", this.refresh, this);\r | |
919 | }\r | |
920 | if (store) {\r | |
921 | store.on("datachanged", this.onDataChanged, this);\r | |
922 | store.on("add", this.onAdd, this);\r | |
923 | store.on("remove", this.onRemove, this);\r | |
924 | store.on("update", this.onUpdate, this);\r | |
925 | store.on("clear", this.refresh, this);\r | |
926 | }\r | |
927 | this.store = store;\r | |
928 | if (store && store.getCount() > 0) {\r | |
929 | this.refresh();\r | |
930 | }\r | |
931 | },\r | |
932 | \r | |
933 | getEventRecord: function(id) {\r | |
934 | var idx = this.store.find(Ext.calendar.data.EventMappings.EventId.name, id);\r | |
935 | return this.store.getAt(idx);\r | |
936 | },\r | |
937 | \r | |
938 | getEventRecordFromEl: function(el) {\r | |
939 | return this.getEventRecord(this.getEventIdFromEl(el));\r | |
940 | },\r | |
941 | \r | |
942 | // private\r | |
943 | getParams: function() {\r | |
944 | return {\r | |
945 | viewStart: this.viewStart,\r | |
946 | viewEnd: this.viewEnd,\r | |
947 | startDate: this.startDate,\r | |
948 | dayCount: this.dayCount,\r | |
949 | weekCount: this.weekCount,\r | |
950 | title: this.getTitle()\r | |
951 | };\r | |
952 | },\r | |
953 | \r | |
954 | getTitle: function() {\r | |
955 | return Ext.Date.format(this.startDate, 'F Y');\r | |
956 | },\r | |
957 | \r | |
958 | /*\r | |
959 | * Shared click handling. Each specific view also provides view-specific\r | |
960 | * click handling that calls this first. This method returns true if it\r | |
961 | * can handle the click (and so the subclass should ignore it) else false.\r | |
962 | */\r | |
963 | onClick: function(e, t) {\r | |
964 | var el = e.getTarget(this.eventSelector, 5);\r | |
965 | if (el) {\r | |
966 | var id = this.getEventIdFromEl(el);\r | |
967 | this.fireEvent('eventclick', this, this.getEventRecord(id), el);\r | |
968 | return true;\r | |
969 | }\r | |
970 | },\r | |
971 | \r | |
972 | // private\r | |
973 | onMouseOver: function(e, t) {\r | |
974 | if (this.trackMouseOver !== false && (this.dragZone == undefined || !this.dragZone.dragging)) {\r | |
975 | if (!this.handleEventMouseEvent(e, t, 'over')) {\r | |
976 | this.handleDayMouseEvent(e, t, 'over');\r | |
977 | }\r | |
978 | }\r | |
979 | },\r | |
980 | \r | |
981 | // private\r | |
982 | onMouseOut: function(e, t) {\r | |
983 | if (this.trackMouseOver !== false && (this.dragZone == undefined || !this.dragZone.dragging)) {\r | |
984 | if (!this.handleEventMouseEvent(e, t, 'out')) {\r | |
985 | this.handleDayMouseEvent(e, t, 'out');\r | |
986 | }\r | |
987 | }\r | |
988 | },\r | |
989 | \r | |
990 | // private\r | |
991 | handleEventMouseEvent: function(e, t, type) {\r | |
992 | var el = e.getTarget(this.eventSelector, 5, true),\r | |
993 | rel,\r | |
994 | els,\r | |
995 | evtId;\r | |
996 | if (el) {\r | |
997 | rel = Ext.get(e.getRelatedTarget());\r | |
998 | if (el == rel || el.contains(rel)) {\r | |
999 | return true;\r | |
1000 | }\r | |
1001 | \r | |
1002 | evtId = this.getEventIdFromEl(el);\r | |
1003 | \r | |
1004 | if (this.eventOverClass) {\r | |
1005 | els = this.getEventEls(evtId);\r | |
1006 | els[type == 'over' ? 'addCls': 'removeCls'](this.eventOverClass);\r | |
1007 | }\r | |
1008 | this.fireEvent('event' + type, this, this.getEventRecord(evtId), el);\r | |
1009 | return true;\r | |
1010 | }\r | |
1011 | return false;\r | |
1012 | },\r | |
1013 | \r | |
1014 | // private\r | |
1015 | getDateFromId: function(id, delim) {\r | |
1016 | var parts = id.split(delim);\r | |
1017 | return parts[parts.length - 1];\r | |
1018 | },\r | |
1019 | \r | |
1020 | // private\r | |
1021 | handleDayMouseEvent: function(e, t, type) {\r | |
1022 | t = e.getTarget('td', 3);\r | |
1023 | if (t) {\r | |
1024 | if (t.id && t.id.indexOf(this.dayElIdDelimiter) > -1) {\r | |
1025 | var dt = this.getDateFromId(t.id, this.dayElIdDelimiter),\r | |
1026 | rel = Ext.get(e.getRelatedTarget()),\r | |
1027 | relTD,\r | |
1028 | relDate;\r | |
1029 | \r | |
1030 | if (rel) {\r | |
1031 | relTD = rel.is('td') ? rel: rel.up('td', 3);\r | |
1032 | relDate = relTD && relTD.id ? this.getDateFromId(relTD.id, this.dayElIdDelimiter) : '';\r | |
1033 | }\r | |
1034 | if (!rel || dt != relDate) {\r | |
1035 | var el = this.getDayEl(dt);\r | |
1036 | if (el && this.dayOverClass != '') {\r | |
1037 | el[type == 'over' ? 'addCls': 'removeCls'](this.dayOverClass);\r | |
1038 | }\r | |
1039 | this.fireEvent('day' + type, this, Ext.Date.parseDate(dt, "Ymd"), el);\r | |
1040 | }\r | |
1041 | }\r | |
1042 | }\r | |
1043 | },\r | |
1044 | \r | |
1045 | // private\r | |
1046 | renderItems: function() {\r | |
1047 | throw 'This method must be implemented by a subclass';\r | |
1048 | },\r | |
1049 | \r | |
1050 | // private\r | |
1051 | destroy: function(){\r | |
1052 | this.callParent(arguments);\r | |
1053 | \r | |
1054 | if(this.el){\r | |
1055 | this.el.un('contextmenu', this.onContextMenu, this);\r | |
1056 | }\r | |
1057 | Ext.destroy(\r | |
1058 | this.editWin, \r | |
1059 | this.eventMenu,\r | |
1060 | this.dragZone,\r | |
1061 | this.dropZone\r | |
1062 | );\r | |
1063 | }\r | |
1064 | }); |