3 * Copyright(c) 2006-2010 Sencha Inc.
5 * http://www.sencha.com/license
7 Ext.ns('Ext.calendar');
10 Ext.apply(Ext.calendar, {
12 diffDays: function(start, end) {
13 day = 1000 * 60 * 60 * 24;
14 diff = end.clearTime(true).getTime() - start.clearTime(true).getTime();
15 return Math.ceil(diff / day);
18 copyTime: function(fromDt, toDt) {
19 var dt = toDt.clone();
24 fromDt.getMilliseconds());
29 compare: function(dt1, dt2, precise) {
30 if (precise !== true) {
32 dt1.setMilliseconds(0);
34 dt2.setMilliseconds(0);
36 return dt2.getTime() - dt1.getTime();
40 maxOrMin: function(max) {
41 var dt = (max ? 0: Number.MAX_VALUE),
46 dt = Math[max ? 'max': 'min'](dt, args[i].getTime());
52 return this.maxOrMin.apply(this, [true, arguments]);
56 return this.maxOrMin.apply(this, [false, arguments]);
61 * @class Ext.calendar.DayHeaderTemplate
62 * @extends Ext.XTemplate
63 * <p>This is the template used to render the all-day event container used in {@link Ext.calendar.DayView DayView} and
64 * {@link Ext.calendar.WeekView WeekView}. Internally the majority of the layout logic is deferred to an instance of
65 * {@link Ext.calendar.BoxLayoutTemplate}.</p>
66 * <p>This template is automatically bound to the underlying event store by the
67 * calendar components and expects records of type {@link Ext.calendar.EventRecord}.</p>
68 * <p>Note that this template would not normally be used directly. Instead you would use the {@link Ext.calendar.DayViewTemplate}
69 * that internally creates an instance of this template along with a {@link Ext.calendar.DayBodyTemplate}.</p>
71 * @param {Object} config The config object
73 Ext.calendar.DayHeaderTemplate = function(config){
75 Ext.apply(this, config);
77 this.allDayTpl = new Ext.calendar.BoxLayoutTemplate(config);
78 this.allDayTpl.compile();
80 Ext.calendar.DayHeaderTemplate.superclass.constructor.call(this,
81 '<div class="ext-cal-hd-ct">',
82 '<table class="ext-cal-hd-days-tbl" cellspacing="0" cellpadding="0">',
85 '<td class="ext-cal-gutter"></td>',
86 '<td class="ext-cal-hd-days-td"><div class="ext-cal-hd-ad-inner">{allDayTpl}</div></td>',
87 '<td class="ext-cal-gutter-rt"></td>',
95 Ext.extend(Ext.calendar.DayHeaderTemplate, Ext.XTemplate, {
96 applyTemplate : function(o){
97 return Ext.calendar.DayHeaderTemplate.superclass.applyTemplate.call(this, {
98 allDayTpl: this.allDayTpl.apply(o)
103 Ext.calendar.DayHeaderTemplate.prototype.apply = Ext.calendar.DayHeaderTemplate.prototype.applyTemplate;
105 * @class Ext.calendar.DayBodyTemplate
106 * @extends Ext.XTemplate
107 * <p>This is the template used to render the scrolling body container used in {@link Ext.calendar.DayView DayView} and
108 * {@link Ext.calendar.WeekView WeekView}. This template is automatically bound to the underlying event store by the
109 * calendar components and expects records of type {@link Ext.calendar.EventRecord}.</p>
110 * <p>Note that this template would not normally be used directly. Instead you would use the {@link Ext.calendar.DayViewTemplate}
111 * that internally creates an instance of this template along with a {@link Ext.calendar.DayHeaderTemplate}.</p>
113 * @param {Object} config The config object
115 Ext.calendar.DayBodyTemplate = function(config){
117 Ext.apply(this, config);
119 Ext.calendar.DayBodyTemplate.superclass.constructor.call(this,
120 '<table class="ext-cal-bg-tbl" cellspacing="0" cellpadding="0">',
123 '<td class="ext-cal-gutter"></td>',
124 '<td colspan="{dayCount}">',
125 '<div class="ext-cal-bg-rows">',
126 '<div class="ext-cal-bg-rows-inner">',
128 '<div class="ext-cal-bg-row">',
129 '<div class="ext-cal-bg-row-div ext-row-{[xindex]}"></div>',
137 '<td class="ext-cal-day-times">',
139 '<div class="ext-cal-bg-row">',
140 '<div class="ext-cal-day-time-inner">{.}</div>',
145 '<td class="ext-cal-day-col">',
146 '<div class="ext-cal-day-col-inner">',
147 '<div id="{[this.id]}-day-col-{.:date("Ymd")}" class="ext-cal-day-col-gutter"></div>',
157 Ext.extend(Ext.calendar.DayBodyTemplate, Ext.XTemplate, {
159 applyTemplate : function(o){
160 this.today = new Date().clearTime();
161 this.dayCount = this.dayCount || 1;
163 var i = 0, days = [],
164 dt = o.viewStart.clone(),
167 for(; i<this.dayCount; i++){
168 days[i] = dt.add(Date.DAY, i);
172 dt = new Date().clearTime();
174 times.push(dt.format('ga'));
175 dt = dt.add(Date.HOUR, 1);
178 return Ext.calendar.DayBodyTemplate.superclass.applyTemplate.call(this, {
180 dayCount: days.length,
186 Ext.calendar.DayBodyTemplate.prototype.apply = Ext.calendar.DayBodyTemplate.prototype.applyTemplate;
188 * @class Ext.calendar.DayViewTemplate
189 * @extends Ext.XTemplate
190 * <p>This is the template used to render the all-day event container used in {@link Ext.calendar.DayView DayView} and
191 * {@link Ext.calendar.WeekView WeekView}. Internally this class simply defers to instances of {@link Ext.calerndar.DayHeaderTemplate}
192 * and {@link Ext.calerndar.DayBodyTemplate} to perform the actual rendering logic, but it also provides the overall calendar view
193 * container that contains them both. As such this is the template that should be used when rendering day or week views.</p>
194 * <p>This template is automatically bound to the underlying event store by the
195 * calendar components and expects records of type {@link Ext.calendar.EventRecord}.</p>
197 * @param {Object} config The config object
199 Ext.calendar.DayViewTemplate = function(config){
201 Ext.apply(this, config);
203 this.headerTpl = new Ext.calendar.DayHeaderTemplate(config);
204 this.headerTpl.compile();
206 this.bodyTpl = new Ext.calendar.DayBodyTemplate(config);
207 this.bodyTpl.compile();
209 Ext.calendar.DayViewTemplate.superclass.constructor.call(this,
210 '<div class="ext-cal-inner-ct">',
217 Ext.extend(Ext.calendar.DayViewTemplate, Ext.XTemplate, {
219 applyTemplate : function(o){
220 return Ext.calendar.DayViewTemplate.superclass.applyTemplate.call(this, {
221 headerTpl: this.headerTpl.apply(o),
222 bodyTpl: this.bodyTpl.apply(o)
227 Ext.calendar.DayViewTemplate.prototype.apply = Ext.calendar.DayViewTemplate.prototype.applyTemplate;
229 * @class Ext.calendar.BoxLayoutTemplate
230 * @extends Ext.XTemplate
231 * <p>This is the template used to render calendar views based on small day boxes within a non-scrolling container (currently
232 * the {@link Ext.calendar.MonthView MonthView} and the all-day headers for {@link Ext.calendar.DayView DayView} and
233 * {@link Ext.calendar.WeekView WeekView}. This template is automatically bound to the underlying event store by the
234 * calendar components and expects records of type {@link Ext.calendar.EventRecord}.</p>
236 * @param {Object} config The config object
238 Ext.calendar.BoxLayoutTemplate = function(config){
240 Ext.apply(this, config);
242 var weekLinkTpl = this.showWeekLinks ? '<div id="{weekLinkId}" class="ext-cal-week-link">{weekNum}</div>' : '';
244 Ext.calendar.BoxLayoutTemplate.superclass.constructor.call(this,
246 '<div id="{[this.id]}-wk-{[xindex-1]}" class="ext-cal-wk-ct" style="top:{[this.getRowTop(xindex, xcount)]}%; height:{[this.getRowHeight(xcount)]}%;">',
248 '<table class="ext-cal-bg-tbl" cellpadding="0" cellspacing="0">',
252 '<td id="{[this.id]}-day-{date:date("Ymd")}" class="{cellCls}"> </td>',
257 '<table class="ext-cal-evt-tbl" cellpadding="0" cellspacing="0">',
261 '<td id="{[this.id]}-ev-day-{date:date("Ymd")}" class="{titleCls}"><div>{title}</div></td>',
268 getRowTop: function(i, ln){
269 return ((i-1)*(100/ln));
271 getRowHeight: function(ln){
278 Ext.extend(Ext.calendar.BoxLayoutTemplate, Ext.XTemplate, {
280 applyTemplate : function(o){
284 var w = 0, title = '', first = true, isToday = false, showMonth = false, prevMonth = false, nextMonth = false,
286 today = new Date().clearTime(),
287 dt = this.viewStart.clone(),
288 thisMonth = this.startDate.getMonth();
290 for(; w < this.weekCount || this.weekCount == -1; w++){
291 if(dt > this.viewEnd){
296 for(var d = 0; d < this.dayCount; d++){
297 isToday = dt.getTime() === today.getTime();
298 showMonth = first || (dt.getDate() == 1);
299 prevMonth = (dt.getMonth() < thisMonth) && this.weekCount == -1;
300 nextMonth = (dt.getMonth() > thisMonth) && this.weekCount == -1;
302 if(dt.getDay() == 1){
303 // The ISO week format 'W' is relative to a Monday week start. If we
304 // make this check on Sunday the week number will be off.
305 weeks[w].weekNum = this.showWeekNumbers ? dt.format('W') : ' ';
306 weeks[w].weekLinkId = 'ext-cal-week-'+dt.format('Ymd');
311 title = this.getTodayText();
314 title = dt.format(this.dayCount == 1 ? 'l, F j, Y' : (first ? 'M j, Y' : 'M j'));
318 var dayFmt = (w == 0 && this.showHeader !== true) ? 'D j' : 'j';
319 title = isToday ? this.getTodayText() : dt.format(dayFmt);
325 titleCls: 'ext-cal-dtitle ' + (isToday ? ' ext-cal-dtitle-today' : '') +
326 (w==0 ? ' ext-cal-dtitle-first' : '') +
327 (prevMonth ? ' ext-cal-dtitle-prev' : '') +
328 (nextMonth ? ' ext-cal-dtitle-next' : ''),
329 cellCls: 'ext-cal-day ' + (isToday ? ' ext-cal-day-today' : '') +
330 (d==0 ? ' ext-cal-day-first' : '') +
331 (prevMonth ? ' ext-cal-day-prev' : '') +
332 (nextMonth ? ' ext-cal-day-next' : '')
334 dt = dt.add(Date.DAY, 1);
339 return Ext.calendar.BoxLayoutTemplate.superclass.applyTemplate.call(this, {
345 getTodayText : function(){
346 var dt = new Date().format('l, F j, Y'),
347 todayText = this.showTodayText !== false ? this.todayText : '',
348 timeText = this.showTime !== false ? ' <span id="'+this.id+'-clock" class="ext-cal-dtitle-time">' +
349 new Date().format('g:i a') + '</span>' : '',
350 separator = todayText.length > 0 || timeText.length > 0 ? ' — ' : '';
352 if(this.dayCount == 1){
353 return dt + separator + todayText + timeText;
355 fmt = this.weekCount == 1 ? 'D j' : 'j';
356 return todayText.length > 0 ? todayText + timeText : new Date().format(fmt) + timeText;
360 Ext.calendar.BoxLayoutTemplate.prototype.apply = Ext.calendar.BoxLayoutTemplate.prototype.applyTemplate;
362 * @class Ext.calendar.MonthViewTemplate
\r
363 * @extends Ext.XTemplate
\r
364 * <p>This is the template used to render the {@link Ext.calendar.MonthView MonthView}. Internally this class defers to an
\r
365 * instance of {@link Ext.calerndar.BoxLayoutTemplate} to handle the inner layout rendering and adds containing elements around
\r
366 * that to form the month view.</p>
\r
367 * <p>This template is automatically bound to the underlying event store by the
\r
368 * calendar components and expects records of type {@link Ext.calendar.EventRecord}.</p>
\r
370 * @param {Object} config The config object
\r
372 Ext.calendar.MonthViewTemplate = function(config){
\r
374 Ext.apply(this, config);
\r
376 this.weekTpl = new Ext.calendar.BoxLayoutTemplate(config);
\r
377 this.weekTpl.compile();
\r
379 var weekLinkTpl = this.showWeekLinks ? '<div class="ext-cal-week-link-hd"> </div>' : '';
\r
381 Ext.calendar.MonthViewTemplate.superclass.constructor.call(this,
\r
382 '<div class="ext-cal-inner-ct {extraClasses}">',
\r
383 '<div class="ext-cal-hd-ct ext-cal-month-hd">',
\r
385 '<table class="ext-cal-hd-days-tbl" cellpadding="0" cellspacing="0">',
\r
388 '<tpl for="days">',
\r
389 '<th class="ext-cal-hd-day{[xindex==1 ? " ext-cal-day-first" : ""]}" title="{.:date("l, F j, Y")}">{.:date("D")}</th>',
\r
395 '<div class="ext-cal-body-ct">{weeks}</div>',
\r
400 Ext.extend(Ext.calendar.MonthViewTemplate, Ext.XTemplate, {
\r
402 applyTemplate : function(o){
\r
404 weeks = this.weekTpl.apply(o),
\r
407 for(var i = 0; i < 7; i++){
\r
408 days.push(dt.add(Date.DAY, i));
\r
411 var extraClasses = this.showHeader === true ? '' : 'ext-cal-noheader';
\r
412 if(this.showWeekLinks){
\r
413 extraClasses += ' ext-cal-week-links';
\r
416 return Ext.calendar.MonthViewTemplate.superclass.applyTemplate.call(this, {
\r
419 extraClasses: extraClasses
\r
424 Ext.calendar.MonthViewTemplate.prototype.apply = Ext.calendar.MonthViewTemplate.prototype.applyTemplate;
\r
426 * @class Ext.dd.ScrollManager
427 * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>
428 * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,
429 * but you can also override most of the configs per scroll container by adding a
430 * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},
431 * {@link #vthresh}, {@link #increment} and {@link #frequency}. Example usage:
433 var el = Ext.get('scroll-ct');
434 el.ddScrollConfig = {
440 Ext.dd.ScrollManager.register(el);
442 * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
445 Ext.dd.ScrollManager = function() {
446 var ddm = Ext.dd.DragDropMgr,
450 onStop = function(e) {
454 triggerRefresh = function() {
455 if (ddm.dragCurrent) {
456 ddm.refreshCache(ddm.dragCurrent.groups);
459 doScroll = function() {
460 if (ddm.dragCurrent) {
461 var dds = Ext.dd.ScrollManager,
462 inc = proc.el.ddScrollConfig ? proc.el.ddScrollConfig.increment: dds.increment;
464 if (proc.el.scroll(proc.dir, inc)) {
468 proc.el.scroll(proc.dir, inc, true, dds.animDuration, triggerRefresh);
472 clearProc = function() {
474 clearInterval(proc.id);
480 startProc = function(el, dir) {
484 var freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) ?
485 el.ddScrollConfig.frequency: Ext.dd.ScrollManager.frequency,
486 group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup: undefined;
488 if (group === undefined || ddm.dragCurrent.ddGroup == group) {
489 proc.id = setInterval(doScroll, freq);
492 onFire = function(e, isDrop) {
493 if (isDrop || !ddm.dragCurrent) {
496 var dds = Ext.dd.ScrollManager;
497 if (!dragEl || dragEl != ddm.dragCurrent) {
498 dragEl = ddm.dragCurrent;
499 // refresh regions on drag start
503 var xy = Ext.lib.Event.getXY(e),
504 pt = new Ext.lib.Point(xy[0], xy[1]),
510 if (els.hasOwnProperty(id)) {
513 c = el.ddScrollConfig ? el.ddScrollConfig: dds;
514 if (r && r.contains(pt) && el.isScrollable()) {
515 if (r.bottom - pt.y <= c.vthresh) {
517 startProc(el, "down");
520 } else if (r.right - pt.x <= c.hthresh) {
522 startProc(el, "left");
525 } else if (pt.y - r.top <= c.vthresh) {
530 } else if (pt.x - r.left <= c.hthresh) {
532 startProc(el, "right");
542 ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
543 ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
547 * Registers new overflow element(s) to auto scroll
548 * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either
550 register: function(el) {
551 if (Ext.isArray(el)) {
554 for (; i < len; i++) {
555 this.register(el[i]);
564 * Unregisters overflow element(s) so they are no longer scrolled
565 * @param {Mixed/Array} el The id of or the element to be removed or an array of either
567 unregister: function(el) {
568 if (Ext.isArray(el)) {
571 for (; i < len; i++) {
572 this.unregister(el[i]);
581 * The number of pixels from the top or bottom edge of a container the pointer needs to be to
582 * trigger scrolling (defaults to 25)
587 * The number of pixels from the right or left edge of a container the pointer needs to be to
588 * trigger scrolling (defaults to 25)
594 * The number of pixels to scroll in each scroll increment (defaults to 50)
600 * The frequency of scrolls in milliseconds (defaults to 500)
606 * True to animate the scroll (defaults to true)
612 * The animation duration in seconds -
613 * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)
619 * Manually trigger a cache refresh.
621 refreshCache: function() {
624 if (els.hasOwnProperty(id)) {
625 if (typeof els[id] == 'object') {
626 // for people extending the object prototype
627 els[id]._region = els[id].getRegion();
634 * @class Ext.calendar.StatusProxy
635 * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair. It also
636 * contains a calendar-specific drag status message containing details about the dragged event's target drop date range.
637 * This is the default drag proxy used by all calendar views.
639 * @param {Object} config
641 Ext.calendar.StatusProxy = function(config) {
642 Ext.apply(this, config);
643 this.id = this.id || Ext.id();
644 this.el = new Ext.Layer({
647 cls: 'ext-dd-drag-proxy x-dd-drag-proxy ' + this.dropNotAllowed,
650 cls: 'x-dd-drop-icon'
653 cls: 'ext-dd-ghost-ct',
656 cls: 'x-dd-drag-ghost'
665 shadow: !config || config.shadow !== false
667 this.ghost = Ext.get(this.el.dom.childNodes[1].childNodes[0]);
668 this.message = Ext.get(this.el.dom.childNodes[1].childNodes[1]);
669 this.dropStatus = this.dropNotAllowed;
672 Ext.extend(Ext.calendar.StatusProxy, Ext.dd.StatusProxy, {
674 * @cfg {String} moveEventCls
675 * The CSS class to apply to the status element when an event is being dragged (defaults to 'ext-cal-dd-move').
677 moveEventCls: 'ext-cal-dd-move',
679 * @cfg {String} addEventCls
680 * The CSS class to apply to the status element when drop is not allowed (defaults to 'ext-cal-dd-add').
682 addEventCls: 'ext-cal-dd-add',
685 update: function(html) {
686 if (typeof html == 'string') {
687 this.ghost.update(html);
689 this.ghost.update('');
690 html.style.margin = '0';
691 this.ghost.dom.appendChild(html);
693 var el = this.ghost.dom.firstChild;
695 Ext.fly(el).setStyle('float', 'none').setHeight('auto');
696 Ext.getDom(el).id += '-ddproxy';
701 * Update the calendar-specific drag status message without altering the ghost element.
702 * @param {String} msg The new status message
704 updateMsg: function(msg) {
705 this.message.update(msg);
708 * Internal drag zone implementation for the calendar components. This provides base functionality
709 * and is primarily for the month view -- DayViewDD adds day/week view-specific functionality.
711 Ext.calendar.DragZone = Ext.extend(Ext.dd.DragZone, {
712 ddGroup: 'CalendarDD',
713 eventSelector: '.ext-cal-evt',
715 constructor: function(el, config) {
716 if (!Ext.calendar._statusProxyInstance) {
717 Ext.calendar._statusProxyInstance = new Ext.calendar.StatusProxy();
719 this.proxy = Ext.calendar._statusProxyInstance;
720 Ext.calendar.DragZone.superclass.constructor.call(this, el, config);
723 getDragData: function(e) {
724 // Check whether we are dragging on an event first
725 var t = e.getTarget(this.eventSelector, 3);
727 var rec = this.view.getEventRecordFromEl(t);
731 eventStart: rec.data[Ext.calendar.EventMappings.StartDate.name],
732 eventEnd: rec.data[Ext.calendar.EventMappings.EndDate.name],
737 // If not dragging an event then we are dragging on
738 // the calendar to add a new event
739 t = this.view.getDayAt(e.getPageX(), e.getPageY());
750 onInitDrag: function(x, y) {
751 if (this.dragData.ddel) {
752 var ghost = this.dragData.ddel.cloneNode(true),
753 child = Ext.fly(ghost).child('dl');
755 Ext.fly(ghost).setWidth('auto');
759 child.setHeight('auto');
761 this.proxy.update(ghost);
762 this.onStartDrag(x, y);
764 else if (this.dragData.start) {
765 this.onStartDrag(x, y);
767 this.view.onInitDrag();
771 afterRepair: function() {
772 if (Ext.enableFx && this.dragData.ddel) {
773 Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || 'c3daf9');
775 this.dragging = false;
778 getRepairXY: function(e) {
779 if (this.dragData.ddel) {
780 return Ext.Element.fly(this.dragData.ddel).getXY();
784 afterInvalidDrop: function(e, id) {
785 Ext.select('.ext-dd-shim').hide();
790 * Internal drop zone implementation for the calendar components. This provides base functionality
791 * and is primarily for the month view -- DayViewDD adds day/week view-specific functionality.
793 Ext.calendar.DropZone = Ext.extend(Ext.dd.DropZone, {
794 ddGroup: 'CalendarDD',
795 eventSelector: '.ext-cal-evt',
800 getTargetFromEvent: function(e) {
801 var dragOffset = this.dragOffset || 0,
802 y = e.getPageY() - dragOffset,
803 d = this.view.getDayAt(e.getPageX(), y);
805 return d.el ? d: null;
808 onNodeOver: function(n, dd, e, data) {
809 var D = Ext.calendar.Date,
810 start = data.type == 'eventdrag' ? n.date: D.min(data.start, n.date),
811 end = data.type == 'eventdrag' ? n.date.add(Date.DAY, D.diffDays(data.eventStart, data.eventEnd)) :
812 D.max(data.start, n.date);
814 if (!this.dragStartDate || !this.dragEndDate || (D.diffDays(start, this.dragStartDate) != 0) || (D.diffDays(end, this.dragEndDate) != 0)) {
815 this.dragStartDate = start;
816 this.dragEndDate = end.clearTime().add(Date.DAY, 1).add(Date.MILLI, -1);
817 this.shim(start, end);
819 var range = start.format('n/j');
820 if (D.diffDays(start, end) > 0) {
821 range += '-' + end.format('n/j');
823 var msg = String.format(data.type == 'eventdrag' ? this.moveText: this.createText, range);
824 data.proxy.updateMsg(msg);
826 return this.dropAllowed;
829 shim: function(start, end) {
831 var dt = start.clone(),
835 cnt = Ext.calendar.Date.diffDays(dt, end) + 1;
840 shim.isActive = false;
846 var dayEl = this.view.getDayEl(dt);
848 // if the date is not in the current view ignore it (this
849 // can happen when an event is dragged to the end of the
850 // month so that it ends outside the view)
852 var wk = this.view.getWeekIndex(dt);
853 shim = this.shims[wk];
856 shim = this.createShim();
857 this.shims[wk] = shim;
859 if (wk != this.currWeek) {
860 shim.boxInfo = dayEl.getBox();
864 box = dayEl.getBox();
865 shim.boxInfo.right = box.right;
866 shim.boxInfo.width = box.right - shim.boxInfo.x;
868 shim.isActive = true;
870 dt = dt.add(Date.DAY, 1);
878 shim.setBox(shim.boxInfo);
880 else if (shim.isVisible()) {
887 createShim: function() {
889 this.shimCt = Ext.get('ext-dd-shim-ct');
891 this.shimCt = document.createElement('div');
892 this.shimCt.id = 'ext-dd-shim-ct';
893 Ext.getBody().appendChild(this.shimCt);
896 var el = document.createElement('div');
897 el.className = 'ext-dd-shim';
898 this.shimCt.appendChild(el);
900 return new Ext.Layer({
908 clearShims: function() {
917 onContainerOver: function(dd, e, data) {
918 return this.dropAllowed;
921 onCalendarDragComplete: function() {
922 delete this.dragStartDate;
923 delete this.dragEndDate;
927 onNodeDrop: function(n, dd, e, data) {
929 if (data.type == 'eventdrag') {
930 var rec = this.view.getEventRecordFromEl(data.ddel),
931 dt = Ext.calendar.Date.copyTime(rec.data[Ext.calendar.EventMappings.StartDate.name], n.date);
933 this.view.onEventDrop(rec, dt);
934 this.onCalendarDragComplete();
937 if (data.type == 'caldrag') {
938 this.view.onCalendarEndDrag(this.dragStartDate, this.dragEndDate,
939 this.onCalendarDragComplete.createDelegate(this));
940 //shims are NOT cleared here -- they stay visible until the handling
941 //code calls the onCalendarDragComplete callback which hides them.
945 this.onCalendarDragComplete();
949 onContainerDrop: function(dd, e, data) {
950 this.onCalendarDragComplete();
954 destroy: function() {
955 Ext.calendar.DropZone.superclass.destroy.call(this);
956 Ext.destroy(this.shimCt);
961 * Internal drag zone implementation for the calendar day and week views.
963 Ext.calendar.DayViewDragZone = Ext.extend(Ext.calendar.DragZone, {
964 ddGroup: 'DayViewDD',
965 resizeSelector: '.ext-evt-rsz',
967 getDragData: function(e) {
968 var t = e.getTarget(this.resizeSelector, 2, true),
972 p = t.parent(this.eventSelector);
973 rec = this.view.getEventRecordFromEl(p);
978 eventStart: rec.data[Ext.calendar.EventMappings.StartDate.name],
979 eventEnd: rec.data[Ext.calendar.EventMappings.EndDate.name],
983 t = e.getTarget(this.eventSelector, 3);
985 rec = this.view.getEventRecordFromEl(t);
989 eventStart: rec.data[Ext.calendar.EventMappings.StartDate.name],
990 eventEnd: rec.data[Ext.calendar.EventMappings.EndDate.name],
995 // If not dragging/resizing an event then we are dragging on
996 // the calendar to add a new event
997 t = this.view.getDayAt(e.getPageX(), e.getPageY());
1010 * Internal drop zone implementation for the calendar day and week views.
1012 Ext.calendar.DayViewDropZone = Ext.extend(Ext.calendar.DropZone, {
1013 ddGroup: 'DayViewDD',
1015 onNodeOver: function(n, dd, e, data) {
1019 text = this.createText,
1025 if (data.type == 'caldrag') {
1026 if (!this.dragStartMarker) {
1027 // Since the container can scroll, this gets a little tricky.
1028 // There is no el in the DOM that we can measure by default since
1029 // the box is simply calculated from the original drag start (as opposed
1030 // to dragging or resizing the event where the orig event box is present).
1031 // To work around this we add a placeholder el into the DOM and give it
1032 // the original starting time's box so that we can grab its updated
1033 // box measurements as the underlying container scrolls up or down.
1034 // This placeholder is removed in onNodeDrop.
1035 this.dragStartMarker = n.el.parent().createChild({
1036 style: 'position:absolute;'
1038 this.dragStartMarker.setBox(n.timeBox);
1039 this.dragCreateDt = n.date;
1041 box = this.dragStartMarker.getBox();
1042 box.height = Math.ceil(Math.abs(e.xy[1] - box.y) / n.timeBox.height) * n.timeBox.height;
1044 if (e.xy[1] < box.y) {
1045 box.height += n.timeBox.height;
1046 box.y = box.y - box.height + n.timeBox.height;
1047 endDt = this.dragCreateDt.add(Date.MINUTE, 30);
1050 n.date = n.date.add(Date.MINUTE, 30);
1052 this.shim(this.dragCreateDt, box);
1054 curr = Ext.calendar.Date.copyTime(n.date, this.dragCreateDt);
1055 this.dragStartDate = Ext.calendar.Date.min(this.dragCreateDt, curr);
1056 this.dragEndDate = endDt || Ext.calendar.Date.max(this.dragCreateDt, curr);
1058 dt = this.dragStartDate.format('g:ia-') + this.dragEndDate.format('g:ia');
1061 evtEl = Ext.get(data.ddel);
1062 dayCol = evtEl.parent().parent();
1063 box = evtEl.getBox();
1065 box.width = dayCol.getWidth();
1067 if (data.type == 'eventdrag') {
1068 if (this.dragOffset === undefined) {
1069 this.dragOffset = n.timeBox.y - box.y;
1070 box.y = n.timeBox.y - this.dragOffset;
1073 box.y = n.timeBox.y;
1075 dt = n.date.format('n/j g:ia');
1076 box.x = n.el.getLeft();
1078 this.shim(n.date, box);
1079 text = this.moveText;
1081 if (data.type == 'eventresize') {
1082 if (!this.resizeDt) {
1083 this.resizeDt = n.date;
1085 box.x = dayCol.getLeft();
1086 box.height = Math.ceil(Math.abs(e.xy[1] - box.y) / n.timeBox.height) * n.timeBox.height;
1087 if (e.xy[1] < box.y) {
1088 box.y -= box.height;
1091 n.date = n.date.add(Date.MINUTE, 30);
1093 this.shim(this.resizeDt, box);
1095 curr = Ext.calendar.Date.copyTime(n.date, this.resizeDt);
1096 start = Ext.calendar.Date.min(data.eventStart, curr);
1097 end = Ext.calendar.Date.max(data.eventStart, curr);
1099 data.resizeDates = {
1103 dt = start.format('g:ia-') + end.format('g:ia');
1104 text = this.resizeText;
1108 data.proxy.updateMsg(String.format(text, dt));
1109 return this.dropAllowed;
1112 shim: function(dt, box) {
1113 Ext.each(this.shims,
1116 shim.isActive = false;
1121 var shim = this.shims[0];
1123 shim = this.createShim();
1124 this.shims[0] = shim;
1127 shim.isActive = true;
1132 onNodeDrop: function(n, dd, e, data) {
1135 if (data.type == 'eventdrag') {
1136 rec = this.view.getEventRecordFromEl(data.ddel);
1137 this.view.onEventDrop(rec, n.date);
1138 this.onCalendarDragComplete();
1139 delete this.dragOffset;
1142 if (data.type == 'eventresize') {
1143 rec = this.view.getEventRecordFromEl(data.ddel);
1144 this.view.onEventResize(rec, data.resizeDates);
1145 this.onCalendarDragComplete();
1146 delete this.resizeDt;
1149 if (data.type == 'caldrag') {
1150 Ext.destroy(this.dragStartMarker);
1151 delete this.dragStartMarker;
1152 delete this.dragCreateDt;
1153 this.view.onCalendarEndDrag(this.dragStartDate, this.dragEndDate,
1154 this.onCalendarDragComplete.createDelegate(this));
1155 //shims are NOT cleared here -- they stay visible until the handling
1156 //code calls the onCalendarDragComplete callback which hides them.
1160 this.onCalendarDragComplete();
1165 * @class Ext.calendar.EventMappings
1167 * A simple object that provides the field definitions for EventRecords so that they can be easily overridden.
1169 Ext.calendar.EventMappings = {
1230 * @class Ext.calendar.EventRecord
1231 * @extends Ext.data.Record
1232 * <p>This is the {@link Ext.data.Record Record} specification for calendar event data used by the
1233 * {@link Ext.calendar.CalendarPanel CalendarPanel}'s underlying store. It can be overridden as
1234 * necessary to customize the fields supported by events, although the existing column names should
1235 * not be altered. If your model fields are named differently you should update the <b>mapping</b>
1236 * configs accordingly.</p>
1237 * <p>The only required fields when creating a new event record instance are StartDate and
1238 * EndDate. All other fields are either optional are will be defaulted if blank.</p>
1239 * <p>Here is a basic example for how to create a new record of this type:<pre><code>
1240 rec = new Ext.calendar.EventRecord({
1241 StartDate: '2101-01-12 12:00:00',
1242 EndDate: '2101-01-12 13:30:00',
1243 Title: 'My cool event',
1247 * If you have overridden any of the record's data mappings via the Ext.calendar.EventMappings object
1248 * you may need to set the values using this alternate syntax to ensure that the fields match up correctly:<pre><code>
1249 var M = Ext.calendar.EventMappings;
1251 rec = new Ext.calendar.EventRecord();
1252 rec.data[M.StartDate.name] = '2101-01-12 12:00:00';
1253 rec.data[M.EndDate.name] = '2101-01-12 13:30:00';
1254 rec.data[M.Title.name] = 'My cool event';
1255 rec.data[M.Notes.name] = 'Some notes';
1258 * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
1259 * fields. If not specified the {@link Ext.data.Field#defaultValue defaultValue}
1260 * for each field will be assigned.
1261 * @param {Object} id (Optional) The id of the Record. The id is used by the
1262 * {@link Ext.data.Store} object which owns the Record to index its collection
1263 * of Records (therefore this id should be unique within each store). If an
1264 * id is not specified a {@link #phantom}
1265 * Record will be created with an {@link #Record.id automatically generated id}.
1268 var M = Ext.calendar.EventMappings;
1270 Ext.calendar.EventRecord = Ext.data.Record.create([
1285 * Reconfigures the default record definition based on the current Ext.calendar.EventMappings object
1287 Ext.calendar.EventRecord.reconfigure = function() {
1288 Ext.calendar.EventRecord = Ext.data.Record.create([
1304 * This is the view used internally by the panel that displays overflow events in the
\r
1305 * month view. Anytime a day cell cannot display all of its events, it automatically displays
\r
1306 * a link at the bottom to view all events for that day. When clicked, a panel pops up that
\r
1307 * uses this view to display the events for that day.
\r
1309 Ext.calendar.MonthDayDetailView = Ext.extend(Ext.BoxComponent, {
\r
1310 initComponent: function() {
\r
1311 Ext.calendar.CalendarView.superclass.initComponent.call(this);
\r
1314 eventsrendered: true
\r
1318 this.el = document.createElement('div');
\r
1322 afterRender: function() {
\r
1323 this.tpl = this.getTemplate();
\r
1325 Ext.calendar.MonthDayDetailView.superclass.afterRender.call(this);
\r
1328 'click': this.view.onClick,
\r
1329 'mouseover': this.view.onMouseOver,
\r
1330 'mouseout': this.view.onMouseOut,
\r
1335 getTemplate: function() {
\r
1337 this.tpl = new Ext.XTemplate(
\r
1338 '<div class="ext-cal-mdv x-unselectable">',
\r
1339 '<table class="ext-cal-mvd-tbl" cellpadding="0" cellspacing="0">',
\r
1342 '<tr><td class="ext-cal-ev">{markup}</td></tr>',
\r
1349 this.tpl.compile();
\r
1353 update: function(dt) {
\r
1358 refresh: function() {
\r
1359 if (!this.rendered) {
\r
1362 var eventTpl = this.view.getEventTemplate(),
\r
1364 templateData = [];
\r
1366 evts = this.store.queryBy(function(rec) {
\r
1367 var thisDt = this.date.clearTime(true).getTime(),
\r
1368 recStart = rec.data[Ext.calendar.EventMappings.StartDate.name].clearTime(true).getTime(),
\r
1369 startsOnDate = (thisDt == recStart),
\r
1370 spansDate = false;
\r
1372 if (!startsOnDate) {
\r
1373 var recEnd = rec.data[Ext.calendar.EventMappings.EndDate.name].clearTime(true).getTime();
\r
1374 spansDate = recStart < thisDt && recEnd >= thisDt;
\r
1376 return startsOnDate || spansDate;
\r
1380 evts.each(function(evt) {
\r
1381 var item = evt.data,
\r
1382 M = Ext.calendar.EventMappings;
\r
1384 item._renderAsAllDay = item[M.IsAllDay.name] || Ext.calendar.Date.diffDays(item[M.StartDate.name], item[M.EndDate.name]) > 0;
\r
1385 item.spanLeft = Ext.calendar.Date.diffDays(item[M.StartDate.name], this.date) > 0;
\r
1386 item.spanRight = Ext.calendar.Date.diffDays(this.date, item[M.EndDate.name]) > 0;
\r
1387 item.spanCls = (item.spanLeft ? (item.spanRight ? 'ext-cal-ev-spanboth':
\r
1388 'ext-cal-ev-spanleft') : (item.spanRight ? 'ext-cal-ev-spanright': ''));
\r
1390 templateData.push({
\r
1391 markup: eventTpl.apply(this.getTemplateEventData(item))
\r
1396 this.tpl.overwrite(this.el, templateData);
\r
1397 this.fireEvent('eventsrendered', this, this.date, evts.getCount());
\r
1400 getTemplateEventData: function(evt) {
\r
1401 var data = this.view.getTemplateEventData(evt);
\r
1402 data._elId = 'dtl-' + data._elId;
\r
1407 Ext.reg('monthdaydetailview', Ext.calendar.MonthDayDetailView);
\r
1409 * @class Ext.calendar.CalendarPicker
1410 * @extends Ext.form.ComboBox
1411 * <p>A custom combo used for choosing from the list of available calendars to assign an event to.</p>
1412 * <p>This is pretty much a standard combo that is simply pre-configured for the options needed by the
1413 * calendar components. The default configs are as follows:<pre><code>
1414 fieldLabel: 'Calendar',
1415 valueField: 'CalendarId',
1416 displayField: 'Title',
1417 triggerAction: 'all',
1419 forceSelection: true,
1423 * @param {Object} config The config object
1425 Ext.calendar.CalendarPicker = Ext.extend(Ext.form.ComboBox, {
1426 fieldLabel: 'Calendar',
1427 valueField: 'CalendarId',
1428 displayField: 'Title',
1429 triggerAction: 'all',
1431 forceSelection: true,
1435 initComponent: function() {
1436 Ext.calendar.CalendarPicker.superclass.initComponent.call(this);
1437 this.tpl = this.tpl ||
1438 '<tpl for="."><div class="x-combo-list-item ext-color-{' + this.valueField +
1439 '}"><div class="ext-cal-picker-icon"> </div>{' + this.displayField + '}</div></tpl>';
1443 afterRender: function() {
1444 Ext.calendar.CalendarPicker.superclass.afterRender.call(this);
1446 this.wrap = this.el.up('.x-form-field-wrap');
1447 this.wrap.addClass('ext-calendar-picker');
1449 this.icon = Ext.DomHelper.append(this.wrap, {
1451 cls: 'ext-cal-picker-icon ext-cal-picker-mainicon'
1456 setValue: function(value) {
1457 this.wrap.removeClass('ext-color-' + this.getValue());
1458 if (!value && this.store !== undefined) {
1459 // always default to a valid calendar
1460 value = this.store.getAt(0).data.CalendarId;
1462 Ext.calendar.CalendarPicker.superclass.setValue.call(this, value);
1463 this.wrap.addClass('ext-color-' + value);
1467 Ext.reg('calendarpicker', Ext.calendar.CalendarPicker);
1469 * This is an internal helper class for the calendar views and should not be overridden.
1470 * It is responsible for the base event rendering logic underlying all of the calendar views.
1472 Ext.calendar.WeekEventRenderer = function() {
1474 var getEventRow = function(id, week, index) {
1475 var indexOffset = 1,
1476 //skip row with date #'s
1478 wkRow = Ext.get(id + '-wk-' + week);
1480 var table = wkRow.child('.ext-cal-evt-tbl', true);
1481 evtRow = table.tBodies[0].childNodes[index + indexOffset];
1483 evtRow = Ext.DomHelper.append(table.tBodies[0], '<tr></tr>');
1486 return Ext.get(evtRow);
1490 render: function(o) {
1493 dt = o.viewStart.clone(),
1495 max = o.maxEventsPerDay != undefined ? o.maxEventsPerDay: 999,
1496 weekCount = o.weekCount < 1 ? 6: o.weekCount,
1497 dayCount = o.weekCount == 1 ? o.dayCount: 7,
1500 for (; w < weekCount; w++) {
1501 if (!grid[w] || grid[w].length == 0) {
1502 // no events or span cells for the entire week
1503 if (weekCount == 1) {
1504 row = getEventRow(o.id, w, 0);
1508 id: o.id + '-empty-0-day-' + dt.format('Ymd'),
1512 cellCfg.colspan = dayCount;
1514 Ext.DomHelper.append(row, cellCfg);
1516 dt = dt.add(Date.DAY, 7);
1521 startOfWeek = dt.clone(),
1522 endOfWeek = startOfWeek.add(Date.DAY, dayCount).add(Date.MILLI, -1);
1524 for (; d < dayCount; d++) {
1526 var ev = emptyCells = skipped = 0,
1531 for (; ev < ct; ev++) {
1536 if (emptyCells > 0 && ev - emptyCells < max) {
1537 row = getEventRow(o.id, w, ev - emptyCells);
1541 id: o.id + '-empty-' + ct + '-day-' + dt.format('Ymd')
1543 if (emptyCells > 1 && max - ev > emptyCells) {
1544 cellCfg.rowspan = Math.min(emptyCells, max - ev);
1546 Ext.DomHelper.append(row, cellCfg);
1556 if (!evt.isSpan || evt.isSpanStart) {
1557 //skip non-starting span cells
1558 var item = evt.data || evt.event.data;
1559 item._weekIndex = w;
1560 item._renderAsAllDay = item[Ext.calendar.EventMappings.IsAllDay.name] || evt.isSpanStart;
1561 item.spanLeft = item[Ext.calendar.EventMappings.StartDate.name].getTime() < startOfWeek.getTime();
1562 item.spanRight = item[Ext.calendar.EventMappings.EndDate.name].getTime() > endOfWeek.getTime();
1563 item.spanCls = (item.spanLeft ? (item.spanRight ? 'ext-cal-ev-spanboth':
1564 'ext-cal-ev-spanleft') : (item.spanRight ? 'ext-cal-ev-spanright': ''));
1566 row = getEventRow(o.id, w, ev);
1570 cn: eventTpl.apply(o.templateDataFn(item))
1572 var diff = Ext.calendar.Date.diffDays(dt, item[Ext.calendar.EventMappings.EndDate.name]) + 1,
1573 cspan = Math.min(diff, dayCount - d);
1576 cellCfg.colspan = cspan;
1578 Ext.DomHelper.append(row, cellCfg);
1582 row = getEventRow(o.id, w, max);
1583 Ext.DomHelper.append(row, {
1585 cls: 'ext-cal-ev-more',
1586 id: 'ext-cal-ev-more-' + dt.format('Ymd'),
1589 html: '+' + skipped + ' more...'
1593 if (ct < o.evtMaxCount[w]) {
1594 row = getEventRow(o.id, w, ct);
1599 id: o.id + '-empty-' + (ct + 1) + '-day-' + dt.format('Ymd')
1601 var rowspan = o.evtMaxCount[w] - ct;
1603 cellCfg.rowspan = rowspan;
1605 Ext.DomHelper.append(row, cellCfg);
1609 row = getEventRow(o.id, w, 0);
1614 id: o.id + '-empty-day-' + dt.format('Ymd')
1616 if (o.evtMaxCount[w] > 1) {
1617 cellCfg.rowSpan = o.evtMaxCount[w];
1619 Ext.DomHelper.append(row, cellCfg);
1622 dt = dt.add(Date.DAY, 1);
1630 * @class Ext.calendar.CalendarView
\r
1631 * @extends Ext.BoxComponent
\r
1632 * <p>This is an abstract class that serves as the base for other calendar views. This class is not
\r
1633 * intended to be directly instantiated.</p>
\r
1634 * <p>When extending this class to create a custom calendar view, you must provide an implementation
\r
1635 * for the <code>renderItems</code> method, as there is no default implementation for rendering events
\r
1636 * The rendering logic is totally dependent on how the UI structures its data, which
\r
1637 * is determined by the underlying UI template (this base class does not have a template).</p>
\r
1639 * @param {Object} config The config object
\r
1641 Ext.calendar.CalendarView = Ext.extend(Ext.BoxComponent, {
\r
1643 * @cfg {Number} startDay
\r
1644 * The 0-based index for the day on which the calendar week begins (0=Sunday, which is the default)
\r
1648 * @cfg {Boolean} spansHavePriority
\r
1649 * Allows switching between two different modes of rendering events that span multiple days. When true,
\r
1650 * span events are always sorted first, possibly at the expense of start dates being out of order (e.g.,
\r
1651 * a span event that starts at 11am one day and spans into the next day would display before a non-spanning
\r
1652 * event that starts at 10am, even though they would not be in date order). This can lead to more compact
\r
1653 * layouts when there are many overlapping events. If false (the default), events will always sort by start date
\r
1654 * first which can result in a less compact, but chronologically consistent layout.
\r
1656 spansHavePriority: false,
\r
1658 * @cfg {Boolean} trackMouseOver
\r
1659 * Whether or not the view tracks and responds to the browser mouseover event on contained elements (defaults to
\r
1660 * true). If you don't need mouseover event highlighting you can disable this.
\r
1662 trackMouseOver: true,
\r
1664 * @cfg {Boolean} enableFx
\r
1665 * Determines whether or not visual effects for CRUD actions are enabled (defaults to true). If this is false
\r
1666 * it will override any values for {@link #enableAddFx}, {@link #enableUpdateFx} or {@link enableRemoveFx} and
\r
1667 * all animations will be disabled.
\r
1671 * @cfg {Boolean} enableAddFx
\r
1672 * True to enable a visual effect on adding a new event (the default), false to disable it. Note that if
\r
1673 * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the
\r
1674 * {@link #doAddFx} method.
\r
1676 enableAddFx: true,
\r
1678 * @cfg {Boolean} enableUpdateFx
\r
1679 * True to enable a visual effect on updating an event, false to disable it (the default). Note that if
\r
1680 * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the
\r
1681 * {@link #doUpdateFx} method.
\r
1683 enableUpdateFx: false,
\r
1685 * @cfg {Boolean} enableRemoveFx
\r
1686 * True to enable a visual effect on removing an event (the default), false to disable it. Note that if
\r
1687 * {@link #enableFx} is false it will override this value. The specific effect that runs is defined in the
\r
1688 * {@link #doRemoveFx} method.
\r
1690 enableRemoveFx: true,
\r
1692 * @cfg {Boolean} enableDD
\r
1693 * True to enable drag and drop in the calendar view (the default), false to disable it
\r
1697 * @cfg {Boolean} monitorResize
\r
1698 * True to monitor the browser's resize event (the default), false to ignore it. If the calendar view is rendered
\r
1699 * into a fixed-size container this can be set to false. However, if the view can change dimensions (e.g., it's in
\r
1700 * fit layout in a viewport or some other resizable container) it is very important that this config is true so that
\r
1701 * any resize event propagates properly to all subcomponents and layouts get recalculated properly.
\r
1703 monitorResize: true,
\r
1705 * @cfg {String} ddCreateEventText
\r
1706 * The text to display inside the drag proxy while dragging over the calendar to create a new event (defaults to
\r
1707 * 'Create event for {0}' where {0} is a date range supplied by the view)
\r
1709 ddCreateEventText: 'Create event for {0}',
\r
1711 * @cfg {String} ddMoveEventText
\r
1712 * The text to display inside the drag proxy while dragging an event to reposition it (defaults to
\r
1713 * 'Move event to {0}' where {0} is the updated event start date/time supplied by the view)
\r
1715 ddMoveEventText: 'Move event to {0}',
\r
1717 * @cfg {String} ddResizeEventText
\r
1718 * The string displayed to the user in the drag proxy while dragging the resize handle of an event (defaults to
\r
1719 * 'Update event to {0}' where {0} is the updated event start-end range supplied by the view). Note that
\r
1720 * this text is only used in views
\r
1721 * that allow resizing of events.
\r
1723 ddResizeEventText: 'Update event to {0}',
\r
1725 //private properties -- do not override:
\r
1728 eventSelector: '.ext-cal-evt',
\r
1729 eventOverClass: 'ext-evt-over',
\r
1730 eventElIdDelimiter: '-evt-',
\r
1731 dayElIdDelimiter: '-day-',
\r
1734 * Returns a string of HTML template markup to be used as the body portion of the event template created
\r
1735 * by {@link #getEventTemplate}. This provdes the flexibility to customize what's in the body without
\r
1736 * having to override the entire XTemplate. This string can include any valid {@link Ext.Template} code, and
\r
1737 * any data tokens accessible to the containing event template can be referenced in this string.
\r
1738 * @return {String} The body template string
\r
1740 getEventBodyMarkup: Ext.emptyFn,
\r
1741 // must be implemented by a subclass
\r
1743 * <p>Returns the XTemplate that is bound to the calendar's event store (it expects records of type
\r
1744 * {@link Ext.calendar.EventRecord}) to populate the calendar views with events. Internally this method
\r
1745 * by default generates different markup for browsers that support CSS border radius and those that don't.
\r
1746 * This method can be overridden as needed to customize the markup generated.</p>
\r
1747 * <p>Note that this method calls {@link #getEventBodyMarkup} to retrieve the body markup for events separately
\r
1748 * from the surrounding container markup. This provdes the flexibility to customize what's in the body without
\r
1749 * having to override the entire XTemplate. If you do override this method, you should make sure that your
\r
1750 * overridden version also does the same.</p>
\r
1751 * @return {Ext.XTemplate} The event XTemplate
\r
1753 getEventTemplate: Ext.emptyFn,
\r
1754 // must be implemented by a subclass
\r
1756 initComponent: function() {
\r
1757 this.setStartDate(this.startDate || new Date());
\r
1759 Ext.calendar.CalendarView.superclass.initComponent.call(this);
\r
1763 * @event eventsrendered
\r
1764 * Fires after events are finished rendering in the view
\r
1765 * @param {Ext.calendar.CalendarView} this
\r
1767 eventsrendered: true,
\r
1769 * @event eventclick
\r
1770 * Fires after the user clicks on an event element
\r
1771 * @param {Ext.calendar.CalendarView} this
\r
1772 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was clicked on
\r
1773 * @param {HTMLNode} el The DOM node that was clicked on
\r
1777 * @event eventover
\r
1778 * Fires anytime the mouse is over an event element
\r
1779 * @param {Ext.calendar.CalendarView} this
\r
1780 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor is over
\r
1781 * @param {HTMLNode} el The DOM node that is being moused over
\r
1786 * Fires anytime the mouse exits an event element
\r
1787 * @param {Ext.calendar.CalendarView} this
\r
1788 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor exited
\r
1789 * @param {HTMLNode} el The DOM node that was exited
\r
1793 * @event datechange
\r
1794 * Fires after the start date of the view changes
\r
1795 * @param {Ext.calendar.CalendarView} this
\r
1796 * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}
\r
1797 * @param {Date} viewStart The first displayed date in the view
\r
1798 * @param {Date} viewEnd The last displayed date in the view
\r
1802 * @event rangeselect
\r
1803 * Fires after the user drags on the calendar to select a range of dates/times in which to create an event
\r
1804 * @param {Ext.calendar.CalendarView} this
\r
1805 * @param {Object} dates An object containing the start (StartDate property) and end (EndDate property) dates selected
\r
1806 * @param {Function} callback A callback function that MUST be called after the event handling is complete so that
\r
1807 * the view is properly cleaned up (shim elements are persisted in the view while the user is prompted to handle the
\r
1808 * range selection). The callback is already created in the proper scope, so it simply needs to be executed as a standard
\r
1809 * function call (e.g., callback()).
\r
1811 rangeselect: true,
\r
1813 * @event eventmove
\r
1814 * Fires after an event element is dragged by the user and dropped in a new position
\r
1815 * @param {Ext.calendar.CalendarView} this
\r
1816 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was moved with
\r
1817 * updated start and end dates
\r
1822 * Fires when a drag operation is initiated in the view
\r
1823 * @param {Ext.calendar.CalendarView} this
\r
1828 * Fires while the mouse is over a day element
\r
1829 * @param {Ext.calendar.CalendarView} this
\r
1830 * @param {Date} dt The date that is being moused over
\r
1831 * @param {Ext.Element} el The day Element that is being moused over
\r
1836 * Fires when the mouse exits a day element
\r
1837 * @param {Ext.calendar.CalendarView} this
\r
1838 * @param {Date} dt The date that is exited
\r
1839 * @param {Ext.Element} el The day Element that is exited
\r
1843 * @event eventdelete
\r
1844 * Fires after an event element is deleted by the user. Not currently implemented directly at the view level -- currently
\r
1845 * deletes only happen from one of the forms.
\r
1846 * @param {Ext.calendar.CalendarView} this
\r
1847 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was deleted
\r
1849 //eventdelete: true
\r
1854 afterRender: function() {
\r
1855 Ext.calendar.CalendarView.superclass.afterRender.call(this);
\r
1857 this.renderTemplate();
\r
1860 this.setStore(this.store, true);
\r
1864 'mouseover': this.onMouseOver,
\r
1865 'mouseout': this.onMouseOut,
\r
1866 'click': this.onClick,
\r
1867 'resize': this.onResize,
\r
1871 this.el.unselectable();
\r
1873 if (this.enableDD && this.initDD) {
\r
1877 this.on('eventsrendered', this.forceSize);
\r
1878 this.forceSize.defer(100, this);
\r
1883 forceSize: function() {
\r
1884 if (this.el && this.el.child) {
\r
1885 var hd = this.el.child('.ext-cal-hd-ct'),
\r
1886 bd = this.el.child('.ext-cal-body-ct');
\r
1888 if (bd == null || hd == null) return;
\r
1890 var headerHeight = hd.getHeight(),
\r
1891 sz = this.el.parent().getSize();
\r
1893 bd.setHeight(sz.height - headerHeight);
\r
1897 refresh: function() {
\r
1898 this.prepareData();
\r
1899 this.renderTemplate();
\r
1900 this.renderItems();
\r
1903 getWeekCount: function() {
\r
1904 var days = Ext.calendar.Date.diffDays(this.viewStart, this.viewEnd);
\r
1905 return Math.ceil(days / this.dayCount);
\r
1909 prepareData: function() {
\r
1910 var lastInMonth = this.startDate.getLastDateOfMonth(),
\r
1913 dt = this.viewStart.clone(),
\r
1914 weeks = this.weekCount < 1 ? 6: this.weekCount;
\r
1916 this.eventGrid = [[]];
\r
1917 this.allDayGrid = [[]];
\r
1918 this.evtMaxCount = [];
\r
1920 var evtsInView = this.store.queryBy(function(rec) {
\r
1921 return this.isEventVisible(rec.data);
\r
1925 for (; w < weeks; w++) {
\r
1926 this.evtMaxCount[w] = 0;
\r
1927 if (this.weekCount == -1 && dt > lastInMonth) {
\r
1928 //current week is fully in next month so skip
\r
1931 this.eventGrid[w] = this.eventGrid[w] || [];
\r
1932 this.allDayGrid[w] = this.allDayGrid[w] || [];
\r
1934 for (d = 0; d < this.dayCount; d++) {
\r
1935 if (evtsInView.getCount() > 0) {
\r
1936 var evts = evtsInView.filterBy(function(rec) {
\r
1937 var startsOnDate = (dt.getTime() == rec.data[Ext.calendar.EventMappings.StartDate.name].clearTime(true).getTime());
\r
1938 var spansFromPrevView = (w == 0 && d == 0 && (dt > rec.data[Ext.calendar.EventMappings.StartDate.name]));
\r
1939 return startsOnDate || spansFromPrevView;
\r
1943 this.sortEventRecordsForDay(evts);
\r
1944 this.prepareEventGrid(evts, w, d);
\r
1946 dt = dt.add(Date.DAY, 1);
\r
1949 this.currentWeekCount = w;
\r
1953 prepareEventGrid: function(evts, w, d) {
\r
1955 dt = this.viewStart.clone(),
\r
1956 max = this.maxEventsPerDay ? this.maxEventsPerDay: 999;
\r
1958 evts.each(function(evt) {
\r
1959 var M = Ext.calendar.EventMappings,
\r
1960 days = Ext.calendar.Date.diffDays(
\r
1961 Ext.calendar.Date.max(this.viewStart, evt.data[M.StartDate.name]),
\r
1962 Ext.calendar.Date.min(this.viewEnd, evt.data[M.EndDate.name])) + 1;
\r
1964 if (days > 1 || Ext.calendar.Date.diffDays(evt.data[M.StartDate.name], evt.data[M.EndDate.name]) > 1) {
\r
1965 this.prepareEventGridSpans(evt, this.eventGrid, w, d, days);
\r
1966 this.prepareEventGridSpans(evt, this.allDayGrid, w, d, days, true);
\r
1968 row = this.findEmptyRowIndex(w, d);
\r
1969 this.eventGrid[w][d] = this.eventGrid[w][d] || [];
\r
1970 this.eventGrid[w][d][row] = evt;
\r
1972 if (evt.data[M.IsAllDay.name]) {
\r
1973 row = this.findEmptyRowIndex(w, d, true);
\r
1974 this.allDayGrid[w][d] = this.allDayGrid[w][d] || [];
\r
1975 this.allDayGrid[w][d][row] = evt;
\r
1979 if (this.evtMaxCount[w] < this.eventGrid[w][d].length) {
\r
1980 this.evtMaxCount[w] = Math.min(max + 1, this.eventGrid[w][d].length);
\r
1988 prepareEventGridSpans: function(evt, grid, w, d, days, allday) {
\r
1989 // this event spans multiple days/weeks, so we have to preprocess
\r
1990 // the events and store special span events as placeholders so that
\r
1991 // the render routine can build the necessary TD spans correctly.
\r
1994 row = this.findEmptyRowIndex(w, d, allday),
\r
1995 dt = this.viewStart.clone();
\r
2000 isSpanStart: true,
\r
2002 spanRight: (d == 6)
\r
2004 grid[w][d] = grid[w][d] || [];
\r
2005 grid[w][d][row] = start;
\r
2008 dt = dt.add(Date.DAY, 1);
\r
2009 if (dt > this.viewEnd) {
\r
2013 // reset counters to the next week
\r
2016 row = this.findEmptyRowIndex(w1, 0);
\r
2018 grid[w1] = grid[w1] || [];
\r
2019 grid[w1][d1] = grid[w1][d1] || [];
\r
2021 grid[w1][d1][row] = {
\r
2024 isSpanStart: (d1 == 0),
\r
2025 spanLeft: (w1 > w) && (d1 % 7 == 0),
\r
2026 spanRight: (d1 == 6) && (days > 1)
\r
2032 findEmptyRowIndex: function(w, d, allday) {
\r
2033 var grid = allday ? this.allDayGrid: this.eventGrid,
\r
2034 day = grid[w] ? grid[w][d] || [] : [],
\r
2038 for (; i < ln; i++) {
\r
2039 if (day[i] == null) {
\r
2047 renderTemplate: function() {
\r
2049 this.tpl.overwrite(this.el, this.getParams());
\r
2050 this.lastRenderStart = this.viewStart.clone();
\r
2051 this.lastRenderEnd = this.viewEnd.clone();
\r
2055 disableStoreEvents: function() {
\r
2056 this.monitorStoreEvents = false;
\r
2059 enableStoreEvents: function(refresh) {
\r
2060 this.monitorStoreEvents = true;
\r
2061 if (refresh === true) {
\r
2067 onResize: function() {
\r
2072 onInitDrag: function() {
\r
2073 this.fireEvent('initdrag', this);
\r
2077 onEventDrop: function(rec, dt) {
\r
2078 if (Ext.calendar.Date.compare(rec.data[Ext.calendar.EventMappings.StartDate.name], dt) === 0) {
\r
2082 var diff = dt.getTime() - rec.data[Ext.calendar.EventMappings.StartDate.name].getTime();
\r
2083 rec.set(Ext.calendar.EventMappings.StartDate.name, dt);
\r
2084 rec.set(Ext.calendar.EventMappings.EndDate.name, rec.data[Ext.calendar.EventMappings.EndDate.name].add(Date.MILLI, diff));
\r
2086 this.fireEvent('eventmove', this, rec);
\r
2090 onCalendarEndDrag: function(start, end, onComplete) {
\r
2091 // set this flag for other event handlers that might conflict while we're waiting
\r
2092 this.dragPending = true;
\r
2094 // have to wait for the user to save or cancel before finalizing the dd interation
\r
2096 o[Ext.calendar.EventMappings.StartDate.name] = start;
\r
2097 o[Ext.calendar.EventMappings.EndDate.name] = end;
\r
2099 this.fireEvent('rangeselect', this, o, this.onCalendarEndDragComplete.createDelegate(this, [onComplete]));
\r
2103 onCalendarEndDragComplete: function(onComplete) {
\r
2104 // callback for the drop zone to clean up
\r
2106 // clear flag for other events to resume normally
\r
2107 this.dragPending = false;
\r
2111 onUpdate: function(ds, rec, operation) {
\r
2112 if (this.monitorStoreEvents === false) {
\r
2115 if (operation == Ext.data.Record.COMMIT) {
\r
2117 if (this.enableFx && this.enableUpdateFx) {
\r
2118 this.doUpdateFx(this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]), {
\r
2126 doUpdateFx: function(els, o) {
\r
2127 this.highlightEvent(els, null, o);
\r
2131 onAdd: function(ds, records, index) {
\r
2132 if (this.monitorStoreEvents === false) {
\r
2135 var rec = records[0];
\r
2136 this.tempEventId = rec.id;
\r
2139 if (this.enableFx && this.enableAddFx) {
\r
2140 this.doAddFx(this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]), {
\r
2146 doAddFx: function(els, o) {
\r
2147 els.fadeIn(Ext.apply(o, {
\r
2153 onRemove: function(ds, rec) {
\r
2154 if (this.monitorStoreEvents === false) {
\r
2157 if (this.enableFx && this.enableRemoveFx) {
\r
2158 this.doRemoveFx(this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]), {
\r
2161 callback: this.refresh
\r
2165 this.getEventEls(rec.data[Ext.calendar.EventMappings.EventId.name]).remove();
\r
2170 doRemoveFx: function(els, o) {
\r
2175 * Visually highlights an event using {@link Ext.Fx#highlight} config options.
\r
2176 * If {@link #highlightEventActions} is false this method will have no effect.
\r
2177 * @param {Ext.CompositeElement} els The element(s) to highlight
\r
2178 * @param {Object} color (optional) The highlight color. Should be a 6 char hex
\r
2179 * color without the leading # (defaults to yellow: 'ffff9c')
\r
2180 * @param {Object} o (optional) Object literal with any of the {@link Ext.Fx} config
\r
2181 * options. See {@link Ext.Fx#highlight} for usage examples.
\r
2183 highlightEvent: function(els, color, o) {
\r
2184 if (this.enableFx) {
\r
2186 ! (Ext.isIE || Ext.isOpera) ?
\r
2187 els.highlight(color, o) :
\r
2188 // Fun IE/Opera handling:
\r
2189 els.each(function(el) {
\r
2190 el.highlight(color, Ext.applyIf({
\r
2194 c = el.child('.ext-cal-evm');
\r
2196 c.highlight(color, o);
\r
2204 * Retrieve an Event object's id from its corresponding node in the DOM.
\r
2205 * @param {String/Element/HTMLElement} el An {@link Ext.Element}, DOM node or id
\r
2207 getEventIdFromEl: function(el) {
\r
2209 var id = el.id.split(this.eventElIdDelimiter)[1];
\r
2210 if (id.indexOf('-') > -1) {
\r
2211 //This id has the index of the week it is rendered in as the suffix.
\r
2212 //This allows events that span across weeks to still have reproducibly-unique DOM ids.
\r
2213 id = id.split('-')[0];
\r
2219 getEventId: function(eventId) {
\r
2220 if (eventId === undefined && this.tempEventId) {
\r
2221 eventId = this.tempEventId;
\r
2228 * @param {String} eventId
\r
2229 * @param {Boolean} forSelect
\r
2230 * @return {String} The selector class
\r
2232 getEventSelectorCls: function(eventId, forSelect) {
\r
2233 var prefix = forSelect ? '.': '';
\r
2234 return prefix + this.id + this.eventElIdDelimiter + this.getEventId(eventId);
\r
2239 * @param {String} eventId
\r
2240 * @return {Ext.CompositeElement} The matching CompositeElement of nodes
\r
2241 * that comprise the rendered event. Any event that spans across a view
\r
2242 * boundary will contain more than one internal Element.
\r
2244 getEventEls: function(eventId) {
\r
2245 var els = Ext.select(this.getEventSelectorCls(this.getEventId(eventId), true), false, this.el.id);
\r
2246 return new Ext.CompositeElement(els);
\r
2250 * Returns true if the view is currently displaying today's date, else false.
\r
2251 * @return {Boolean} True or false
\r
2253 isToday: function() {
\r
2254 var today = new Date().clearTime().getTime();
\r
2255 return this.viewStart.getTime() <= today && this.viewEnd.getTime() >= today;
\r
2259 onDataChanged: function(store) {
\r
2264 isEventVisible: function(evt) {
\r
2265 var start = this.viewStart.getTime(),
\r
2266 end = this.viewEnd.getTime(),
\r
2267 M = Ext.calendar.EventMappings,
\r
2268 evStart = (evt.data ? evt.data[M.StartDate.name] : evt[M.StartDate.name]).getTime(),
\r
2269 evEnd = (evt.data ? evt.data[M.EndDate.name] : evt[M.EndDate.name]).add(Date.SECOND, -1).getTime(),
\r
2271 startsInRange = (evStart >= start && evStart <= end),
\r
2272 endsInRange = (evEnd >= start && evEnd <= end),
\r
2273 spansRange = (evStart < start && evEnd > end);
\r
2275 return (startsInRange || endsInRange || spansRange);
\r
2279 isOverlapping: function(evt1, evt2) {
\r
2280 var ev1 = evt1.data ? evt1.data: evt1,
\r
2281 ev2 = evt2.data ? evt2.data: evt2,
\r
2282 M = Ext.calendar.EventMappings,
\r
2283 start1 = ev1[M.StartDate.name].getTime(),
\r
2284 end1 = ev1[M.EndDate.name].add(Date.SECOND, -1).getTime(),
\r
2285 start2 = ev2[M.StartDate.name].getTime(),
\r
2286 end2 = ev2[M.EndDate.name].add(Date.SECOND, -1).getTime();
\r
2288 if (end1 < start1) {
\r
2291 if (end2 < start2) {
\r
2295 var ev1startsInEv2 = (start1 >= start2 && start1 <= end2),
\r
2296 ev1EndsInEv2 = (end1 >= start2 && end1 <= end2),
\r
2297 ev1SpansEv2 = (start1 < start2 && end1 > end2);
\r
2299 return (ev1startsInEv2 || ev1EndsInEv2 || ev1SpansEv2);
\r
2302 getDayEl: function(dt) {
\r
2303 return Ext.get(this.getDayId(dt));
\r
2306 getDayId: function(dt) {
\r
2307 if (Ext.isDate(dt)) {
\r
2308 dt = dt.format('Ymd');
\r
2310 return this.id + this.dayElIdDelimiter + dt;
\r
2314 * Returns the start date of the view, as set by {@link #setStartDate}. Note that this may not
\r
2315 * be the first date displayed in the rendered calendar -- to get the start and end dates displayed
\r
2316 * to the user use {@link #getViewBounds}.
\r
2317 * @return {Date} The start date
\r
2319 getStartDate: function() {
\r
2320 return this.startDate;
\r
2324 * Sets the start date used to calculate the view boundaries to display. The displayed view will be the
\r
2325 * earliest and latest dates that match the view requirements and contain the date passed to this function.
\r
2326 * @param {Date} dt The date used to calculate the new view boundaries
\r
2328 setStartDate: function(start, refresh) {
\r
2329 this.startDate = start.clearTime();
\r
2330 this.setViewBounds(start);
\r
2333 start: this.viewStart.format('m-d-Y'),
\r
2334 end: this.viewEnd.format('m-d-Y')
\r
2337 if (refresh === true) {
\r
2340 this.fireEvent('datechange', this, this.startDate, this.viewStart, this.viewEnd);
\r
2344 setViewBounds: function(startDate) {
\r
2345 var start = startDate || this.startDate,
\r
2346 offset = start.getDay() - this.startDay;
\r
2348 switch (this.weekCount) {
\r
2351 this.viewStart = this.dayCount < 7 ? start: start.add(Date.DAY, -offset).clearTime(true);
\r
2352 this.viewEnd = this.viewStart.add(Date.DAY, this.dayCount || 7).add(Date.SECOND, -1);
\r
2357 start = start.getFirstDateOfMonth();
\r
2358 offset = start.getDay() - this.startDay;
\r
2362 this.viewStart = start.add(Date.DAY, -offset).clearTime(true);
\r
2364 // start from current month start, not view start:
\r
2365 var end = start.add(Date.MONTH, 1).add(Date.SECOND, -1);
\r
2366 // fill out to the end of the week:
\r
2367 this.viewEnd = end.add(Date.DAY, 6 - end.getDay());
\r
2371 this.viewStart = start.add(Date.DAY, -offset).clearTime(true);
\r
2372 this.viewEnd = this.viewStart.add(Date.DAY, this.weekCount * 7).add(Date.SECOND, -1);
\r
2377 getViewBounds: function() {
\r
2379 start: this.viewStart,
\r
2385 * Sort events for a single day for display in the calendar. This sorts allday
\r
2386 * events first, then non-allday events are sorted either based on event start
\r
2387 * priority or span priority based on the value of {@link #spansHavePriority}
\r
2388 * (defaults to event start priority).
\r
2389 * @param {MixedCollection} evts A {@link Ext.util.MixedCollection MixedCollection}
\r
2390 * of {@link #Ext.calendar.EventRecord EventRecord} objects
\r
2392 sortEventRecordsForDay: function(evts) {
\r
2393 if (evts.length < 2) {
\r
2397 function(evtA, evtB) {
\r
2398 var a = evtA.data,
\r
2400 M = Ext.calendar.EventMappings;
\r
2402 // Always sort all day events before anything else
\r
2403 if (a[M.IsAllDay.name]) {
\r
2406 else if (b[M.IsAllDay.name]) {
\r
2409 if (this.spansHavePriority) {
\r
2410 // This logic always weights span events higher than non-span events
\r
2411 // (at the possible expense of start time order). This seems to
\r
2412 // be the approach used by Google calendar and can lead to a more
\r
2413 // visually appealing layout in complex cases, but event order is
\r
2414 // not guaranteed to be consistent.
\r
2415 var diff = Ext.calendar.Date.diffDays;
\r
2416 if (diff(a[M.StartDate.name], a[M.EndDate.name]) > 0) {
\r
2417 if (diff(b[M.StartDate.name], b[M.EndDate.name]) > 0) {
\r
2418 // Both events are multi-day
\r
2419 if (a[M.StartDate.name].getTime() == b[M.StartDate.name].getTime()) {
\r
2420 // If both events start at the same time, sort the one
\r
2421 // that ends later (potentially longer span bar) first
\r
2422 return b[M.EndDate.name].getTime() - a[M.EndDate.name].getTime();
\r
2424 return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();
\r
2428 else if (diff(b[M.StartDate.name], b[M.EndDate.name]) > 0) {
\r
2431 return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();
\r
2434 // Doing this allows span and non-span events to intermingle but
\r
2435 // remain sorted sequentially by start time. This seems more proper
\r
2436 // but can make for a less visually-compact layout when there are
\r
2437 // many such events mixed together closely on the calendar.
\r
2438 return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();
\r
2440 }.createDelegate(this));
\r
2444 * Updates the view to contain the passed date
\r
2445 * @param {Date} dt The date to display
\r
2447 moveTo: function(dt, noRefresh) {
\r
2448 if (Ext.isDate(dt)) {
\r
2449 this.setStartDate(dt);
\r
2450 if (noRefresh !== false) {
\r
2453 return this.startDate;
\r
2459 * Updates the view to the next consecutive date(s)
\r
2461 moveNext: function(noRefresh) {
\r
2462 return this.moveTo(this.viewEnd.add(Date.DAY, 1));
\r
2466 * Updates the view to the previous consecutive date(s)
\r
2468 movePrev: function(noRefresh) {
\r
2469 var days = Ext.calendar.Date.diffDays(this.viewStart, this.viewEnd) + 1;
\r
2470 return this.moveDays( - days, noRefresh);
\r
2474 * Shifts the view by the passed number of months relative to the currently set date
\r
2475 * @param {Number} value The number of months (positive or negative) by which to shift the view
\r
2477 moveMonths: function(value, noRefresh) {
\r
2478 return this.moveTo(this.startDate.add(Date.MONTH, value), noRefresh);
\r
2482 * Shifts the view by the passed number of weeks relative to the currently set date
\r
2483 * @param {Number} value The number of weeks (positive or negative) by which to shift the view
\r
2485 moveWeeks: function(value, noRefresh) {
\r
2486 return this.moveTo(this.startDate.add(Date.DAY, value * 7), noRefresh);
\r
2490 * Shifts the view by the passed number of days relative to the currently set date
\r
2491 * @param {Number} value The number of days (positive or negative) by which to shift the view
\r
2493 moveDays: function(value, noRefresh) {
\r
2494 return this.moveTo(this.startDate.add(Date.DAY, value), noRefresh);
\r
2498 * Updates the view to show today
\r
2500 moveToday: function(noRefresh) {
\r
2501 return this.moveTo(new Date(), noRefresh);
\r
2505 * Sets the event store used by the calendar to display {@link Ext.calendar.EventRecord events}.
\r
2506 * @param {Ext.data.Store} store
\r
2508 setStore: function(store, initial) {
\r
2509 if (!initial && this.store) {
\r
2510 this.store.un("datachanged", this.onDataChanged, this);
\r
2511 this.store.un("add", this.onAdd, this);
\r
2512 this.store.un("remove", this.onRemove, this);
\r
2513 this.store.un("update", this.onUpdate, this);
\r
2514 this.store.un("clear", this.refresh, this);
\r
2517 store.on("datachanged", this.onDataChanged, this);
\r
2518 store.on("add", this.onAdd, this);
\r
2519 store.on("remove", this.onRemove, this);
\r
2520 store.on("update", this.onUpdate, this);
\r
2521 store.on("clear", this.refresh, this);
\r
2523 this.store = store;
\r
2524 if (store && store.getCount() > 0) {
\r
2529 getEventRecord: function(id) {
\r
2530 var idx = this.store.find(Ext.calendar.EventMappings.EventId.name, id);
\r
2531 return this.store.getAt(idx);
\r
2534 getEventRecordFromEl: function(el) {
\r
2535 return this.getEventRecord(this.getEventIdFromEl(el));
\r
2539 getParams: function() {
\r
2541 viewStart: this.viewStart,
\r
2542 viewEnd: this.viewEnd,
\r
2543 startDate: this.startDate,
\r
2544 dayCount: this.dayCount,
\r
2545 weekCount: this.weekCount,
\r
2546 title: this.getTitle()
\r
2550 getTitle: function() {
\r
2551 return this.startDate.format('F Y');
\r
2555 * Shared click handling. Each specific view also provides view-specific
\r
2556 * click handling that calls this first. This method returns true if it
\r
2557 * can handle the click (and so the subclass should ignore it) else false.
\r
2559 onClick: function(e, t) {
\r
2560 var el = e.getTarget(this.eventSelector, 5);
\r
2562 var id = this.getEventIdFromEl(el);
\r
2563 this.fireEvent('eventclick', this, this.getEventRecord(id), el);
\r
2569 onMouseOver: function(e, t) {
\r
2570 if (this.trackMouseOver !== false && (this.dragZone == undefined || !this.dragZone.dragging)) {
\r
2571 if (!this.handleEventMouseEvent(e, t, 'over')) {
\r
2572 this.handleDayMouseEvent(e, t, 'over');
\r
2578 onMouseOut: function(e, t) {
\r
2579 if (this.trackMouseOver !== false && (this.dragZone == undefined || !this.dragZone.dragging)) {
\r
2580 if (!this.handleEventMouseEvent(e, t, 'out')) {
\r
2581 this.handleDayMouseEvent(e, t, 'out');
\r
2587 handleEventMouseEvent: function(e, t, type) {
\r
2588 var el = e.getTarget(this.eventSelector, 5, true),
\r
2593 rel = Ext.get(e.getRelatedTarget());
\r
2594 if (el == rel || el.contains(rel)) {
\r
2598 evtId = this.getEventIdFromEl(el);
\r
2600 if (this.eventOverClass != '') {
\r
2601 els = this.getEventEls(evtId);
\r
2602 els[type == 'over' ? 'addClass': 'removeClass'](this.eventOverClass);
\r
2604 this.fireEvent('event' + type, this, this.getEventRecord(evtId), el);
\r
2611 getDateFromId: function(id, delim) {
\r
2612 var parts = id.split(delim);
\r
2613 return parts[parts.length - 1];
\r
2617 handleDayMouseEvent: function(e, t, type) {
\r
2618 t = e.getTarget('td', 3);
\r
2620 if (t.id && t.id.indexOf(this.dayElIdDelimiter) > -1) {
\r
2621 var dt = this.getDateFromId(t.id, this.dayElIdDelimiter),
\r
2622 rel = Ext.get(e.getRelatedTarget()),
\r
2627 relTD = rel.is('td') ? rel: rel.up('td', 3);
\r
2628 relDate = relTD && relTD.id ? this.getDateFromId(relTD.id, this.dayElIdDelimiter) : '';
\r
2630 if (!rel || dt != relDate) {
\r
2631 var el = this.getDayEl(dt);
\r
2632 if (el && this.dayOverClass != '') {
\r
2633 el[type == 'over' ? 'addClass': 'removeClass'](this.dayOverClass);
\r
2635 this.fireEvent('day' + type, this, Date.parseDate(dt, "Ymd"), el);
\r
2642 renderItems: function() {
\r
2643 throw 'This method must be implemented by a subclass';
\r
2647 * @class Ext.calendar.MonthView
\r
2648 * @extends Ext.calendar.CalendarView
\r
2649 * <p>Displays a calendar view by month. This class does not usually need ot be used directly as you can
\r
2650 * use a {@link Ext.calendar.CalendarPanel CalendarPanel} to manage multiple calendar views at once including
\r
2651 * the month view.</p>
\r
2653 * @param {Object} config The config object
\r
2655 Ext.calendar.MonthView = Ext.extend(Ext.calendar.CalendarView, {
\r
2657 * @cfg {Boolean} showTime
\r
2658 * True to display the current time in today's box in the calendar, false to not display it (defautls to true)
\r
2662 * @cfg {Boolean} showTodayText
\r
2663 * True to display the {@link #todayText} string in today's box in the calendar, false to not display it (defautls to true)
\r
2665 showTodayText: true,
\r
2667 * @cfg {String} todayText
\r
2668 * The text to display in the current day's box in the calendar when {@link #showTodayText} is true (defaults to 'Today')
\r
2670 todayText: 'Today',
\r
2672 * @cfg {Boolean} showHeader
\r
2673 * True to display a header beneath the navigation bar containing the week names above each week's column, false not to
\r
2674 * show it and instead display the week names in the first row of days in the calendar (defaults to false).
\r
2676 showHeader: false,
\r
2678 * @cfg {Boolean} showWeekLinks
\r
2679 * True to display an extra column before the first day in the calendar that links to the {@link Ext.calendar.WeekView view}
\r
2680 * for each individual week, false to not show it (defaults to false). If true, the week links can also contain the week
\r
2681 * number depending on the value of {@link #showWeekNumbers}.
\r
2683 showWeekLinks: false,
\r
2685 * @cfg {Boolean} showWeekNumbers
\r
2686 * True to show the week number for each week in the calendar in the week link column, false to show nothing (defaults to false).
\r
2687 * Note that if {@link #showWeekLinks} is false this config will have no affect even if true.
\r
2689 showWeekNumbers: false,
\r
2691 * @cfg {String} weekLinkOverClass
\r
2692 * The CSS class name applied when the mouse moves over a week link element (only applies when {@link #showWeekLinks} is true,
\r
2693 * defaults to 'ext-week-link-over').
\r
2695 weekLinkOverClass: 'ext-week-link-over',
\r
2697 //private properties -- do not override:
\r
2698 daySelector: '.ext-cal-day',
\r
2699 moreSelector: '.ext-cal-ev-more',
\r
2700 weekLinkSelector: '.ext-cal-week-link',
\r
2702 // defaults to auto by month
\r
2704 moreElIdDelimiter: '-more-',
\r
2705 weekLinkIdDelimiter: 'ext-cal-week-',
\r
2708 initComponent: function() {
\r
2709 Ext.calendar.MonthView.superclass.initComponent.call(this);
\r
2713 * Fires after the user clicks within the view container and not on an event element
\r
2714 * @param {Ext.calendar.MonthView} this
\r
2715 * @param {Date} dt The date/time that was clicked on
\r
2716 * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks within the
\r
2717 * MonthView always return true for this param.
\r
2718 * @param {Ext.Element} el The Element that was clicked on
\r
2722 * @event weekclick
\r
2723 * Fires after the user clicks within a week link (when {@link #showWeekLinks is true)
\r
2724 * @param {Ext.calendar.MonthView} this
\r
2725 * @param {Date} dt The start date of the week that was clicked on
\r
2736 initDD: function() {
\r
2739 createText: this.ddCreateEventText,
\r
2740 moveText: this.ddMoveEventText,
\r
2741 ddGroup: 'MonthViewDD'
\r
2744 this.dragZone = new Ext.calendar.DragZone(this.el, cfg);
\r
2745 this.dropZone = new Ext.calendar.DropZone(this.el, cfg);
\r
2749 onDestroy: function() {
\r
2750 Ext.destroy(this.ddSelector);
\r
2751 Ext.destroy(this.dragZone);
\r
2752 Ext.destroy(this.dropZone);
\r
2753 Ext.calendar.MonthView.superclass.onDestroy.call(this);
\r
2757 afterRender: function() {
\r
2759 this.tpl = new Ext.calendar.MonthViewTemplate({
\r
2761 showTodayText: this.showTodayText,
\r
2762 todayText: this.todayText,
\r
2763 showTime: this.showTime,
\r
2764 showHeader: this.showHeader,
\r
2765 showWeekLinks: this.showWeekLinks,
\r
2766 showWeekNumbers: this.showWeekNumbers
\r
2769 this.tpl.compile();
\r
2770 this.addClass('ext-cal-monthview ext-cal-ct');
\r
2772 Ext.calendar.MonthView.superclass.afterRender.call(this);
\r
2776 onResize: function() {
\r
2777 if (this.monitorResize) {
\r
2778 this.maxEventsPerDay = this.getMaxEventsPerDay();
\r
2784 forceSize: function() {
\r
2785 // Compensate for the week link gutter width if visible
\r
2786 if (this.showWeekLinks && this.el && this.el.child) {
\r
2787 var hd = this.el.select('.ext-cal-hd-days-tbl'),
\r
2788 bgTbl = this.el.select('.ext-cal-bg-tbl'),
\r
2789 evTbl = this.el.select('.ext-cal-evt-tbl'),
\r
2790 wkLinkW = this.el.child('.ext-cal-week-link').getWidth(),
\r
2791 w = this.el.getWidth() - wkLinkW;
\r
2794 bgTbl.setWidth(w);
\r
2795 evTbl.setWidth(w);
\r
2797 Ext.calendar.MonthView.superclass.forceSize.call(this);
\r
2801 initClock: function() {
\r
2802 if (Ext.fly(this.id + '-clock') !== null) {
\r
2803 this.prevClockDay = new Date().getDay();
\r
2804 if (this.clockTask) {
\r
2805 Ext.TaskMgr.stop(this.clockTask);
\r
2807 this.clockTask = Ext.TaskMgr.start({
\r
2809 var el = Ext.fly(this.id + '-clock'),
\r
2812 if (t.getDay() == this.prevClockDay) {
\r
2814 el.update(t.format('g:i a'));
\r
2818 this.prevClockDay = t.getDay();
\r
2829 getEventBodyMarkup: function() {
\r
2830 if (!this.eventBodyMarkup) {
\r
2831 this.eventBodyMarkup = ['{Title}',
\r
2832 '<tpl if="_isReminder">',
\r
2833 '<i class="ext-cal-ic ext-cal-ic-rem"> </i>',
\r
2835 '<tpl if="_isRecurring">',
\r
2836 '<i class="ext-cal-ic ext-cal-ic-rcr"> </i>',
\r
2838 '<tpl if="spanLeft">',
\r
2839 '<i class="ext-cal-spl"> </i>',
\r
2841 '<tpl if="spanRight">',
\r
2842 '<i class="ext-cal-spr"> </i>',
\r
2846 return this.eventBodyMarkup;
\r
2850 getEventTemplate: function() {
\r
2851 if (!this.eventTpl) {
\r
2853 body = this.getEventBodyMarkup();
\r
2855 tpl = !(Ext.isIE || Ext.isOpera) ?
\r
2856 new Ext.XTemplate(
\r
2857 '<div id="{_elId}" class="{_selectorCls} {_colorCls} {values.spanCls} ext-cal-evt ext-cal-evr">',
\r
2861 : new Ext.XTemplate(
\r
2862 '<tpl if="_renderAsAllDay">',
\r
2863 '<div id="{_elId}" class="{_selectorCls} {values.spanCls} {_colorCls} ext-cal-evt ext-cal-evo">',
\r
2864 '<div class="ext-cal-evm">',
\r
2865 '<div class="ext-cal-evi">',
\r
2867 '<tpl if="!_renderAsAllDay">',
\r
2868 '<div id="{_elId}" class="{_selectorCls} {_colorCls} ext-cal-evt ext-cal-evr">',
\r
2871 '<tpl if="_renderAsAllDay">',
\r
2878 this.eventTpl = tpl;
\r
2880 return this.eventTpl;
\r
2884 getTemplateEventData: function(evt) {
\r
2885 var M = Ext.calendar.EventMappings,
\r
2886 selector = this.getEventSelectorCls(evt[M.EventId.name]),
\r
2887 title = evt[M.Title.name];
\r
2889 return Ext.applyIf({
\r
2890 _selectorCls: selector,
\r
2891 _colorCls: 'ext-color-' + (evt[M.CalendarId.name] ?
\r
2892 evt[M.CalendarId.name] : 'default') + (evt._renderAsAllDay ? '-ad': ''),
\r
2893 _elId: selector + '-' + evt._weekIndex,
\r
2894 _isRecurring: evt.Recurrence && evt.Recurrence != '',
\r
2895 _isReminder: evt[M.Reminder.name] && evt[M.Reminder.name] != '',
\r
2896 Title: (evt[M.IsAllDay.name] ? '': evt[M.StartDate.name].format('g:ia ')) + (!title || title.length == 0 ? '(No title)': title)
\r
2902 refresh: function() {
\r
2903 if (this.detailPanel) {
\r
2904 this.detailPanel.hide();
\r
2906 Ext.calendar.MonthView.superclass.refresh.call(this);
\r
2908 if (this.showTime !== false) {
\r
2914 renderItems: function() {
\r
2915 Ext.calendar.WeekEventRenderer.render({
\r
2916 eventGrid: this.allDayOnly ? this.allDayGrid: this.eventGrid,
\r
2917 viewStart: this.viewStart,
\r
2918 tpl: this.getEventTemplate(),
\r
2919 maxEventsPerDay: this.maxEventsPerDay,
\r
2921 templateDataFn: this.getTemplateEventData.createDelegate(this),
\r
2922 evtMaxCount: this.evtMaxCount,
\r
2923 weekCount: this.weekCount,
\r
2924 dayCount: this.dayCount
\r
2926 this.fireEvent('eventsrendered', this);
\r
2930 getDayEl: function(dt) {
\r
2931 return Ext.get(this.getDayId(dt));
\r
2935 getDayId: function(dt) {
\r
2936 if (Ext.isDate(dt)) {
\r
2937 dt = dt.format('Ymd');
\r
2939 return this.id + this.dayElIdDelimiter + dt;
\r
2943 getWeekIndex: function(dt) {
\r
2944 var el = this.getDayEl(dt).up('.ext-cal-wk-ct');
\r
2945 return parseInt(el.id.split('-wk-')[1], 10);
\r
2949 getDaySize: function(contentOnly) {
\r
2950 var box = this.el.getBox(),
\r
2951 w = box.width / this.dayCount,
\r
2952 h = box.height / this.getWeekCount();
\r
2954 if (contentOnly) {
\r
2955 var hd = this.el.select('.ext-cal-dtitle').first().parent('tr');
\r
2956 h = hd ? h - hd.getHeight(true) : h;
\r
2965 getEventHeight: function() {
\r
2966 if (!this.eventHeight) {
\r
2967 var evt = this.el.select('.ext-cal-evt').first();
\r
2968 this.eventHeight = evt ? evt.parent('tr').getHeight() : 18;
\r
2970 return this.eventHeight;
\r
2974 getMaxEventsPerDay: function() {
\r
2975 var dayHeight = this.getDaySize(true).height,
\r
2976 h = this.getEventHeight(),
\r
2977 max = Math.max(Math.floor((dayHeight - h) / h), 0);
\r
2983 getDayAt: function(x, y) {
\r
2984 var box = this.el.getBox(),
\r
2985 daySize = this.getDaySize(),
\r
2986 dayL = Math.floor(((x - box.x) / daySize.width)),
\r
2987 dayT = Math.floor(((y - box.y) / daySize.height)),
\r
2988 days = (dayT * 7) + dayL,
\r
2989 dt = this.viewStart.add(Date.DAY, days);
\r
2992 el: this.getDayEl(dt)
\r
2997 moveNext: function() {
\r
2998 return this.moveMonths(1);
\r
3002 movePrev: function() {
\r
3003 return this.moveMonths( - 1);
\r
3007 onInitDrag: function() {
\r
3008 Ext.calendar.MonthView.superclass.onInitDrag.call(this);
\r
3009 Ext.select(this.daySelector).removeClass(this.dayOverClass);
\r
3010 if (this.detailPanel) {
\r
3011 this.detailPanel.hide();
\r
3016 onMoreClick: function(dt) {
\r
3017 if (!this.detailPanel) {
\r
3018 this.detailPanel = new Ext.Panel({
\r
3019 id: this.id + '-details-panel',
\r
3020 title: dt.format('F j'),
\r
3023 renderTo: Ext.getBody(),
\r
3026 handler: function(e, t, p) {
\r
3031 xtype: 'monthdaydetailview',
\r
3032 id: this.id + '-details-view',
\r
3035 store: this.store,
\r
3037 'eventsrendered': this.onDetailViewUpdated.createDelegate(this)
\r
3043 this.detailPanel.setTitle(dt.format('F j'));
\r
3045 this.detailPanel.getComponent(this.id + '-details-view').update(dt);
\r
3049 onDetailViewUpdated: function(view, dt, numEvents) {
\r
3050 var p = this.detailPanel,
\r
3051 frameH = p.getFrameHeight(),
\r
3052 evtH = this.getEventHeight(),
\r
3053 bodyH = frameH + (numEvents * evtH) + 3,
\r
3054 dayEl = this.getDayEl(dt),
\r
3055 box = dayEl.getBox();
\r
3058 p.setHeight(bodyH);
\r
3059 p.setWidth(Math.max(box.width, 220));
\r
3061 p.getPositionEl().alignTo(dayEl, 't-t?');
\r
3065 onHide: function() {
\r
3066 Ext.calendar.MonthView.superclass.onHide.call(this);
\r
3067 if (this.detailPanel) {
\r
3068 this.detailPanel.hide();
\r
3073 onClick: function(e, t) {
\r
3074 if (this.detailPanel) {
\r
3075 this.detailPanel.hide();
\r
3077 if (Ext.calendar.MonthView.superclass.onClick.apply(this, arguments)) {
\r
3078 // The superclass handled the click already so exit
\r
3081 if (this.dropZone) {
\r
3082 this.dropZone.clearShims();
\r
3084 var el = e.getTarget(this.weekLinkSelector, 3),
\r
3088 dt = el.id.split(this.weekLinkIdDelimiter)[1];
\r
3089 this.fireEvent('weekclick', this, Date.parseDate(dt, 'Ymd'));
\r
3092 el = e.getTarget(this.moreSelector, 3);
\r
3094 dt = el.id.split(this.moreElIdDelimiter)[1];
\r
3095 this.onMoreClick(Date.parseDate(dt, 'Ymd'));
\r
3098 el = e.getTarget('td', 3);
\r
3100 if (el.id && el.id.indexOf(this.dayElIdDelimiter) > -1) {
\r
3101 parts = el.id.split(this.dayElIdDelimiter);
\r
3102 dt = parts[parts.length - 1];
\r
3104 this.fireEvent('dayclick', this, Date.parseDate(dt, 'Ymd'), false, Ext.get(this.getDayId(dt)));
\r
3111 handleDayMouseEvent: function(e, t, type) {
\r
3112 var el = e.getTarget(this.weekLinkSelector, 3, true);
\r
3114 el[type == 'over' ? 'addClass': 'removeClass'](this.weekLinkOverClass);
\r
3117 Ext.calendar.MonthView.superclass.handleDayMouseEvent.apply(this, arguments);
\r
3121 Ext.reg('monthview', Ext.calendar.MonthView);
\r
3123 * @class Ext.calendar.DayHeaderView
3124 * @extends Ext.calendar.MonthView
3125 * <p>This is the header area container within the day and week views where all-day events are displayed.
3126 * Normally you should not need to use this class directly -- instead you should use {@link Ext.calendar.DayView DayView}
3127 * which aggregates this class and the {@link Ext.calendar.DayBodyView DayBodyView} into the single unified view
3128 * presented by {@link Ext.calendar.CalendarPanel CalendarPanel}.</p>
3130 * @param {Object} config The config object
3132 Ext.calendar.DayHeaderView = Ext.extend(Ext.calendar.MonthView, {
3137 monitorResize: false,
3141 * Fires after the user clicks within the day view container and not on an event element
3142 * @param {Ext.calendar.DayBodyView} this
3143 * @param {Date} dt The date/time that was clicked on
3144 * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks within the
3145 * DayHeaderView always return true for this param.
3146 * @param {Ext.Element} el The Element that was clicked on
3150 afterRender: function() {
3152 this.tpl = new Ext.calendar.DayHeaderTemplate({
3154 showTodayText: this.showTodayText,
3155 todayText: this.todayText,
3156 showTime: this.showTime
3160 this.addClass('ext-cal-day-header');
3162 Ext.calendar.DayHeaderView.superclass.afterRender.call(this);
3166 forceSize: Ext.emptyFn,
3169 refresh: function() {
3170 Ext.calendar.DayHeaderView.superclass.refresh.call(this);
3171 this.recalcHeaderBox();
3175 recalcHeaderBox: function() {
3176 var tbl = this.el.child('.ext-cal-evt-tbl'),
3177 h = tbl.getHeight();
3179 this.el.setHeight(h + 7);
3181 if (Ext.isIE && Ext.isStrict) {
3182 this.el.child('.ext-cal-hd-ad-inner').setHeight(h + 4);
3185 //TODO: figure out why Opera refuses to refresh height when
3186 //the new height is lower than the previous one
3187 // var ct = this.el.child('.ext-cal-hd-ct');
3193 moveNext: function(noRefresh) {
3194 this.moveDays(this.dayCount, noRefresh);
3198 movePrev: function(noRefresh) {
3199 this.moveDays( - this.dayCount, noRefresh);
3203 onClick: function(e, t) {
3204 var el = e.getTarget('td', 3),
3208 if (el.id && el.id.indexOf(this.dayElIdDelimiter) > -1) {
3209 parts = el.id.split(this.dayElIdDelimiter);
3210 dt = parts[parts.length - 1];
3212 this.fireEvent('dayclick', this, Date.parseDate(dt, 'Ymd'), true, Ext.get(this.getDayId(dt)));
3216 Ext.calendar.DayHeaderView.superclass.onClick.apply(this, arguments);
3220 Ext.reg('dayheaderview', Ext.calendar.DayHeaderView);
3222 * @class Ext.calendar.DayBodyView
3223 * @extends Ext.calendar.CalendarView
3224 * <p>This is the scrolling container within the day and week views where non-all-day events are displayed.
3225 * Normally you should not need to use this class directly -- instead you should use {@link Ext.calendar.DayView DayView}
3226 * which aggregates this class and the {@link Ext.calendar.DayHeaderView DayHeaderView} into the single unified view
3227 * presented by {@link Ext.calendar.CalendarPanel CalendarPanel}.</p>
3229 * @param {Object} config The config object
3231 Ext.calendar.DayBodyView = Ext.extend(Ext.calendar.CalendarView, {
3233 dayColumnElIdDelimiter: '-day-col-',
3236 initComponent: function() {
3237 Ext.calendar.DayBodyView.superclass.initComponent.call(this);
3241 * @event eventresize
3242 * Fires after the user drags the resize handle of an event to resize it
3243 * @param {Ext.calendar.DayBodyView} this
3244 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was resized
3245 * containing the updated start and end dates
3250 * Fires after the user clicks within the day view container and not on an event element
3251 * @param {Ext.calendar.DayBodyView} this
3252 * @param {Date} dt The date/time that was clicked on
3253 * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks within the
3254 * DayBodyView always return false for this param.
3255 * @param {Ext.Element} el The Element that was clicked on
3262 initDD: function() {
3264 createText: this.ddCreateEventText,
3265 moveText: this.ddMoveEventText,
3266 resizeText: this.ddResizeEventText
3269 this.el.ddScrollConfig = {
3270 // scrolling is buggy in IE/Opera for some reason. A larger vthresh
3271 // makes it at least functional if not perfect
3272 vthresh: Ext.isIE || Ext.isOpera ? 100: 40,
3276 ddGroup: 'DayViewDD'
3278 this.dragZone = new Ext.calendar.DayViewDragZone(this.el, Ext.apply({
3280 containerScroll: true
3284 this.dropZone = new Ext.calendar.DayViewDropZone(this.el, Ext.apply({
3291 refresh: function() {
3292 var top = this.el.getScroll().top;
3294 this.renderTemplate();
3297 // skip this if the initial render scroll position has not yet been set.
3298 // necessary since IE/Opera must be deferred, so the first refresh will
3299 // override the initial position by default and always set it to 0.
3300 if (this.scrollReady) {
3306 * Scrolls the container to the specified vertical position. If the view is large enough that
3307 * there is no scroll overflow then this method will have no affect.
3308 * @param {Number} y The new vertical scroll position in pixels
3309 * @param {Boolean} defer (optional) <p>True to slightly defer the call, false to execute immediately.</p>
3310 * <p>This method will automatically defer itself for IE and Opera (even if you pass false) otherwise
3311 * the scroll position will not update in those browsers. You can optionally pass true, however, to
3312 * force the defer in all browsers, or use your own custom conditions to determine whether this is needed.</p>
3313 * <p>Note that this method should not generally need to be called directly as scroll position is managed internally.</p>
3315 scrollTo: function(y, defer) {
3316 defer = defer || (Ext.isIE || Ext.isOpera);
3319 this.el.scrollTo('top', y);
3320 this.scrollReady = true;
3324 this.el.scrollTo('top', y);
3325 this.scrollReady = true;
3330 afterRender: function() {
3332 this.tpl = new Ext.calendar.DayBodyTemplate({
3334 dayCount: this.dayCount,
3335 showTodayText: this.showTodayText,
3336 todayText: this.todayText,
3337 showTime: this.showTime
3342 this.addClass('ext-cal-body-ct');
3344 Ext.calendar.DayBodyView.superclass.afterRender.call(this);
3346 // default scroll position to 7am:
3347 this.scrollTo(7 * 42);
3351 forceSize: Ext.emptyFn,
3354 onEventResize: function(rec, data) {
3355 var D = Ext.calendar.Date,
3356 start = Ext.calendar.EventMappings.StartDate.name,
3357 end = Ext.calendar.EventMappings.EndDate.name;
3359 if (D.compare(rec.data[start], data.StartDate) === 0 &&
3360 D.compare(rec.data[end], data.EndDate) === 0) {
3364 rec.set(start, data.StartDate);
3365 rec.set(end, data.EndDate);
3367 this.fireEvent('eventresize', this, rec);
3371 getEventBodyMarkup: function() {
3372 if (!this.eventBodyMarkup) {
3373 this.eventBodyMarkup = ['{Title}',
3374 '<tpl if="_isReminder">',
3375 '<i class="ext-cal-ic ext-cal-ic-rem"> </i>',
3377 '<tpl if="_isRecurring">',
3378 '<i class="ext-cal-ic ext-cal-ic-rcr"> </i>',
3380 // '<tpl if="spanLeft">',
3381 // '<i class="ext-cal-spl"> </i>',
3383 // '<tpl if="spanRight">',
3384 // '<i class="ext-cal-spr"> </i>',
3388 return this.eventBodyMarkup;
3392 getEventTemplate: function() {
3393 if (!this.eventTpl) {
3394 this.eventTpl = !(Ext.isIE || Ext.isOpera) ?
3396 '<div id="{_elId}" class="{_selectorCls} {_colorCls} ext-cal-evt ext-cal-evr" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
3397 '<div class="ext-evt-bd">', this.getEventBodyMarkup(), '</div>',
3398 '<div class="ext-evt-rsz"><div class="ext-evt-rsz-h"> </div></div>',
3401 : new Ext.XTemplate(
3402 '<div id="{_elId}" class="ext-cal-evt {_selectorCls} {_colorCls}-x" style="left: {_left}%; width: {_width}%; top: {_top}px;">',
3403 '<div class="ext-cal-evb"> </div>',
3404 '<dl style="height: {_height}px;" class="ext-cal-evdm">',
3405 '<dd class="ext-evt-bd">',
3406 this.getEventBodyMarkup(),
3408 '<div class="ext-evt-rsz"><div class="ext-evt-rsz-h"> </div></div>',
3410 '<div class="ext-cal-evb"> </div>',
3413 this.eventTpl.compile();
3415 return this.eventTpl;
3419 * <p>Returns the XTemplate that is bound to the calendar's event store (it expects records of type
3420 * {@link Ext.calendar.EventRecord}) to populate the calendar views with <strong>all-day</strong> events.
3421 * Internally this method by default generates different markup for browsers that support CSS border radius
3422 * and those that don't. This method can be overridden as needed to customize the markup generated.</p>
3423 * <p>Note that this method calls {@link #getEventBodyMarkup} to retrieve the body markup for events separately
3424 * from the surrounding container markup. This provdes the flexibility to customize what's in the body without
3425 * having to override the entire XTemplate. If you do override this method, you should make sure that your
3426 * overridden version also does the same.</p>
3427 * @return {Ext.XTemplate} The event XTemplate
3429 getEventAllDayTemplate: function() {
3430 if (!this.eventAllDayTpl) {
3432 body = this.getEventBodyMarkup();
3434 tpl = !(Ext.isIE || Ext.isOpera) ?
3436 '<div id="{_elId}" class="{_selectorCls} {_colorCls} {values.spanCls} ext-cal-evt ext-cal-evr" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
3440 : new Ext.XTemplate(
3441 '<div id="{_elId}" class="ext-cal-evt" style="left: {_left}%; width: {_width}%; top: {_top}px; height: {_height}px;">',
3442 '<div class="{_selectorCls} {values.spanCls} {_colorCls} ext-cal-evo">',
3443 '<div class="ext-cal-evm">',
3444 '<div class="ext-cal-evi">',
3451 this.eventAllDayTpl = tpl;
3453 return this.eventAllDayTpl;
3457 getTemplateEventData: function(evt) {
3458 var selector = this.getEventSelectorCls(evt[Ext.calendar.EventMappings.EventId.name]),
3460 M = Ext.calendar.EventMappings;
3462 this.getTemplateEventBox(evt);
3464 data._selectorCls = selector;
3465 data._colorCls = 'ext-color-' + evt[M.CalendarId.name] + (evt._renderAsAllDay ? '-ad': '');
3466 data._elId = selector + (evt._weekIndex ? '-' + evt._weekIndex: '');
3467 data._isRecurring = evt.Recurrence && evt.Recurrence != '';
3468 data._isReminder = evt[M.Reminder.name] && evt[M.Reminder.name] != '';
3469 var title = evt[M.Title.name];
3470 data.Title = (evt[M.IsAllDay.name] ? '': evt[M.StartDate.name].format('g:ia ')) + (!title || title.length == 0 ? '(No title)': title);
3472 return Ext.applyIf(data, evt);
3476 getTemplateEventBox: function(evt) {
3477 var heightFactor = 0.7,
3478 start = evt[Ext.calendar.EventMappings.StartDate.name],
3479 end = evt[Ext.calendar.EventMappings.EndDate.name],
3480 startMins = start.getHours() * 60 + start.getMinutes(),
3481 endMins = end.getHours() * 60 + end.getMinutes(),
3482 diffMins = endMins - startMins;
3486 evt._top = Math.round(startMins * heightFactor) + 1;
3487 evt._height = Math.max((diffMins * heightFactor) - 2, 15);
3491 renderItems: function() {
3507 for (; day < this.dayCount; day++) {
3508 ev = emptyCells = skipped = 0;
3509 d = this.eventGrid[0][day];
3510 ct = d ? d.length: 0;
3512 for (; ev < ct; ev++) {
3517 item = evt.data || evt.event.data;
3518 if (item._renderAsAllDay) {
3526 data: this.getTemplateEventData(item),
3527 date: this.viewStart.add(Date.DAY, day)
3532 // overlapping event pre-processing loop
3533 i = j = overlapCols = prevCol = 0;
3535 for (; i < l; i++) {
3538 prevCol = overlapCols;
3539 for (j = 0; j < l; j++) {
3543 evt2 = evts[j].data;
3544 if (this.isOverlapping(evt, evt2)) {
3545 evt._overlap = evt._overlap == undefined ? 1: evt._overlap + 1;
3547 if (evt._overcol === undefined) {
3550 evt2._overcol = evt._overcol + 1;
3551 overlapCols = Math.max(overlapCols, evt2._overcol);
3558 for (i = 0; i < l; i++) {
3560 if (evt._overlap !== undefined) {
3561 colWidth = 100 / (overlapCols + 1);
3562 evtWidth = 100 - (colWidth * evt._overlap);
3564 evt._width = colWidth;
3565 evt._left = colWidth * evt._overcol;
3567 markup = this.getEventTemplate().apply(evt);
3568 target = this.id + '-day-col-' + evts[i].date.format('Ymd');
3570 Ext.DomHelper.append(target, markup);
3573 this.fireEvent('eventsrendered', this);
3577 getDayEl: function(dt) {
3578 return Ext.get(this.getDayId(dt));
3582 getDayId: function(dt) {
3583 if (Ext.isDate(dt)) {
3584 dt = dt.format('Ymd');
3586 return this.id + this.dayColumnElIdDelimiter + dt;
3590 getDaySize: function() {
3591 var box = this.el.child('.ext-cal-day-col-inner').getBox();
3599 getDayAt: function(x, y) {
3600 var sel = '.ext-cal-body-ct',
3601 xoffset = this.el.child('.ext-cal-day-times').getWidth(),
3602 viewBox = this.el.getBox(),
3603 daySize = this.getDaySize(false),
3604 relX = x - viewBox.x - xoffset,
3605 dayIndex = Math.floor(relX / daySize.width),
3606 // clicked col index
3607 scroll = this.el.getScroll(),
3608 row = this.el.child('.ext-cal-bg-row'),
3609 // first avail row, just to calc size
3610 rowH = row.getHeight() / 2,
3611 // 30 minute increment since a row is 60 minutes
3612 relY = y - viewBox.y - rowH + scroll.top,
3613 rowIndex = Math.max(0, Math.ceil(relY / rowH)),
3614 mins = rowIndex * 30,
3615 dt = this.viewStart.add(Date.DAY, dayIndex).add(Date.MINUTE, mins),
3616 el = this.getDayEl(dt),
3620 timeX = el.getLeft();
3626 // this is the box for the specific time block in the day that was clicked on:
3629 y: (rowIndex * 21) + viewBox.y - scroll.top,
3630 width: daySize.width,
3637 onClick: function(e, t) {
3638 if (this.dragPending || Ext.calendar.DayBodyView.superclass.onClick.apply(this, arguments)) {
3639 // The superclass handled the click already so exit
3642 if (e.getTarget('.ext-cal-day-times', 3) !== null) {
3643 // ignore clicks on the times-of-day gutter
3646 var el = e.getTarget('td', 3);
3648 if (el.id && el.id.indexOf(this.dayElIdDelimiter) > -1) {
3649 var dt = this.getDateFromId(el.id, this.dayElIdDelimiter);
3650 this.fireEvent('dayclick', this, Date.parseDate(dt, 'Ymd'), true, Ext.get(this.getDayId(dt, true)));
3654 var day = this.getDayAt(e.xy[0], e.xy[1]);
3655 if (day && day.date) {
3656 this.fireEvent('dayclick', this, day.date, false, null);
3661 Ext.reg('daybodyview', Ext.calendar.DayBodyView);
3663 * @class Ext.calendar.DayView
3664 * @extends Ext.Container
3665 * <p>Unlike other calendar views, is not actually a subclass of {@link Ext.calendar.CalendarView CalendarView}.
3666 * Instead it is a {@link Ext.Container Container} subclass that internally creates and manages the layouts of
3667 * a {@link Ext.calendar.DayHeaderView DayHeaderView} and a {@link Ext.calendar.DayBodyView DayBodyView}. As such
3668 * DayView accepts any config values that are valid for DayHeaderView and DayBodyView and passes those through
3669 * to the contained views. It also supports the interface required of any calendar view and in turn calls methods
3670 * on the contained views as necessary.</p>
3672 * @param {Object} config The config object
3674 Ext.calendar.DayView = Ext.extend(Ext.Container, {
3676 * @cfg {Boolean} showTime
3677 * True to display the current time in today's box in the calendar, false to not display it (defautls to true)
3681 * @cfg {Boolean} showTodayText
3682 * True to display the {@link #todayText} string in today's box in the calendar, false to not display it (defautls to true)
3684 showTodayText: true,
3686 * @cfg {String} todayText
3687 * The text to display in the current day's box in the calendar when {@link #showTodayText} is true (defaults to 'Today')
3691 * @cfg {String} ddCreateEventText
3692 * The text to display inside the drag proxy while dragging over the calendar to create a new event (defaults to
3693 * 'Create event for {0}' where {0} is a date range supplied by the view)
3695 ddCreateEventText: 'Create event for {0}',
3697 * @cfg {String} ddMoveEventText
3698 * The text to display inside the drag proxy while dragging an event to reposition it (defaults to
3699 * 'Move event to {0}' where {0} is the updated event start date/time supplied by the view)
3701 ddMoveEventText: 'Move event to {0}',
3703 * @cfg {Number} dayCount
3704 * The number of days to display in the view (defaults to 1)
3709 initComponent : function(){
3710 // rendering more than 7 days per view is not supported
3711 this.dayCount = this.dayCount > 7 ? 7 : this.dayCount;
3713 var cfg = Ext.apply({}, this.initialConfig);
3714 cfg.showTime = this.showTime;
3715 cfg.showTodatText = this.showTodayText;
3716 cfg.todayText = this.todayText;
3717 cfg.dayCount = this.dayCount;
3720 var header = Ext.applyIf({
3721 xtype: 'dayheaderview',
3725 var body = Ext.applyIf({
3726 xtype: 'daybodyview',
3730 this.items = [header, body];
3731 this.addClass('ext-cal-dayview ext-cal-ct');
3733 Ext.calendar.DayView.superclass.initComponent.call(this);
3737 afterRender : function(){
3738 Ext.calendar.DayView.superclass.afterRender.call(this);
3740 this.header = Ext.getCmp(this.id+'-hd');
3741 this.body = Ext.getCmp(this.id+'-bd');
3742 this.body.on('eventsrendered', this.forceSize, this);
3746 refresh : function(){
3747 this.header.refresh();
3748 this.body.refresh();
3752 forceSize: function(){
3753 // The defer call is mainly for good ol' IE, but it doesn't hurt in
3754 // general to make sure that the window resize is good and done first
3755 // so that we can properly calculate sizes.
3757 var ct = this.el.up('.x-panel-body'),
3758 hd = this.el.child('.ext-cal-day-header'),
3759 h = ct.getHeight() - hd.getHeight();
3761 this.el.child('.ext-cal-body-ct').setHeight(h);
3766 onResize : function(){
3771 getViewBounds : function(){
3772 return this.header.getViewBounds();
3776 * Returns the start date of the view, as set by {@link #setStartDate}. Note that this may not
3777 * be the first date displayed in the rendered calendar -- to get the start and end dates displayed
3778 * to the user use {@link #getViewBounds}.
3779 * @return {Date} The start date
3781 getStartDate : function(){
3782 return this.header.getStartDate();
3786 * Sets the start date used to calculate the view boundaries to display. The displayed view will be the
3787 * earliest and latest dates that match the view requirements and contain the date passed to this function.
3788 * @param {Date} dt The date used to calculate the new view boundaries
3790 setStartDate: function(dt){
3791 this.header.setStartDate(dt, true);
3792 this.body.setStartDate(dt, true);
3796 renderItems: function(){
3797 this.header.renderItems();
3798 this.body.renderItems();
3802 * Returns true if the view is currently displaying today's date, else false.
3803 * @return {Boolean} True or false
3805 isToday : function(){
3806 return this.header.isToday();
3810 * Updates the view to contain the passed date
3811 * @param {Date} dt The date to display
3813 moveTo : function(dt, noRefresh){
3814 this.header.moveTo(dt, noRefresh);
3815 this.body.moveTo(dt, noRefresh);
3819 * Updates the view to the next consecutive date(s)
3821 moveNext : function(noRefresh){
3822 this.header.moveNext(noRefresh);
3823 this.body.moveNext(noRefresh);
3827 * Updates the view to the previous consecutive date(s)
3829 movePrev : function(noRefresh){
3830 this.header.movePrev(noRefresh);
3831 this.body.movePrev(noRefresh);
3835 * Shifts the view by the passed number of days relative to the currently set date
3836 * @param {Number} value The number of days (positive or negative) by which to shift the view
3838 moveDays : function(value, noRefresh){
3839 this.header.moveDays(value, noRefresh);
3840 this.body.moveDays(value, noRefresh);
3844 * Updates the view to show today
3846 moveToday : function(noRefresh){
3847 this.header.moveToday(noRefresh);
3848 this.body.moveToday(noRefresh);
3852 Ext.reg('dayview', Ext.calendar.DayView);
3854 * @class Ext.calendar.WeekView
3855 * @extends Ext.calendar.DayView
3856 * <p>Displays a calendar view by week. This class does not usually need ot be used directly as you can
3857 * use a {@link Ext.calendar.CalendarPanel CalendarPanel} to manage multiple calendar views at once including
3858 * the week view.</p>
3860 * @param {Object} config The config object
3862 Ext.calendar.WeekView = Ext.extend(Ext.calendar.DayView, {
3864 * @cfg {Number} dayCount
3865 * The number of days to display in the view (defaults to 7)
3870 Ext.reg('weekview', Ext.calendar.WeekView);/**
3871 * @class Ext.calendar.DateRangeField
3872 * @extends Ext.form.Field
3873 * <p>A combination field that includes start and end dates and times, as well as an optional all-day checkbox.</p>
3875 * @param {Object} config The config object
3877 Ext.calendar.DateRangeField = Ext.extend(Ext.form.Field, {
3879 * @cfg {String} toText
3880 * The text to display in between the date/time fields (defaults to 'to')
3884 * @cfg {String} toText
3885 * The text to display as the label for the all day checkbox (defaults to 'All day')
3887 allDayText: 'All day',
3890 onRender: function(ct, position) {
3892 this.startDate = new Ext.form.DateField({
3893 id: this.id + '-start-date',
3899 this.checkDates('date', 'start');
3905 this.startTime = new Ext.form.TimeField({
3906 id: this.id + '-start-time',
3907 hidden: this.showTimes === false,
3914 this.checkDates('time', 'start');
3920 this.endTime = new Ext.form.TimeField({
3921 id: this.id + '-end-time',
3922 hidden: this.showTimes === false,
3929 this.checkDates('time', 'end');
3935 this.endDate = new Ext.form.DateField({
3936 id: this.id + '-end-date',
3943 this.checkDates('date', 'end');
3949 this.allDay = new Ext.form.Checkbox({
3950 id: this.id + '-allday',
3951 hidden: this.showTimes === false || this.showAllDay === false,
3952 boxLabel: this.allDayText,
3953 handler: function(chk, checked) {
3954 this.startTime.setVisible(!checked);
3955 this.endTime.setVisible(!checked);
3959 this.toLabel = new Ext.form.Label({
3961 id: this.id + '-to-label',
3965 this.fieldCt = new Ext.Container({
3969 //make sure the container el has the field's id
3970 cls: 'ext-dt-range',
3989 this.fieldCt.ownerCt = this;
3990 this.el = this.fieldCt.getEl();
3991 this.items = new Ext.util.MixedCollection();
3992 this.items.addAll([this.startDate, this.endDate, this.toLabel, this.startTime, this.endTime, this.allDay]);
3994 Ext.calendar.DateRangeField.superclass.onRender.call(this, ct, position);
3998 checkDates: function(type, startend) {
3999 var startField = Ext.getCmp(this.id + '-start-' + type),
4000 endField = Ext.getCmp(this.id + '-end-' + type),
4001 startValue = this.getDT('start'),
4002 endValue = this.getDT('end');
4004 if (startValue > endValue) {
4005 if (startend == 'start') {
4006 endField.setValue(startValue);
4008 startField.setValue(endValue);
4009 this.checkDates(type, 'start');
4012 if (type == 'date') {
4013 this.checkDates('time', startend);
4018 * Returns an array containing the following values in order:<div class="mdetail-params"><ul>
4019 * <li><b><code>DateTime</code></b> : <div class="sub-desc">The start date/time</div></li>
4020 * <li><b><code>DateTime</code></b> : <div class="sub-desc">The end date/time</div></li>
4021 * <li><b><code>Boolean</code></b> : <div class="sub-desc">True if the dates are all-day, false
4022 * if the time values should be used</div></li><ul></div>
4023 * @return {Array} The array of return values
4025 getValue: function() {
4027 this.getDT('start'),
4029 this.allDay.getValue()
4033 // private getValue helper
4034 getDT: function(startend) {
4035 var time = this[startend + 'Time'].getValue(),
4036 dt = this[startend + 'Date'].getValue();
4038 if (Ext.isDate(dt)) {
4039 dt = dt.format(this[startend + 'Date'].format);
4044 if (time != '' && this[startend + 'Time'].isVisible()) {
4045 return Date.parseDate(dt + ' ' + time, this[startend + 'Date'].format + ' ' + this[startend + 'Time'].format);
4047 return Date.parseDate(dt, this[startend + 'Date'].format);
4052 * Sets the values to use in the date range.
4053 * @param {Array/Date/Object} v The value(s) to set into the field. Valid types are as follows:<div class="mdetail-params"><ul>
4054 * <li><b><code>Array</code></b> : <div class="sub-desc">An array containing, in order, a start date, end date and all-day flag.
4055 * This array should exactly match the return type as specified by {@link #getValue}.</div></li>
4056 * <li><b><code>DateTime</code></b> : <div class="sub-desc">A single Date object, which will be used for both the start and
4057 * end dates in the range. The all-day flag will be defaulted to false.</div></li>
4058 * <li><b><code>Object</code></b> : <div class="sub-desc">An object containing properties for StartDate, EndDate and IsAllDay
4059 * as defined in {@link Ext.calendar.EventMappings}.</div></li><ul></div>
4061 setValue: function(v) {
4062 if (Ext.isArray(v)) {
4063 this.setDT(v[0], 'start');
4064 this.setDT(v[1], 'end');
4065 this.allDay.setValue( !! v[2]);
4067 else if (Ext.isDate(v)) {
4068 this.setDT(v, 'start');
4069 this.setDT(v, 'end');
4070 this.allDay.setValue(false);
4072 else if (v[Ext.calendar.EventMappings.StartDate.name]) {
4074 this.setDT(v[Ext.calendar.EventMappings.StartDate.name], 'start');
4075 if (!this.setDT(v[Ext.calendar.EventMappings.EndDate.name], 'end')) {
4076 this.setDT(v[Ext.calendar.EventMappings.StartDate.name], 'end');
4078 this.allDay.setValue( !! v[Ext.calendar.EventMappings.IsAllDay.name]);
4082 // private setValue helper
4083 setDT: function(dt, startend) {
4084 if (dt && Ext.isDate(dt)) {
4085 this[startend + 'Date'].setValue(dt);
4086 this[startend + 'Time'].setValue(dt.format(this[startend + 'Time'].format));
4092 isDirty: function() {
4094 if (this.rendered && !this.disabled) {
4095 this.items.each(function(item) {
4096 if (item.isDirty()) {
4106 onDisable: function() {
4107 this.delegateFn('disable');
4111 onEnable: function() {
4112 this.delegateFn('enable');
4117 this.delegateFn('reset');
4121 delegateFn: function(fn) {
4122 this.items.each(function(item) {
4130 beforeDestroy: function() {
4131 Ext.destroy(this.fieldCt);
4132 Ext.calendar.DateRangeField.superclass.beforeDestroy.call(this);
4136 * @method getRawValue
4139 getRawValue: Ext.emptyFn,
4141 * @method setRawValue
4144 setRawValue: Ext.emptyFn
4147 Ext.reg('daterangefield', Ext.calendar.DateRangeField);
4149 * @class Ext.calendar.ReminderField
4150 * @extends Ext.form.ComboBox
4151 * <p>A custom combo used for choosing a reminder setting for an event.</p>
4152 * <p>This is pretty much a standard combo that is simply pre-configured for the options needed by the
4153 * calendar components. The default configs are as follows:<pre><code>
4155 fieldLabel: 'Reminder',
4157 triggerAction: 'all',
4158 forceSelection: true,
4159 displayField: 'desc',
4163 * @param {Object} config The config object
4165 Ext.calendar.ReminderField = Ext.extend(Ext.form.ComboBox, {
4167 fieldLabel: 'Reminder',
4169 triggerAction: 'all',
4170 forceSelection: true,
4171 displayField: 'desc',
4172 valueField: 'value',
4175 initComponent: function() {
4176 Ext.calendar.ReminderField.superclass.initComponent.call(this);
4178 this.store = this.store || new Ext.data.ArrayStore({
4179 fields: ['value', 'desc'],
4183 ['0', 'At start time'],
4184 ['5', '5 minutes before start'],
4185 ['15', '15 minutes before start'],
4186 ['30', '30 minutes before start'],
4187 ['60', '1 hour before start'],
4188 ['90', '1.5 hours before start'],
4189 ['120', '2 hours before start'],
4190 ['180', '3 hours before start'],
4191 ['360', '6 hours before start'],
4192 ['720', '12 hours before start'],
4193 ['1440', '1 day before start'],
4194 ['2880', '2 days before start'],
4195 ['4320', '3 days before start'],
4196 ['5760', '4 days before start'],
4197 ['7200', '5 days before start'],
4198 ['10080', '1 week before start'],
4199 ['20160', '2 weeks before start']
4205 initValue: function() {
4206 if (this.value !== undefined) {
4207 this.setValue(this.value);
4212 this.originalValue = this.getValue();
4216 Ext.reg('reminderfield', Ext.calendar.ReminderField);
4218 * @class Ext.calendar.EventEditForm
4219 * @extends Ext.form.FormPanel
4220 * <p>A custom form used for detailed editing of events.</p>
4221 * <p>This is pretty much a standard form that is simply pre-configured for the options needed by the
4222 * calendar components. It is also configured to automatically bind records of type {@link Ext.calendar.EventRecord}
4223 * to and from the form.</p>
4224 * <p>This form also provides custom events specific to the calendar so that other calendar components can be easily
4225 * notified when an event has been edited via this component.</p>
4226 * <p>The default configs are as follows:</p><pre><code>
4228 title: 'Event Form',
4229 titleTextAdd: 'Add Event',
4230 titleTextEdit: 'Edit Event',
4231 bodyStyle: 'background:transparent;padding:20px 20px 10px;',
4233 buttonAlign: 'center',
4235 cls: 'ext-evt-edit-form',
4238 * @param {Object} config The config object
4240 Ext.calendar.EventEditForm = Ext.extend(Ext.form.FormPanel, {
4242 title: 'Event Form',
4243 titleTextAdd: 'Add Event',
4244 titleTextEdit: 'Edit Event',
4245 bodyStyle: 'background:transparent;padding:20px 20px 10px;',
4247 buttonAlign: 'center',
4249 // to allow for the notes field to autogrow
4250 cls: 'ext-evt-edit-form',
4252 // private properties:
4257 initComponent: function() {
4262 * Fires after a new event is added
4263 * @param {Ext.calendar.EventEditForm} this
4264 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was added
4268 * @event eventupdate
4269 * Fires after an existing event is updated
4270 * @param {Ext.calendar.EventEditForm} this
4271 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was updated
4275 * @event eventdelete
4276 * Fires after an event is deleted
4277 * @param {Ext.calendar.EventEditForm} this
4278 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was deleted
4282 * @event eventcancel
4283 * Fires after an event add/edit operation is canceled by the user and no store update took place
4284 * @param {Ext.calendar.EventEditForm} this
4285 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was canceled
4290 this.titleField = new Ext.form.TextField({
4291 fieldLabel: 'Title',
4292 name: Ext.calendar.EventMappings.Title.name,
4295 this.dateRangeField = new Ext.calendar.DateRangeField({
4299 this.reminderField = new Ext.calendar.ReminderField({
4302 this.notesField = new Ext.form.TextArea({
4303 fieldLabel: 'Notes',
4304 name: Ext.calendar.EventMappings.Notes.name,
4309 this.locationField = new Ext.form.TextField({
4310 fieldLabel: 'Location',
4311 name: Ext.calendar.EventMappings.Location.name,
4314 this.urlField = new Ext.form.TextField({
4315 fieldLabel: 'Web Link',
4316 name: Ext.calendar.EventMappings.Url.name,
4320 var leftFields = [this.titleField, this.dateRangeField, this.reminderField],
4321 rightFields = [this.notesField, this.locationField, this.urlField];
4323 if (this.calendarStore) {
4324 this.calendarField = new Ext.calendar.CalendarPicker({
4325 store: this.calendarStore,
4326 name: Ext.calendar.EventMappings.CalendarId.name
4328 leftFields.splice(2, 0, this.calendarField);
4349 handler: this.onSave
4355 handler: this.onDelete
4360 handler: this.onCancel
4363 Ext.calendar.EventEditForm.superclass.initComponent.call(this);
4367 loadRecord: function(rec) {
4368 this.form.loadRecord.apply(this.form, arguments);
4369 this.activeRecord = rec;
4370 this.dateRangeField.setValue(rec.data);
4371 if (this.calendarStore) {
4372 this.form.setValues({
4373 'calendar': rec.data[Ext.calendar.EventMappings.CalendarId.name]
4376 this.isAdd = !!rec.data[Ext.calendar.EventMappings.IsNew.name];
4379 this.setTitle(this.titleTextAdd);
4380 Ext.select('.ext-del-btn').setDisplayed(false);
4383 this.setTitle(this.titleTextEdit);
4384 Ext.select('.ext-del-btn').setDisplayed(true);
4386 this.titleField.focus();
4390 updateRecord: function() {
4391 var dates = this.dateRangeField.getValue();
4393 this.form.updateRecord(this.activeRecord);
4394 this.activeRecord.set(Ext.calendar.EventMappings.StartDate.name, dates[0]);
4395 this.activeRecord.set(Ext.calendar.EventMappings.EndDate.name, dates[1]);
4396 this.activeRecord.set(Ext.calendar.EventMappings.IsAllDay.name, dates[2]);
4400 onCancel: function() {
4402 this.fireEvent('eventcancel', this, this.activeRecord);
4406 cleanup: function(hide) {
4407 if (this.activeRecord && this.activeRecord.dirty) {
4408 this.activeRecord.reject();
4410 delete this.activeRecord;
4412 if (this.form.isDirty()) {
4418 onSave: function() {
4419 if (!this.form.isValid()) {
4422 this.updateRecord();
4424 if (!this.activeRecord.dirty) {
4429 this.fireEvent(this.isAdd ? 'eventadd': 'eventupdate', this, this.activeRecord);
4433 onDelete: function() {
4434 this.fireEvent('eventdelete', this, this.activeRecord);
4438 Ext.reg('eventeditform', Ext.calendar.EventEditForm);
4440 * @class Ext.calendar.EventEditWindow
4441 * @extends Ext.Window
4442 * <p>A custom window containing a basic edit form used for quick editing of events.</p>
4443 * <p>This window also provides custom events specific to the calendar so that other calendar components can be easily
4444 * notified when an event has been edited via this component.</p>
4446 * @param {Object} config The config object
4448 Ext.calendar.EventEditWindow = function(config) {
4449 var formPanelCfg = {
4453 bodyStyle: 'background:transparent;padding:5px 10px 10px;',
4458 name: Ext.calendar.EventMappings.Title.name,
4459 fieldLabel: 'Title',
4464 xtype: 'daterangefield',
4471 if (config.calendarStore) {
4472 this.calendarStore = config.calendarStore;
4473 delete config.calendarStore;
4475 formPanelCfg.items.push({
4476 xtype: 'calendarpicker',
4480 store: this.calendarStore
4484 Ext.calendar.EventEditWindow.superclass.constructor.call(this, Ext.apply({
4485 titleTextAdd: 'Add Event',
4486 titleTextEdit: 'Edit Event',
4490 closeAction: 'hide',
4493 buttonAlign: 'left',
4494 savingMessage: 'Saving changes...',
4495 deletingMessage: 'Deleting event...',
4499 text: '<a href="#" id="tblink">Edit Details...</a>'
4504 handler: this.onSave,
4511 handler: this.onDelete,
4518 handler: this.onCancel,
4526 Ext.extend(Ext.calendar.EventEditWindow, Ext.Window, {
4531 initComponent: function() {
4532 Ext.calendar.EventEditWindow.superclass.initComponent.call(this);
4534 this.formPanel = this.items.items[0];
4539 * Fires after a new event is added
4540 * @param {Ext.calendar.EventEditWindow} this
4541 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was added
4545 * @event eventupdate
4546 * Fires after an existing event is updated
4547 * @param {Ext.calendar.EventEditWindow} this
4548 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was updated
4552 * @event eventdelete
4553 * Fires after an event is deleted
4554 * @param {Ext.calendar.EventEditWindow} this
4555 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was deleted
4559 * @event eventcancel
4560 * Fires after an event add/edit operation is canceled by the user and no store update took place
4561 * @param {Ext.calendar.EventEditWindow} this
4562 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was canceled
4566 * @event editdetails
4567 * Fires when the user selects the option in this window to continue editing in the detailed edit form
4568 * (by default, an instance of {@link Ext.calendar.EventEditForm}. Handling code should hide this window
4569 * and transfer the current event record to the appropriate instance of the detailed form by showing it
4570 * and calling {@link Ext.calendar.EventEditForm#loadRecord loadRecord}.
4571 * @param {Ext.calendar.EventEditWindow} this
4572 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} that is currently being edited
4579 afterRender: function() {
4580 Ext.calendar.EventEditWindow.superclass.afterRender.call(this);
4582 this.el.addClass('ext-cal-event-win');
4584 Ext.get('tblink').on('click',
4587 this.updateRecord();
4588 this.fireEvent('editdetails', this, this.activeRecord);
4594 * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.
4595 * @param {Ext.data.Record/Object} o Either a {@link Ext.data.Record} if showing the form
4596 * for an existing event in edit mode, or a plain object containing a StartDate property (and
4597 * optionally an EndDate property) for showing the form in add mode.
4598 * @param {String/Element} animateTarget (optional) The target element or id from which the window should
4599 * animate while opening (defaults to null with no animation)
4600 * @return {Ext.Window} this
4602 show: function(o, animateTarget) {
4603 // Work around the CSS day cell height hack needed for initial render in IE8/strict:
4604 var anim = (Ext.isIE8 && Ext.isStrict) ? null: animateTarget;
4606 Ext.calendar.EventEditWindow.superclass.show.call(this, anim,
4608 Ext.getCmp('title').focus(false, 100);
4610 Ext.getCmp('delete-btn')[o.data && o.data[Ext.calendar.EventMappings.EventId.name] ? 'show': 'hide']();
4613 f = this.formPanel.form;
4617 this.isAdd = !!rec.data[Ext.calendar.EventMappings.IsNew.name];
4619 // Enable adding the default record that was passed in
4620 // if it's new even if the user makes no changes
4622 this.setTitle(this.titleTextAdd);
4625 this.setTitle(this.titleTextEdit);
4632 this.setTitle(this.titleTextAdd);
4634 var M = Ext.calendar.EventMappings,
4635 eventId = M.EventId.name,
4636 start = o[M.StartDate.name],
4637 end = o[M.EndDate.name] || start.add('h', 1);
4639 rec = new Ext.calendar.EventRecord();
4640 rec.data[M.EventId.name] = this.newId++;
4641 rec.data[M.StartDate.name] = start;
4642 rec.data[M.EndDate.name] = end;
4643 rec.data[M.IsAllDay.name] = !!o[M.IsAllDay.name] || start.getDate() != end.clone().add(Date.MILLI, 1).getDate();
4644 rec.data[M.IsNew.name] = true;
4650 if (this.calendarStore) {
4651 Ext.getCmp('calendar').setValue(rec.data[Ext.calendar.EventMappings.CalendarId.name]);
4653 Ext.getCmp('date-range').setValue(rec.data);
4654 this.activeRecord = rec;
4660 roundTime: function(dt, incr) {
4662 var m = parseInt(dt.getMinutes(), 10);
4663 return dt.add('mi', incr - (m % incr));
4667 onCancel: function() {
4669 this.fireEvent('eventcancel', this);
4673 cleanup: function(hide) {
4674 if (this.activeRecord && this.activeRecord.dirty) {
4675 this.activeRecord.reject();
4677 delete this.activeRecord;
4679 if (hide === true) {
4680 // Work around the CSS day cell height hack needed for initial render in IE8/strict:
4681 //var anim = afterDelete || (Ext.isIE8 && Ext.isStrict) ? null : this.animateTarget;
4687 updateRecord: function() {
4688 var f = this.formPanel.form,
4689 dates = Ext.getCmp('date-range').getValue(),
4690 M = Ext.calendar.EventMappings;
4692 f.updateRecord(this.activeRecord);
4693 this.activeRecord.set(M.StartDate.name, dates[0]);
4694 this.activeRecord.set(M.EndDate.name, dates[1]);
4695 this.activeRecord.set(M.IsAllDay.name, dates[2]);
4696 this.activeRecord.set(M.CalendarId.name, this.formPanel.form.findField('calendar').getValue());
4700 onSave: function() {
4701 if (!this.formPanel.form.isValid()) {
4704 this.updateRecord();
4706 if (!this.activeRecord.dirty) {
4711 this.fireEvent(this.isAdd ? 'eventadd': 'eventupdate', this, this.activeRecord);
4715 onDelete: function() {
4716 this.fireEvent('eventdelete', this, this.activeRecord);
4719 * @class Ext.calendar.CalendarPanel
4720 * @extends Ext.Panel
4721 * <p>This is the default container for Ext calendar views. It supports day, week and month views as well
4722 * as a built-in event edit form. The only requirement for displaying a calendar is passing in a valid
4723 * {@link #calendarStore} config containing records of type {@link Ext.calendar.EventRecord EventRecord}. In order
4724 * to make the calendar interactive (enable editing, drag/drop, etc.) you can handle any of the various
4725 * events fired by the underlying views and exposed through the CalendarPanel.</p>
4726 * {@link #layoutConfig} option if needed.</p>
4728 * @param {Object} config The config object
4729 * @xtype calendarpanel
4731 Ext.calendar.CalendarPanel = Ext.extend(Ext.Panel, {
4733 * @cfg {Boolean} showDayView
4734 * True to include the day view (and toolbar button), false to hide them (defaults to true).
4738 * @cfg {Boolean} showWeekView
4739 * True to include the week view (and toolbar button), false to hide them (defaults to true).
4743 * @cfg {Boolean} showMonthView
4744 * True to include the month view (and toolbar button), false to hide them (defaults to true).
4745 * If the day and week views are both hidden, the month view will show by default even if
4746 * this config is false.
4748 showMonthView: true,
4750 * @cfg {Boolean} showNavBar
4751 * True to display the calendar navigation toolbar, false to hide it (defaults to true). Note that
4752 * if you hide the default navigation toolbar you'll have to provide an alternate means of navigating the calendar.
4756 * @cfg {String} todayText
4757 * Alternate text to use for the 'Today' nav bar button.
4761 * @cfg {Boolean} showTodayText
4762 * True to show the value of {@link #todayText} instead of today's date in the calendar's current day box,
4763 * false to display the day number(defaults to true).
4765 showTodayText: true,
4767 * @cfg {Boolean} showTime
4768 * True to display the current time next to the date in the calendar's current day box, false to not show it
4769 * (defaults to true).
4773 * @cfg {String} dayText
4774 * Alternate text to use for the 'Day' nav bar button.
4778 * @cfg {String} weekText
4779 * Alternate text to use for the 'Week' nav bar button.
4783 * @cfg {String} monthText
4784 * Alternate text to use for the 'Month' nav bar button.
4790 layoutOnCardChange: true,
4791 deferredRender: true
4795 startDate: new Date(),
4798 initComponent: function() {
4800 cls: 'ext-cal-toolbar',
4802 buttonAlign: 'center',
4804 id: this.id + '-tb-prev',
4805 handler: this.onPrevClick,
4807 iconCls: 'x-tbar-page-prev'
4813 if (this.showDayView) {
4814 this.tbar.items.push({
4815 id: this.id + '-tb-day',
4817 handler: this.onDayClick,
4819 toggleGroup: 'tb-views'
4823 if (this.showWeekView) {
4824 this.tbar.items.push({
4825 id: this.id + '-tb-week',
4826 text: this.weekText,
4827 handler: this.onWeekClick,
4829 toggleGroup: 'tb-views'
4833 if (this.showMonthView || this.viewCount == 0) {
4834 this.tbar.items.push({
4835 id: this.id + '-tb-month',
4836 text: this.monthText,
4837 handler: this.onMonthClick,
4839 toggleGroup: 'tb-views'
4842 this.showMonthView = true;
4844 this.tbar.items.push({
4845 id: this.id + '-tb-next',
4846 handler: this.onNextClick,
4848 iconCls: 'x-tbar-page-next'
4850 this.tbar.items.push('->');
4852 var idx = this.viewCount - 1;
4853 this.activeItem = this.activeItem === undefined ? idx: (this.activeItem > idx ? idx: this.activeItem);
4855 if (this.showNavBar === false) {
4857 this.addClass('x-calendar-nonav');
4860 Ext.calendar.CalendarPanel.superclass.initComponent.call(this);
4865 * Fires after a new event is added to the underlying store
4866 * @param {Ext.calendar.CalendarPanel} this
4867 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was added
4871 * @event eventupdate
4872 * Fires after an existing event is updated
4873 * @param {Ext.calendar.CalendarPanel} this
4874 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was updated
4878 * @event eventdelete
4879 * Fires after an event is removed from the underlying store
4880 * @param {Ext.calendar.CalendarPanel} this
4881 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was removed
4885 * @event eventcancel
4886 * Fires after an event add/edit operation is canceled by the user and no store update took place
4887 * @param {Ext.calendar.CalendarPanel} this
4888 * @param {Ext.calendar.EventRecord} rec The new {@link Ext.calendar.EventRecord record} that was canceled
4893 * Fires after a different calendar view is activated (but not when the event edit form is activated)
4894 * @param {Ext.calendar.CalendarPanel} this
4895 * @param {Ext.CalendarView} view The view being activated (any valid {@link Ext.calendar.CalendarView CalendarView} subclass)
4896 * @param {Object} info Extra information about the newly activated view. This is a plain object
4897 * with following properties:<div class="mdetail-params"><ul>
4898 * <li><b><code>activeDate</code></b> : <div class="sub-desc">The currently-selected date</div></li>
4899 * <li><b><code>viewStart</code></b> : <div class="sub-desc">The first date in the new view range</div></li>
4900 * <li><b><code>viewEnd</code></b> : <div class="sub-desc">The last date in the new view range</div></li>
4906 // NOTE: CalendarPanel also relays the following events from contained views as if they originated from this:
4909 * @event eventsrendered
4910 * Fires after events are finished rendering in the view
4911 * @param {Ext.calendar.CalendarPanel} this
4915 * Fires after the user clicks on an event element
4916 * @param {Ext.calendar.CalendarPanel} this
4917 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was clicked on
4918 * @param {HTMLNode} el The DOM node that was clicked on
4922 * Fires anytime the mouse is over an event element
4923 * @param {Ext.calendar.CalendarPanel} this
4924 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor is over
4925 * @param {HTMLNode} el The DOM node that is being moused over
4929 * Fires anytime the mouse exits an event element
4930 * @param {Ext.calendar.CalendarPanel} this
4931 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that the cursor exited
4932 * @param {HTMLNode} el The DOM node that was exited
4936 * Fires after the start date of the view changes
4937 * @param {Ext.calendar.CalendarPanel} this
4938 * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}
4939 * @param {Date} viewStart The first displayed date in the view
4940 * @param {Date} viewEnd The last displayed date in the view
4943 * @event rangeselect
4944 * Fires after the user drags on the calendar to select a range of dates/times in which to create an event
4945 * @param {Ext.calendar.CalendarPanel} this
4946 * @param {Object} dates An object containing the start (StartDate property) and end (EndDate property) dates selected
4947 * @param {Function} callback A callback function that MUST be called after the event handling is complete so that
4948 * the view is properly cleaned up (shim elements are persisted in the view while the user is prompted to handle the
4949 * range selection). The callback is already created in the proper scope, so it simply needs to be executed as a standard
4950 * function call (e.g., callback()).
4954 * Fires after an event element is dragged by the user and dropped in a new position
4955 * @param {Ext.calendar.CalendarPanel} this
4956 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was moved with
4957 * updated start and end dates
4961 * Fires when a drag operation is initiated in the view
4962 * @param {Ext.calendar.CalendarPanel} this
4965 * @event eventresize
4966 * Fires after the user drags the resize handle of an event to resize it
4967 * @param {Ext.calendar.CalendarPanel} this
4968 * @param {Ext.calendar.EventRecord} rec The {@link Ext.calendar.EventRecord record} for the event that was resized
4969 * containing the updated start and end dates
4973 * Fires after the user clicks within a day/week view container and not on an event element
4974 * @param {Ext.calendar.CalendarPanel} this
4975 * @param {Date} dt The date/time that was clicked on
4976 * @param {Boolean} allday True if the day clicked on represents an all-day box, else false.
4977 * @param {Ext.Element} el The Element that was clicked on
4981 this.layout = 'card';
4982 // do not allow override
4983 if (this.showDayView) {
4984 var day = Ext.apply({
4986 title: this.dayText,
4987 showToday: this.showToday,
4988 showTodayText: this.showTodayText,
4989 showTime: this.showTime
4993 day.id = this.id + '-day';
4994 day.store = day.store || this.eventStore;
4995 this.initEventRelay(day);
4998 if (this.showWeekView) {
4999 var wk = Ext.applyIf({
5001 title: this.weekText,
5002 showToday: this.showToday,
5003 showTodayText: this.showTodayText,
5004 showTime: this.showTime
5008 wk.id = this.id + '-week';
5009 wk.store = wk.store || this.eventStore;
5010 this.initEventRelay(wk);
5013 if (this.showMonthView) {
5014 var month = Ext.applyIf({
5016 title: this.monthText,
5017 showToday: this.showToday,
5018 showTodayText: this.showTodayText,
5019 showTime: this.showTime,
5022 fn: function(vw, dt) {
5031 month.id = this.id + '-month';
5032 month.store = month.store || this.eventStore;
5033 this.initEventRelay(month);
5037 this.add(Ext.applyIf({
5038 xtype: 'eventeditform',
5039 id: this.id + '-edit',
5040 calendarStore: this.calendarStore,
5048 fn: this.onEventUpdate
5052 fn: this.onEventDelete
5056 fn: this.onEventCancel
5064 initEventRelay: function(cfg) {
5065 cfg.listeners = cfg.listeners || {};
5066 cfg.listeners.afterrender = {
5068 // relay the view events so that app code only has to handle them in one place
5069 this.relayEvents(c, ['eventsrendered', 'eventclick', 'eventover', 'eventout', 'dayclick',
5070 'eventmove', 'datechange', 'rangeselect', 'eventdelete', 'eventresize', 'initdrag']);
5078 afterRender: function() {
5079 Ext.calendar.CalendarPanel.superclass.afterRender.call(this);
5080 this.fireViewChange();
5084 onLayout: function() {
5085 Ext.calendar.CalendarPanel.superclass.onLayout.call(this);
5086 if (!this.navInitComplete) {
5087 this.updateNavState();
5088 this.navInitComplete = true;
5093 onEventAdd: function(form, rec) {
5094 rec.data[Ext.calendar.EventMappings.IsNew.name] = false;
5095 this.eventStore.add(rec);
5096 this.hideEditForm();
5097 this.fireEvent('eventadd', this, rec);
5101 onEventUpdate: function(form, rec) {
5103 this.hideEditForm();
5104 this.fireEvent('eventupdate', this, rec);
5108 onEventDelete: function(form, rec) {
5109 this.eventStore.remove(rec);
5110 this.hideEditForm();
5111 this.fireEvent('eventdelete', this, rec);
5115 onEventCancel: function(form, rec) {
5116 this.hideEditForm();
5117 this.fireEvent('eventcancel', this, rec);
5121 * Shows the built-in event edit form for the passed in event record. This method automatically
5122 * hides the calendar views and navigation toolbar. To return to the calendar, call {@link #hideEditForm}.
5123 * @param {Ext.calendar.EventRecord} record The event record to edit
5124 * @return {Ext.calendar.CalendarPanel} this
5126 showEditForm: function(rec) {
5127 this.preEditView = this.layout.activeItem.id;
5128 this.setActiveView(this.id + '-edit');
5129 this.layout.activeItem.loadRecord(rec);
5134 * Hides the built-in event edit form and returns to the previous calendar view. If the edit form is
5135 * not currently visible this method has no effect.
5136 * @return {Ext.calendar.CalendarPanel} this
5138 hideEditForm: function() {
5139 if (this.preEditView) {
5140 this.setActiveView(this.preEditView);
5141 delete this.preEditView;
5147 setActiveView: function(id) {
5148 var l = this.layout;
5149 l.setActiveItem(id);
5151 if (id == this.id + '-edit') {
5152 this.getTopToolbar().hide();
5156 l.activeItem.refresh();
5157 this.getTopToolbar().show();
5158 this.updateNavState();
5160 this.activeView = l.activeItem;
5161 this.fireViewChange();
5165 fireViewChange: function() {
5167 view = this.layout.activeItem;
5169 if (view.getViewBounds) {
5170 vb = view.getViewBounds();
5172 activeDate: view.getStartDate(),
5173 viewStart: vb.start,
5177 this.fireEvent('viewchange', this, view, info);
5181 updateNavState: function() {
5182 if (this.showNavBar !== false) {
5183 var item = this.layout.activeItem,
5184 suffix = item.id.split(this.id + '-')[1];
5186 var btn = Ext.getCmp(this.id + '-tb-' + suffix);
5192 * Sets the start date for the currently-active calendar view.
5195 setStartDate: function(dt) {
5196 this.layout.activeItem.setStartDate(dt, true);
5197 this.updateNavState();
5198 this.fireViewChange();
5202 showWeek: function(dt) {
5203 this.setActiveView(this.id + '-week');
5204 this.setStartDate(dt);
5208 onPrevClick: function() {
5209 this.startDate = this.layout.activeItem.movePrev();
5210 this.updateNavState();
5211 this.fireViewChange();
5215 onNextClick: function() {
5216 this.startDate = this.layout.activeItem.moveNext();
5217 this.updateNavState();
5218 this.fireViewChange();
5222 onDayClick: function() {
5223 this.setActiveView(this.id + '-day');
5227 onWeekClick: function() {
5228 this.setActiveView(this.id + '-week');
5232 onMonthClick: function() {
5233 this.setActiveView(this.id + '-month');
5237 * Return the calendar view that is currently active, which will be a subclass of
5238 * {@link Ext.calendar.CalendarView CalendarView}.
5239 * @return {Ext.calendar.CalendarView} The active view
5241 getActiveView: function() {
5242 return this.layout.activeItem;
5246 Ext.reg('calendarpanel', Ext.calendar.CalendarPanel);