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>
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
15 <div id="cls-Ext.calendar.CalendarView"></div>/**
\r
16 * @class Ext.calendar.CalendarView
\r
17 * @extends Ext.BoxComponent
\r
18 * <p>This is an abstract class that serves as the base for other calendar views. This class is not
\r
19 * intended to be directly instantiated.</p>
\r
20 * <p>When extending this class to create a custom calendar view, you must provide an implementation
\r
21 * for the <code>renderItems</code> method, as there is no default implementation for rendering events
\r
22 * The rendering logic is totally dependent on how the UI structures its data, which
\r
23 * is determined by the underlying UI template (this base class does not have a template).</p>
\r
25 * @param {Object} config The config object
\r
27 Ext.calendar.CalendarView = Ext.extend(Ext.BoxComponent, {
\r
28 <div id="cfg-Ext.calendar.CalendarView-startDay"></div>/**
\r
29 * @cfg {Number} startDay
\r
30 * The 0-based index for the day on which the calendar week begins (0=Sunday, which is the default)
\r
33 <div id="cfg-Ext.calendar.CalendarView-spansHavePriority"></div>/**
\r
34 * @cfg {Boolean} spansHavePriority
\r
35 * Allows switching between two different modes of rendering events that span multiple days. When true,
\r
36 * span events are always sorted first, possibly at the expense of start dates being out of order (e.g.,
\r
37 * a span event that starts at 11am one day and spans into the next day would display before a non-spanning
\r
38 * event that starts at 10am, even though they would not be in date order). This can lead to more compact
\r
39 * layouts when there are many overlapping events. If false (the default), events will always sort by start date
\r
40 * first which can result in a less compact, but chronologically consistent layout.
\r
42 spansHavePriority: false,
\r
43 <div id="cfg-Ext.calendar.CalendarView-trackMouseOver"></div>/**
\r
44 * @cfg {Boolean} trackMouseOver
\r
45 * Whether or not the view tracks and responds to the browser mouseover event on contained elements (defaults to
\r
46 * true). If you don't need mouseover event highlighting you can disable this.
\r
48 trackMouseOver: true,
\r
49 <div id="cfg-Ext.calendar.CalendarView-enableFx"></div>/**
\r
50 * @cfg {Boolean} enableFx
\r
51 * Determines whether or not visual effects for CRUD actions are enabled (defaults to true). If this is false
\r
52 * it will override any values for {@link #enableAddFx}, {@link #enableUpdateFx} or {@link enableRemoveFx} and
\r
53 * all animations will be disabled.
\r
56 <div id="cfg-Ext.calendar.CalendarView-enableAddFx"></div>/**
\r
57 * @cfg {Boolean} enableAddFx
\r
58 * True to enable a visual effect on adding a new event (the default), false to disable it. Note that if
\r
59 * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the
\r
60 * {@link #doAddFx} method.
\r
63 <div id="cfg-Ext.calendar.CalendarView-enableUpdateFx"></div>/**
\r
64 * @cfg {Boolean} enableUpdateFx
\r
65 * True to enable a visual effect on updating an event, false to disable it (the default). Note that if
\r
66 * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the
\r
67 * {@link #doUpdateFx} method.
\r
69 enableUpdateFx: false,
\r
70 <div id="cfg-Ext.calendar.CalendarView-enableRemoveFx"></div>/**
\r
71 * @cfg {Boolean} enableRemoveFx
\r
72 * True to enable a visual effect on removing an event (the default), false to disable it. Note that if
\r
73 * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the
\r
74 * {@link #doRemoveFx} method.
\r
76 enableRemoveFx: true,
\r
77 <div id="cfg-Ext.calendar.CalendarView-enableDD"></div>/**
\r
78 * @cfg {Boolean} enableDD
\r
79 * True to enable drag and drop in the calendar view (the default), false to disable it
\r
82 <div id="cfg-Ext.calendar.CalendarView-monitorResize"></div>/**
\r
83 * @cfg {Boolean} monitorResize
\r
84 * True to monitor the browser's resize event (the default), false to ignore it. If the calendar view is rendered
\r
85 * into a fixed-size container this can be set to false. However, if the view can change dimensions (e.g., it's in
\r
86 * fit layout in a viewport or some other resizable container) it is very important that this config is true so that
\r
87 * any resize event propagates properly to all subcomponents and layouts get recalculated properly.
\r
89 monitorResize: true,
\r
90 <div id="cfg-Ext.calendar.CalendarView-ddCreateEventText"></div>/**
\r
91 * @cfg {String} ddCreateEventText
\r
92 * The text to display inside the drag proxy while dragging over the calendar to create a new event (defaults to
\r
93 * 'Create event for {0}' where {0} is a date range supplied by the view)
\r
95 ddCreateEventText: 'Create event for {0}',
\r
96 <div id="cfg-Ext.calendar.CalendarView-ddMoveEventText"></div>/**
\r
97 * @cfg {String} ddMoveEventText
\r
98 * The text to display inside the drag proxy while dragging an event to reposition it (defaults to
\r
99 * 'Move event to {0}' where {0} is the updated event start date/time supplied by the view)
\r
101 ddMoveEventText: 'Move event to {0}',
\r
102 <div id="cfg-Ext.calendar.CalendarView-ddResizeEventText"></div>/**
\r
103 * @cfg {String} ddResizeEventText
\r
104 * The string displayed to the user in the drag proxy while dragging the resize handle of an event (defaults to
\r
105 * 'Update event to {0}' where {0} is the updated event start-end range supplied by the view). Note that
\r
106 * this text is only used in views
\r
107 * that allow resizing of events.
\r
109 ddResizeEventText: 'Update event to {0}',
\r
111 //private properties -- do not override:
\r
114 eventSelector: '.ext-cal-evt',
\r
115 eventOverClass: 'ext-evt-over',
\r
116 eventElIdDelimiter: '-evt-',
\r
117 dayElIdDelimiter: '-day-',
\r
119 <div id="method-Ext.calendar.CalendarView-getEventBodyMarkup"></div>/**
\r
120 * Returns a string of HTML template markup to be used as the body portion of the event template created
\r
121 * by {@link #getEventTemplate}. This provdes the flexibility to customize what's in the body without
\r
122 * having to override the entire XTemplate. This string can include any valid {@link Ext.Template} code, and
\r
123 * any data tokens accessible to the containing event template can be referenced in this string.
\r
124 * @return {String} The body template string
\r
126 getEventBodyMarkup: Ext.emptyFn,
\r
127 // must be implemented by a subclass
\r
128 <div id="method-Ext.calendar.CalendarView-getEventTemplate"></div>/**
\r
129 * <p>Returns the XTemplate that is bound to the calendar's event store (it expects records of type
\r
130 * {@link Ext.calendar.EventRecord}) to populate the calendar views with events. Internally this method
\r
131 * by default generates different markup for browsers that support CSS border radius and those that don't.
\r
132 * This method can be overridden as needed to customize the markup generated.</p>
\r
133 * <p>Note that this method calls {@link #getEventBodyMarkup} to retrieve the body markup for events separately
\r
134 * from the surrounding container markup. This provdes the flexibility to customize what's in the body without
\r
135 * having to override the entire XTemplate. If you do override this method, you should make sure that your
\r
136 * overridden version also does the same.</p>
\r
137 * @return {Ext.XTemplate} The event XTemplate
\r
139 getEventTemplate: Ext.emptyFn,
\r
140 // must be implemented by a subclass
\r
142 initComponent: function() {
\r
143 this.setStartDate(this.startDate || new Date());
\r
145 Ext.calendar.CalendarView.superclass.initComponent.call(this);
\r
148 <div id="event-Ext.calendar.CalendarView-eventsrendered"></div>/**
\r
149 * @event eventsrendered
\r
150 * Fires after events are finished rendering in the view
\r
151 * @param {Ext.calendar.CalendarView} this
\r
153 eventsrendered: true,
\r
154 <div id="event-Ext.calendar.CalendarView-eventclick"></div>/**
\r
155 * @event eventclick
\r
156 * Fires after the user clicks on an event element
\r
157 * @param {Ext.calendar.CalendarView} this
\r
158 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was clicked on
\r
159 * @param {HTMLNode} el The DOM node that was clicked on
\r
162 <div id="event-Ext.calendar.CalendarView-eventover"></div>/**
\r
164 * Fires anytime the mouse is over an event element
\r
165 * @param {Ext.calendar.CalendarView} this
\r
166 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor is over
\r
167 * @param {HTMLNode} el The DOM node that is being moused over
\r
170 <div id="event-Ext.calendar.CalendarView-eventout"></div>/**
\r
172 * Fires anytime the mouse exits an event element
\r
173 * @param {Ext.calendar.CalendarView} this
\r
174 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor exited
\r
175 * @param {HTMLNode} el The DOM node that was exited
\r
178 <div id="event-Ext.calendar.CalendarView-datechange"></div>/**
\r
179 * @event datechange
\r
180 * Fires after the start date of the view changes
\r
181 * @param {Ext.calendar.CalendarView} this
\r
182 * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}
\r
183 * @param {Date} viewStart The first displayed date in the view
\r
184 * @param {Date} viewEnd The last displayed date in the view
\r
187 <div id="event-Ext.calendar.CalendarView-rangeselect"></div>/**
\r
188 * @event rangeselect
\r
189 * Fires after the user drags on the calendar to select a range of dates/times in which to create an event
\r
190 * @param {Ext.calendar.CalendarView} this
\r
191 * @param {Object} dates An object containing the start (StartDate property) and end (EndDate property) dates selected
\r
192 * @param {Function} callback A callback function that MUST be called after the event handling is complete so that
\r
193 * the view is properly cleaned up (shim elements are persisted in the view while the user is prompted to handle the
\r
194 * range selection). The callback is already created in the proper scope, so it simply needs to be executed as a standard
\r
195 * function call (e.g., callback()).
\r
198 <div id="event-Ext.calendar.CalendarView-eventmove"></div>/**
\r
200 * Fires after an event element is dragged by the user and dropped in a new position
\r
201 * @param {Ext.calendar.CalendarView} this
\r
202 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was moved with
\r
203 * updated start and end dates
\r
206 <div id="event-Ext.calendar.CalendarView-initdrag"></div>/**
\r
208 * Fires when a drag operation is initiated in the view
\r
209 * @param {Ext.calendar.CalendarView} this
\r
212 <div id="event-Ext.calendar.CalendarView-dayover"></div>/**
\r
214 * Fires while the mouse is over a day element
\r
215 * @param {Ext.calendar.CalendarView} this
\r
216 * @param {Date} dt The date that is being moused over
\r
217 * @param {Ext.Element} el The day Element that is being moused over
\r
220 <div id="event-Ext.calendar.CalendarView-dayout"></div>/**
\r
222 * Fires when the mouse exits a day element
\r
223 * @param {Ext.calendar.CalendarView} this
\r
224 * @param {Date} dt The date that is exited
\r
225 * @param {Ext.Element} el The day Element that is exited
\r
229 * @event eventdelete
\r
230 * Fires after an event element is deleted by the user. Not currently implemented directly at the view level -- currently
\r
231 * deletes only happen from one of the forms.
\r
232 * @param {Ext.calendar.CalendarView} this
\r
233 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was deleted
\r
235 //eventdelete: true
\r
240 afterRender: function() {
\r
241 Ext.calendar.CalendarView.superclass.afterRender.call(this);
\r
243 this.renderTemplate();
\r
246 this.setStore(this.store, true);
\r
250 'mouseover': this.onMouseOver,
\r
251 'mouseout': this.onMouseOut,
\r
252 'click': this.onClick,
\r
253 'resize': this.onResize,
\r
257 this.el.unselectable();
\r
259 if (this.enableDD && this.initDD) {
\r
263 this.on('eventsrendered', this.forceSize);
\r
264 this.forceSize.defer(100, this);
\r
269 forceSize: function() {
\r
270 if (this.el && this.el.child) {
\r
271 var hd = this.el.child('.ext-cal-hd-ct'),
\r
272 bd = this.el.child('.ext-cal-body-ct');
\r
274 if (bd == null || hd == null) return;
\r
276 var headerHeight = hd.getHeight(),
\r
277 sz = this.el.parent().getSize();
\r
279 bd.setHeight(sz.height - headerHeight);
\r
283 refresh: function() {
\r
284 this.prepareData();
\r
285 this.renderTemplate();
\r
286 this.renderItems();
\r
289 getWeekCount: function() {
\r
290 var days = Ext.calendar.Date.diffDays(this.viewStart, this.viewEnd);
\r
291 return Math.ceil(days / this.dayCount);
\r
295 prepareData: function() {
\r
296 var lastInMonth = this.startDate.getLastDateOfMonth(),
\r
299 dt = this.viewStart.clone(),
\r
300 weeks = this.weekCount < 1 ? 6: this.weekCount;
\r
302 this.eventGrid = [[]];
\r
303 this.allDayGrid = [[]];
\r
304 this.evtMaxCount = [];
\r
306 var evtsInView = this.store.queryBy(function(rec) {
\r
307 return this.isEventVisible(rec.data);
\r
311 for (; w < weeks; w++) {
\r
312 this.evtMaxCount[w] = 0;
\r
313 if (this.weekCount == -1 && dt > lastInMonth) {
\r
314 //current week is fully in next month so skip
\r
317 this.eventGrid[w] = this.eventGrid[w] || [];
\r
318 this.allDayGrid[w] = this.allDayGrid[w] || [];
\r
320 for (d = 0; d < this.dayCount; d++) {
\r
321 if (evtsInView.getCount() > 0) {
\r
322 var evts = evtsInView.filterBy(function(rec) {
\r
323 var startsOnDate = (dt.getTime() == rec.data[Ext.calendar.EventMappings.StartDate.name].clearTime(true).getTime());
\r
324 var spansFromPrevView = (w == 0 && d == 0 && (dt > rec.data[Ext.calendar.EventMappings.StartDate.name]));
\r
325 return startsOnDate || spansFromPrevView;
\r
329 this.sortEventRecordsForDay(evts);
\r
330 this.prepareEventGrid(evts, w, d);
\r
332 dt = dt.add(Date.DAY, 1);
\r
335 this.currentWeekCount = w;
\r
339 prepareEventGrid: function(evts, w, d) {
\r
341 dt = this.viewStart.clone(),
\r
342 max = this.maxEventsPerDay ? this.maxEventsPerDay: 999;
\r
344 evts.each(function(evt) {
\r
345 var M = Ext.calendar.EventMappings,
\r
346 days = Ext.calendar.Date.diffDays(
\r
347 Ext.calendar.Date.max(this.viewStart, evt.data[M.StartDate.name]),
\r
348 Ext.calendar.Date.min(this.viewEnd, evt.data[M.EndDate.name])) + 1;
\r
350 if (days > 1 || Ext.calendar.Date.diffDays(evt.data[M.StartDate.name], evt.data[M.EndDate.name]) > 1) {
\r
351 this.prepareEventGridSpans(evt, this.eventGrid, w, d, days);
\r
352 this.prepareEventGridSpans(evt, this.allDayGrid, w, d, days, true);
\r
354 row = this.findEmptyRowIndex(w, d);
\r
355 this.eventGrid[w][d] = this.eventGrid[w][d] || [];
\r
356 this.eventGrid[w][d][row] = evt;
\r
358 if (evt.data[M.IsAllDay.name]) {
\r
359 row = this.findEmptyRowIndex(w, d, true);
\r
360 this.allDayGrid[w][d] = this.allDayGrid[w][d] || [];
\r
361 this.allDayGrid[w][d][row] = evt;
\r
365 if (this.evtMaxCount[w] < this.eventGrid[w][d].length) {
\r
366 this.evtMaxCount[w] = Math.min(max + 1, this.eventGrid[w][d].length);
\r
374 prepareEventGridSpans: function(evt, grid, w, d, days, allday) {
\r
375 // this event spans multiple days/weeks, so we have to preprocess
\r
376 // the events and store special span events as placeholders so that
\r
377 // the render routine can build the necessary TD spans correctly.
\r
380 row = this.findEmptyRowIndex(w, d, allday),
\r
381 dt = this.viewStart.clone();
\r
388 spanRight: (d == 6)
\r
390 grid[w][d] = grid[w][d] || [];
\r
391 grid[w][d][row] = start;
\r
394 dt = dt.add(Date.DAY, 1);
\r
395 if (dt > this.viewEnd) {
\r
399 // reset counters to the next week
\r
402 row = this.findEmptyRowIndex(w1, 0);
\r
404 grid[w1] = grid[w1] || [];
\r
405 grid[w1][d1] = grid[w1][d1] || [];
\r
407 grid[w1][d1][row] = {
\r
410 isSpanStart: (d1 == 0),
\r
411 spanLeft: (w1 > w) && (d1 % 7 == 0),
\r
412 spanRight: (d1 == 6) && (days > 1)
\r
418 findEmptyRowIndex: function(w, d, allday) {
\r
419 var grid = allday ? this.allDayGrid: this.eventGrid,
\r
420 day = grid[w] ? grid[w][d] || [] : [],
\r
424 for (; i < ln; i++) {
\r
425 if (day[i] == null) {
\r
433 renderTemplate: function() {
\r
435 this.tpl.overwrite(this.el, this.getParams());
\r
436 this.lastRenderStart = this.viewStart.clone();
\r
437 this.lastRenderEnd = this.viewEnd.clone();
\r
441 disableStoreEvents: function() {
\r
442 this.monitorStoreEvents = false;
\r
445 enableStoreEvents: function(refresh) {
\r
446 this.monitorStoreEvents = true;
\r
447 if (refresh === true) {
\r
453 onResize: function() {
\r
458 onInitDrag: function() {
\r
459 this.fireEvent('initdrag', this);
\r
463 onEventDrop: function(rec, dt) {
\r
464 if (Ext.calendar.Date.compare(rec.data[Ext.calendar.EventMappings.StartDate.name], dt) === 0) {
\r
468 var diff = dt.getTime() - rec.data[Ext.calendar.EventMappings.StartDate.name].getTime();
\r
469 rec.set(Ext.calendar.EventMappings.StartDate.name, dt);
\r
470 rec.set(Ext.calendar.EventMappings.EndDate.name, rec.data[Ext.calendar.EventMappings.EndDate.name].add(Date.MILLI, diff));
\r
472 this.fireEvent('eventmove', this, rec);
\r
476 onCalendarEndDrag: function(start, end, onComplete) {
\r
477 // set this flag for other event handlers that might conflict while we're waiting
\r
478 this.dragPending = true;
\r
480 // have to wait for the user to save or cancel before finalizing the dd interation
\r
482 o[Ext.calendar.EventMappings.StartDate.name] = start;
\r
483 o[Ext.calendar.EventMappings.EndDate.name] = end;
\r
485 this.fireEvent('rangeselect', this, o, this.onCalendarEndDragComplete.createDelegate(this, [onComplete]));
\r
489 onCalendarEndDragComplete: function(onComplete) {
\r
490 // callback for the drop zone to clean up
\r
492 // clear flag for other events to resume normally
\r
493 this.dragPending = false;
\r
497 onUpdate: function(ds, rec, operation) {
\r
498 if (this.monitorStoreEvents === false) {
\r
501 if (operation == Ext.data.Record.COMMIT) {
\r
503 if (this.enableFx && this.enableUpdateFx) {
\r
504 this.doUpdateFx(this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]), {
\r
512 doUpdateFx: function(els, o) {
\r
513 this.highlightEvent(els, null, o);
\r
517 onAdd: function(ds, records, index) {
\r
518 if (this.monitorStoreEvents === false) {
\r
521 var rec = records[0];
\r
522 this.tempEventId = rec.id;
\r
525 if (this.enableFx && this.enableAddFx) {
\r
526 this.doAddFx(this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]), {
\r
532 doAddFx: function(els, o) {
\r
533 els.fadeIn(Ext.apply(o, {
\r
539 onRemove: function(ds, rec) {
\r
540 if (this.monitorStoreEvents === false) {
\r
543 if (this.enableFx && this.enableRemoveFx) {
\r
544 this.doRemoveFx(this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]), {
\r
547 callback: this.refresh
\r
551 this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]).remove();
\r
556 doRemoveFx: function(els, o) {
\r
560 <div id="method-Ext.calendar.CalendarView-highlightEvent"></div>/**
\r
561 * Visually highlights an event using {@link Ext.Fx#highlight} config options.
\r
562 * If {@link #highlightEventActions} is false this method will have no effect.
\r
563 * @param {Ext.CompositeElement} els The element(s) to highlight
\r
564 * @param {Object} color (optional) The highlight color. Should be a 6 char hex
\r
565 * color without the leading # (defaults to yellow: 'ffff9c')
\r
566 * @param {Object} o (optional) Object literal with any of the {@link Ext.Fx} config
\r
567 * options. See {@link Ext.Fx#highlight} for usage examples.
\r
569 highlightEvent: function(els, color, o) {
\r
570 if (this.enableFx) {
\r
572 ! (Ext.isIE || Ext.isOpera) ?
\r
573 els.highlight(color, o) :
\r
574 // Fun IE/Opera handling:
\r
575 els.each(function(el) {
\r
576 el.highlight(color, Ext.applyIf({
\r
580 c = el.child('.ext-cal-evm');
\r
582 c.highlight(color, o);
\r
589 <div id="method-Ext.calendar.CalendarView-getEventIdFromEl"></div>/**
\r
590 * Retrieve an Event object's id from its corresponding node in the DOM.
\r
591 * @param {String/Element/HTMLElement} el An {@link Ext.Element}, DOM node or id
\r
593 getEventIdFromEl: function(el) {
\r
595 var id = el.id.split(this.eventElIdDelimiter)[1];
\r
596 if (id.indexOf('-') > -1) {
\r
597 //This id has the index of the week it is rendered in as the suffix.
\r
598 //This allows events that span across weeks to still have reproducibly-unique DOM ids.
\r
599 id = id.split('-')[0];
\r
605 getEventId: function(eventId) {
\r
606 if (eventId === undefined && this.tempEventId) {
\r
607 eventId = this.tempEventId;
\r
612 <div id="method-Ext.calendar.CalendarView-getEventSelectorCls"></div>/**
\r
614 * @param {String} eventId
\r
615 * @param {Boolean} forSelect
\r
616 * @return {String} The selector class
\r
618 getEventSelectorCls: function(eventId, forSelect) {
\r
619 var prefix = forSelect ? '.': '';
\r
620 return prefix + this.id + this.eventElIdDelimiter + this.getEventId(eventId);
\r
623 <div id="method-Ext.calendar.CalendarView-getEventEls"></div>/**
\r
625 * @param {String} eventId
\r
626 * @return {Ext.CompositeElement} The matching CompositeElement of nodes
\r
627 * that comprise the rendered event. Any event that spans across a view
\r
628 * boundary will contain more than one internal Element.
\r
630 getEventEls: function(eventId) {
\r
631 var els = Ext.select(this.getEventSelectorCls(this.getEventId(eventId), true), false, this.el.id);
\r
632 return new Ext.CompositeElement(els);
\r
635 <div id="method-Ext.calendar.CalendarView-isToday"></div>/**
\r
636 * Returns true if the view is currently displaying today's date, else false.
\r
637 * @return {Boolean} True or false
\r
639 isToday: function() {
\r
640 var today = new Date().clearTime().getTime();
\r
641 return this.viewStart.getTime() <= today && this.viewEnd.getTime() >= today;
\r
645 onDataChanged: function(store) {
\r
650 isEventVisible: function(evt) {
\r
651 var start = this.viewStart.getTime(),
\r
652 end = this.viewEnd.getTime(),
\r
653 M = Ext.calendar.EventMappings,
\r
654 evStart = (evt.data ? evt.data[M.StartDate.name] : evt[M.StartDate.name]).getTime(),
\r
655 evEnd = (evt.data ? evt.data[M.EndDate.name] : evt[M.EndDate.name]).add(Date.SECOND, -1).getTime(),
\r
657 startsInRange = (evStart >= start && evStart <= end),
\r
658 endsInRange = (evEnd >= start && evEnd <= end),
\r
659 spansRange = (evStart < start && evEnd > end);
\r
661 return (startsInRange || endsInRange || spansRange);
\r
665 isOverlapping: function(evt1, evt2) {
\r
666 var ev1 = evt1.data ? evt1.data: evt1,
\r
667 ev2 = evt2.data ? evt2.data: evt2,
\r
668 M = Ext.calendar.EventMappings,
\r
669 start1 = ev1[M.StartDate.name].getTime(),
\r
670 end1 = ev1[M.EndDate.name].add(Date.SECOND, -1).getTime(),
\r
671 start2 = ev2[M.StartDate.name].getTime(),
\r
672 end2 = ev2[M.EndDate.name].add(Date.SECOND, -1).getTime();
\r
674 if (end1 < start1) {
\r
677 if (end2 < start2) {
\r
681 var ev1startsInEv2 = (start1 >= start2 && start1 <= end2),
\r
682 ev1EndsInEv2 = (end1 >= start2 && end1 <= end2),
\r
683 ev1SpansEv2 = (start1 < start2 && end1 > end2);
\r
685 return (ev1startsInEv2 || ev1EndsInEv2 || ev1SpansEv2);
\r
688 getDayEl: function(dt) {
\r
689 return Ext.get(this.getDayId(dt));
\r
692 getDayId: function(dt) {
\r
693 if (Ext.isDate(dt)) {
\r
694 dt = dt.format('Ymd');
\r
696 return this.id + this.dayElIdDelimiter + dt;
\r
699 <div id="method-Ext.calendar.CalendarView-getStartDate"></div>/**
\r
700 * Returns the start date of the view, as set by {@link #setStartDate}. Note that this may not
\r
701 * be the first date displayed in the rendered calendar -- to get the start and end dates displayed
\r
702 * to the user use {@link #getViewBounds}.
\r
703 * @return {Date} The start date
\r
705 getStartDate: function() {
\r
706 return this.startDate;
\r
709 <div id="method-Ext.calendar.CalendarView-setStartDate"></div>/**
\r
710 * Sets the start date used to calculate the view boundaries to display. The displayed view will be the
\r
711 * earliest and latest dates that match the view requirements and contain the date passed to this function.
\r
712 * @param {Date} dt The date used to calculate the new view boundaries
\r
714 setStartDate: function(start, refresh) {
\r
715 this.startDate = start.clearTime();
\r
716 this.setViewBounds(start);
\r
719 start: this.viewStart.format('m-d-Y'),
\r
720 end: this.viewEnd.format('m-d-Y')
\r
723 if (refresh === true) {
\r
726 this.fireEvent('datechange', this, this.startDate, this.viewStart, this.viewEnd);
\r
730 setViewBounds: function(startDate) {
\r
731 var start = startDate || this.startDate,
\r
732 offset = start.getDay() - this.startDay;
\r
734 switch (this.weekCount) {
\r
737 this.viewStart = this.dayCount < 7 ? start: start.add(Date.DAY, -offset).clearTime(true);
\r
738 this.viewEnd = this.viewStart.add(Date.DAY, this.dayCount || 7).add(Date.SECOND, -1);
\r
743 start = start.getFirstDateOfMonth();
\r
744 offset = start.getDay() - this.startDay;
\r
748 this.viewStart = start.add(Date.DAY, -offset).clearTime(true);
\r
750 // start from current month start, not view start:
\r
751 var end = start.add(Date.MONTH, 1).add(Date.SECOND, -1);
\r
752 // fill out to the end of the week:
\r
753 this.viewEnd = end.add(Date.DAY, 6 - end.getDay());
\r
757 this.viewStart = start.add(Date.DAY, -offset).clearTime(true);
\r
758 this.viewEnd = this.viewStart.add(Date.DAY, this.weekCount * 7).add(Date.SECOND, -1);
\r
763 getViewBounds: function() {
\r
765 start: this.viewStart,
\r
771 * Sort events for a single day for display in the calendar. This sorts allday
\r
772 * events first, then non-allday events are sorted either based on event start
\r
773 * priority or span priority based on the value of {@link #spansHavePriority}
\r
774 * (defaults to event start priority).
\r
775 * @param {MixedCollection} evts A {@link Ext.util.MixedCollection MixedCollection}
\r
776 * of {@link #Ext.calendar.EventRecord EventRecord} objects
\r
778 sortEventRecordsForDay: function(evts) {
\r
779 if (evts.length < 2) {
\r
783 function(evtA, evtB) {
\r
786 M = Ext.calendar.EventMappings;
\r
788 // Always sort all day events before anything else
\r
789 if (a[M.IsAllDay.name]) {
\r
792 else if (b[M.IsAllDay.name]) {
\r
795 if (this.spansHavePriority) {
\r
796 // This logic always weights span events higher than non-span events
\r
797 // (at the possible expense of start time order). This seems to
\r
798 // be the approach used by Google calendar and can lead to a more
\r
799 // visually appealing layout in complex cases, but event order is
\r
800 // not guaranteed to be consistent.
\r
801 var diff = Ext.calendar.Date.diffDays;
\r
802 if (diff(a[M.StartDate.name], a[M.EndDate.name]) > 0) {
\r
803 if (diff(b[M.StartDate.name], b[M.EndDate.name]) > 0) {
\r
804 // Both events are multi-day
\r
805 if (a[M.StartDate.name].getTime() == b[M.StartDate.name].getTime()) {
\r
806 // If both events start at the same time, sort the one
\r
807 // that ends later (potentially longer span bar) first
\r
808 return b[M.EndDate.name].getTime() - a[M.EndDate.name].getTime();
\r
810 return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();
\r
814 else if (diff(b[M.StartDate.name], b[M.EndDate.name]) > 0) {
\r
817 return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();
\r
820 // Doing this allows span and non-span events to intermingle but
\r
821 // remain sorted sequentially by start time. This seems more proper
\r
822 // but can make for a less visually-compact layout when there are
\r
823 // many such events mixed together closely on the calendar.
\r
824 return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();
\r
826 }.createDelegate(this));
\r
829 <div id="method-Ext.calendar.CalendarView-moveTo"></div>/**
\r
830 * Updates the view to contain the passed date
\r
831 * @param {Date} dt The date to display
\r
833 moveTo: function(dt, noRefresh) {
\r
834 if (Ext.isDate(dt)) {
\r
835 this.setStartDate(dt);
\r
836 if (noRefresh !== false) {
\r
839 return this.startDate;
\r
844 <div id="method-Ext.calendar.CalendarView-moveNext"></div>/**
\r
845 * Updates the view to the next consecutive date(s)
\r
847 moveNext: function(noRefresh) {
\r
848 return this.moveTo(this.viewEnd.add(Date.DAY, 1));
\r
851 <div id="method-Ext.calendar.CalendarView-movePrev"></div>/**
\r
852 * Updates the view to the previous consecutive date(s)
\r
854 movePrev: function(noRefresh) {
\r
855 var days = Ext.calendar.Date.diffDays(this.viewStart, this.viewEnd) + 1;
\r
856 return this.moveDays( - days, noRefresh);
\r
859 <div id="method-Ext.calendar.CalendarView-moveMonths"></div>/**
\r
860 * Shifts the view by the passed number of months relative to the currently set date
\r
861 * @param {Number} value The number of months (positive or negative) by which to shift the view
\r
863 moveMonths: function(value, noRefresh) {
\r
864 return this.moveTo(this.startDate.add(Date.MONTH, value), noRefresh);
\r
867 <div id="method-Ext.calendar.CalendarView-moveWeeks"></div>/**
\r
868 * Shifts the view by the passed number of weeks relative to the currently set date
\r
869 * @param {Number} value The number of weeks (positive or negative) by which to shift the view
\r
871 moveWeeks: function(value, noRefresh) {
\r
872 return this.moveTo(this.startDate.add(Date.DAY, value * 7), noRefresh);
\r
875 <div id="method-Ext.calendar.CalendarView-moveDays"></div>/**
\r
876 * Shifts the view by the passed number of days relative to the currently set date
\r
877 * @param {Number} value The number of days (positive or negative) by which to shift the view
\r
879 moveDays: function(value, noRefresh) {
\r
880 return this.moveTo(this.startDate.add(Date.DAY, value), noRefresh);
\r
883 <div id="method-Ext.calendar.CalendarView-moveToday"></div>/**
\r
884 * Updates the view to show today
\r
886 moveToday: function(noRefresh) {
\r
887 return this.moveTo(new Date(), noRefresh);
\r
890 <div id="method-Ext.calendar.CalendarView-setStore"></div>/**
\r
891 * Sets the event store used by the calendar to display {@link Ext.calendar.EventRecord events}.
\r
892 * @param {Ext.data.Store} store
\r
894 setStore: function(store, initial) {
\r
895 if (!initial && this.store) {
\r
896 this.store.un("datachanged", this.onDataChanged, this);
\r
897 this.store.un("add", this.onAdd, this);
\r
898 this.store.un("remove", this.onRemove, this);
\r
899 this.store.un("update", this.onUpdate, this);
\r
900 this.store.un("clear", this.refresh, this);
\r
903 store.on("datachanged", this.onDataChanged, this);
\r
904 store.on("add", this.onAdd, this);
\r
905 store.on("remove", this.onRemove, this);
\r
906 store.on("update", this.onUpdate, this);
\r
907 store.on("clear", this.refresh, this);
\r
909 this.store = store;
\r
910 if (store && store.getCount() > 0) {
\r
915 getEventRecord: function(id) {
\r
916 var idx = this.store.find(Ext.calendar.EventMappings.EventId.name, id);
\r
917 return this.store.getAt(idx);
\r
920 getEventRecordFromEl: function(el) {
\r
921 return this.getEventRecord(this.getEventIdFromEl(el));
\r
925 getParams: function() {
\r
927 viewStart: this.viewStart,
\r
928 viewEnd: this.viewEnd,
\r
929 startDate: this.startDate,
\r
930 dayCount: this.dayCount,
\r
931 weekCount: this.weekCount,
\r
932 title: this.getTitle()
\r
936 getTitle: function() {
\r
937 return this.startDate.format('F Y');
\r
941 * Shared click handling. Each specific view also provides view-specific
\r
942 * click handling that calls this first. This method returns true if it
\r
943 * can handle the click (and so the subclass should ignore it) else false.
\r
945 onClick: function(e, t) {
\r
946 var el = e.getTarget(this.eventSelector, 5);
\r
948 var id = this.getEventIdFromEl(el);
\r
949 this.fireEvent('eventclick', this, this.getEventRecord(id), el);
\r
955 onMouseOver: function(e, t) {
\r
956 if (this.trackMouseOver !== false && (this.dragZone == undefined || !this.dragZone.dragging)) {
\r
957 if (!this.handleEventMouseEvent(e, t, 'over')) {
\r
958 this.handleDayMouseEvent(e, t, 'over');
\r
964 onMouseOut: function(e, t) {
\r
965 if (this.trackMouseOver !== false && (this.dragZone == undefined || !this.dragZone.dragging)) {
\r
966 if (!this.handleEventMouseEvent(e, t, 'out')) {
\r
967 this.handleDayMouseEvent(e, t, 'out');
\r
973 handleEventMouseEvent: function(e, t, type) {
\r
974 var el = e.getTarget(this.eventSelector, 5, true),
\r
979 rel = Ext.get(e.getRelatedTarget());
\r
980 if (el == rel || el.contains(rel)) {
\r
984 evtId = this.getEventIdFromEl(el);
\r
986 if (this.eventOverClass != '') {
\r
987 els = this.getEventEls(evtId);
\r
988 els[type == 'over' ? 'addClass': 'removeClass'](this.eventOverClass);
\r
990 this.fireEvent('event' + type, this, this.getEventRecord(evtId), el);
\r
997 getDateFromId: function(id, delim) {
\r
998 var parts = id.split(delim);
\r
999 return parts[parts.length - 1];
\r
1003 handleDayMouseEvent: function(e, t, type) {
\r
1004 t = e.getTarget('td', 3);
\r
1006 if (t.id && t.id.indexOf(this.dayElIdDelimiter) > -1) {
\r
1007 var dt = this.getDateFromId(t.id, this.dayElIdDelimiter),
\r
1008 rel = Ext.get(e.getRelatedTarget()),
\r
1013 relTD = rel.is('td') ? rel: rel.up('td', 3);
\r
1014 relDate = relTD && relTD.id ? this.getDateFromId(relTD.id, this.dayElIdDelimiter) : '';
\r
1016 if (!rel || dt != relDate) {
\r
1017 var el = this.getDayEl(dt);
\r
1018 if (el && this.dayOverClass != '') {
\r
1019 el[type == 'over' ? 'addClass': 'removeClass'](this.dayOverClass);
\r
1021 this.fireEvent('day' + type, this, Date.parseDate(dt, "Ymd"), el);
\r
1028 renderItems: function() {
\r
1029 throw 'This method must be implemented by a subclass';
\r