Ext.namespace('Zarafa.calendar.ui.canvas');
/**
* @class Zarafa.calendar.ui.canvas.AppointmentProxy
* @extends Zarafa.calendar.ui.AppointmentView
*
* A special {@link Zarafa.calendar.ui.AbstractDateRangeView} which acts as proxy for the
* {@link Zarafa.calendar.ui.CalendarViewDropZone} when an appointment is being resized or
* dragged over the calendar.
*
* This will render the selecte area as a grey block with some extra information text.
*/
Zarafa.calendar.ui.canvas.AppointmentProxy = Ext.extend(Zarafa.calendar.ui.AppointmentView, {
/**
* @cfg {Boolean} showTime initial value of showTime. When true the start and due times are rendered in the view body. Defaults to false.
*/
showTime : false,
/**
* The <div> element for the body of the appointment. This element is
* created using the {@link #createDiv} function within {@link #createBodyElement}.
* @property
* @type Array
*/
body : undefined,
* The CSS class which must be applied to the {@link #header} element.
* This class can be set using {@link #setHeaderClass}.
* FIXME: This class doesn't work, as it is overriden by the subclass...
* @property
* @type String
*/
headerClassName : '',
/**
* The CSS class which must be applied to the {@link #body} element.
* This class can be set using {@link #setBodyClass}.
* FIXME: This class doesn't work, as it is overriden by the subclass...
* @property
* @type String
*/
bodyClassName : '',
/**
* @constructor
* @param {Object} config Configuration object
*/
constructor : function(config)
{
config = config || {};
Ext.applyIf(config, {
baseCls : 'zarafa-calendar',
itemCls : 'selection'
});
this.addEvents(
/**
* @event keypress
* Fires when a 'keypress' event is detected while this selection range was focussed
* @param {Ext.EventObject} event The Ext.EventObject encapsulating the DOM event
* @param {HtmlElement} element The target of the event
* @param {Object} options The options configuration passed to the addListener call
*/
'keypress',
/**
* @event keydown
* Fires when a 'keydown' event is detected while this selection range was focussed
* @param {Ext.EventObject} event The Ext.EventObject encapsulating the DOM event
* @param {HtmlElement} element The target of the event
* @param {Object} options The options configuration passed to the addListener call
*/
'keydown',
/**
* @event keyup
* Fires when a 'keyup' event is detected while this selection range was focussed
* @param {Ext.EventObject} event The Ext.EventObject encapsulating the DOM event
* @param {HtmlElement} element The target of the event
* @param {Object} options The options configuration passed to the addListener call
*/
'keyup'
);
Zarafa.calendar.ui.canvas.AppointmentProxy.superclass.constructor.call(this, config);
},
/**
* Initialises the AppointmentProxy.
* @private
* @override
*/
init : function()
{
Zarafa.calendar.ui.canvas.AppointmentProxy.superclass.init.call(this);
this.body = [];
},
/**
* Sets date range. This method does not auto-update.
* @param {Zarafa.core.DateRange} dateRange
*/
setDateRange : function(dateRange)
{
if (this.dateRange) {
this.mun(this.dateRange, 'update', this.onDateRangeUpdate, this);
}
Zarafa.calendar.ui.canvas.AppointmentProxy.superclass.setDateRange.call(this, dateRange);
if (this.dateRange) {
this.mon(this.dateRange, 'update', this.onDateRangeUpdate, this);
}
},
/**
* Renders the view.
* @param {Ext.Element} container The Ext.Element into which the view must be rendered.
*/
render : function(container)
{
Zarafa.calendar.ui.canvas.AppointmentProxy.superclass.render.apply(this, arguments);
// Relay key events
this.focusEl.relayEvent('keypress', this);
this.focusEl.relayEvent('keydown', this);
this.focusEl.relayEvent('keyup', this);
},
* Sets the class or classes on the view header element.
* @param {String} className class name or class name list
*/
setHeaderClass : function(className)
{
this.headerClassName = className;
},
/**
* Sets the class or classes on the view body elements.
* @param {String} className class name or class name list
*/
setBodyClass : function(className)
{
this.bodyClassName = className;
},
/**
* Toggles whether the view should show the start and due times in the view body.
* @param {Boolean} showTime if true the view will show the start and due times in the calendar body.
*/
setShowTime : function(showTime)
{
this.showTime = showTime;
},
/**
* Adds text to the view body containing the start and due times of the date range.
* @private
*/
createTimeText: function ()
{
if (this.body.length >= 1) {
var dateRange = this.getDateRange();
var startDate = dateRange.getStartDate();
var dueDate = dateRange.getDueDate();
var time = String.format('{0} - {1}', startDate.format(_("G:i")), dueDate.format(_("G:i")));
if (this.getFirstBodyElement().dom.innerHTML !== time) {
this.getFirstBodyElement().dom.innerHTML = time;
}
}
},
/**
* Removes text from the view body.
* @private
*/
clearTimeText : function()
{
if (this.body.length > 0) {
this.getFirstBodyElement().dom.innerHTML = '';
}
},
/**
* Sets the visibility of the range
* @param {Boolean} visible if true the view will be shown, if false it will be hidden
*/
setVisible : function(visible)
{
Zarafa.calendar.ui.canvas.AppointmentProxy.superclass.setVisible.call(this, visible);
if (visible) {
this.setBodyClass('zarafa-calendar-selection zarafa-calendar-selection-dragging zarafa-calendar-dragproxy-time');
this.setHeaderClass('zarafa-calendar-selection zarafa-calendar-selection-dragging');
} else {
this.setBodyClass('zarafa-calendar-selection');
this.setHeaderClass('zarafa-calendar-selection');
}
//Always show focus element.
if(this.focusEl) {
this.focusEl.show();
}
if (this.rendered) {
this.layout();
}
},
/**
* Lays out the elements of the view body.
* @param {Object[]} bounds array of bounds (left, right, top, bottom) objects.
* @private
*/
layoutBodyElements : function(bounds)
{
// optionally generate time text
if (this.showTime) {
this.createTimeText();
} else {
this.clearTimeText();
}
// resize the body elements to match the bounds
for (var i=0; i<bounds.length; i++) {
this.body[i].dom.className = this.bodyClassName;
this.body[i].setLeftTop(bounds[i].left, bounds[i].top);
this.body[i].setSize(bounds[i].right - bounds[i].left, bounds[i].bottom - bounds[i].top);
}
},
/**
* Event handler which is fired when the {@link #dateRange} is updated.
* When the component is rendered and visible, then this will relayout
* the view.
* @param {Zarafa.core.DateRange} dateRange the changed daterange
*/
onDateRangeUpdate : function(dateRange)
{
if (this.rendered && this.isVisible()) {
this.layout();
}
},
/**
* Date ranges may change dynamically, for instance when a selection is dragged or when an appointment is resized.
* In such cases the view that represents the range must dynamically add and remove 'boxes' as the range may span
* a variable number of days in both the header and body.
* <p>
* This method adds a single such 'box' by creating a new <div> element and attaching events to it.
* The method {@link #destroyBodyElement} should remove any elements created with this method.
* @private
* @override
*/
createBodyElement : function()
{
var calendarBody = this.parentView.getCalendarBody();
var element = this.createDiv(calendarBody, 'body');
this.mon(element, 'contextmenu', this.onContextMenu, this);
this.mon(element, 'dblclick', this.onDoubleClick, this);
},
/**
* This function will cleanup any elements created with {@link #createBodyElement}.
* @private
* @override
*/
destroyBodyElement : function()
{
var element = this.body.pop();
this.mun(element, 'contextmenu', this.onContextMenu, this);
this.mun(element, 'dblclick', this.onDoubleClick, this);
this.remove(element);
},
* Creates elements to represent the range when shown in the header.
* @private
*/
createHeader : function()
{
this.createDiv(this.parentView.getCalendarHeader(), 'header');
this.mon(this.header, 'contextmenu', this.onContextMenu, this);
this.mon(this.header, 'dblclick', this.onDoubleClick, this);
},
* Destroys the header element(s).
* @private
*/
destroyHeader : function()
{
this.mun(this.header, 'contextmenu', this.onContextMenu, this);
this.mun(this.header, 'dblclick', this.onDoubleClick, this);
this.remove(this.header);
this.header = undefined;
},
* Lays out the header element of the view.
* @private
*/
layoutInHeader : function()
{
// Get the bounds of the header from the parent calendar.
var bounds = this.parentView.dateRangeToHeaderBounds(this.getDateRange());
// If header doesn't exist yet, create it
if (!this.header) {
this.createHeader();
}
// If body exists, destroy it
while (this.body.length) {
this.destroyBodyElement();
}
this.header.dom.className = this.headerClassName;
this.header.setLeftTop(bounds.left, bounds.top);
this.header.setSize(bounds.right - bounds.left, this.parentView.getAppointmentHeaderheight());
},
/**
* Lays out the body of the view.
* @private
* @override
*/
layoutInBody : function()
{
var bounds = this.parentView.dateRangeToBodyBounds(this.dateRange);
// If the header exists, destroy it
if (this.header) {
this.destroyHeader();
}
// make sure the number of DIV elements we have allocated matches the size of the
// bounds array
while (this.body.length < bounds.length) {
this.createBodyElement();
}
while (this.body.length > bounds.length) {
this.destroyBodyElement();
}
// Lay out the body elements to match the bounds
this.layoutBodyElements(bounds);
},
/**
* Layout method. We override this here to change the default behavior where
* the range is only shown on the body if the duration is less than 24 hours.
* During appointment dragging and resizing it looks weird if the range is
* shown on the header only i such a case.
* @protected
*/
onLayout : function()
{
Zarafa.calendar.ui.canvas.AppointmentProxy.superclass.onLayout.call(this);
// The selectionRange is updated, make sure we mark
// this selection as focussed.
this.focus();
},
/**
* @return {Ext.Element} first element of the body of the view.
*/
getFirstBodyElement : function()
{
return this.body[0];
},
* Handles the onContextMenu event, and refires it on the parent calendar view.
* @param {Object} event The event object
* @private
*/
onContextMenu : function(event)
{
var model = this.parentView.parentView.model;
var record = model.createRecord(this.parentView.getSelectedFolder(), this.getDateRange());
this.parentView.fireEvent('contextmenu', this.parentView, event, record);
},
/**
* Handles the onDoubleClick event, and refires it on the parent calendar view.
* @param {Object} event The event object
* @private
*/
onDoubleClick : function(event)
{
var model = this.parentView.parentView.model;
var record = model.createRecord(this.parentView.getSelectedFolder(), this.getDateRange());
this.parentView.fireEvent('dblclick', this.parentView, event, record);
}
});