'
+ ];
+ },
+
+ /**
+ * Returns the template used for the simple list.
+ * @return {Array} A array of strings making up the template.
+ */
+ getTemplateForSimpleList: function() {
+ return [
+ '
'
+ ];
+ },
+
+ /**
+ * Returns event start and end times formatted for output on the agenda list. See also {@link #getEventTimesMarkupForSimpleList}.
+ * @param {Extensible.calendar.data.EventModel} evt
+ * @param {Date} dt The date for which to produce output.
+ * @return {String}
+ */
+ getEventTimesMarkupForAgendaList: function(evt, dt) {
+ var result,
+ M = Extensible.calendar.data.EventMappings,
+ currentDt = Ext.Date.clearTime(dt),
+ startDt = Ext.Date.clearTime(evt.data[M.StartDate.name], true),
+ endDt = Ext.Date.clearTime(evt.data[M.EndDate.name], true),
+ startDtTime = evt.data[M.StartDate.name],
+ endDtTime = evt.data[M.EndDate.name];
+
+ // There are five cases to consider:
+ // Case Output example
+ // ---------------------------------------------------------------+---------------
+ // 1) Event is all-day event All day
+ // 2) Event is not all-day event
+ // 2.1) Start time and end time are on the current day 8:00am - 11:00am
+ // 2.2) Start time on current date, end time on later date 8:00 >>
+ // 2.3) Start time on earlier date, end time on current date >> 11:00am
+ // 2.4) Start time on earlier date, end time on later day All day
+ if (evt.data[M.IsAllDay.name]) {
+ result = this.allDayText; // Case 1
+ } else {
+ if (Extensible.Date.compare(currentDt, startDt) == 0) {
+ if (Extensible.Date.compare(currentDt, endDt) == 0) {
+ result = Ext.Date.format(startDtTime, this.hourFormat) + ' - ' + Ext.Date.format(endDtTime, this.hourFormat); // Case 2.1
+ } else {
+ result = Ext.Date.format(startDtTime, this.hourFormat) + ' »'; // Case 2.2
+ }
+ } else {
+ if (Extensible.Date.compare(currentDt, endDt) == 0) {
+ result = '» ' + Ext.Date.format(endDtTime, this.hourFormat); // Case 2.3
+ } else {
+ result = this.allDayText; // Case 2.4
+ }
+ }
+ }
+ return result;
+ },
+
+ /**
+ * Returns event start and end times formatted for output on the simple list. See also {@link #getEventTimesMarkupForAgendaList}.
+ * @param {Extensible.calendar.data.EventModel} evt
+ * @param {Date} dt The date for which to produce output.
+ * @return {String}
+ */
+ getEventTimesMarkupForSimpleList: function(evt, dt) {
+ var result,
+ M = Extensible.calendar.data.EventMappings,
+ currentDt = Ext.Date.clearTime(dt),
+ startDt = Ext.Date.clearTime(evt.data[M.StartDate.name], true),
+ endDt = Ext.Date.clearTime(evt.data[M.EndDate.name], true),
+ startDtTime = evt.data[M.StartDate.name],
+ endDtTime = evt.data[M.EndDate.name],
+ startHourStr = '',
+ untilStr = '-',
+ endDtStr = '',
+ endHourStr = '';
+
+ // This function generates HTML output that contains the following information:
+ // - Event start hour
+ // - Event end date
+ // - Event end hour
+ // Note that the event start date is not part of the output because the start date is displayed once for
+ // all events on the same day.
+ //
+ // There are several cases to consider:
+ // 1) All-day event that starts and ends on the current day.
+ // 2) All-day event that starts on the current day and ends on a later day.
+ // 3) Non-all-day event that starts and ends on the current day.
+ // 4) Non-all-day event that starts on the current day and ends on a later day.
+ //
+ // Generated values for the four cases are:
+ // Evt start hour | Evt end date | Evt end hour
+ // 1) All day | |
+ // 2) All day | Mon May 18 |
+ // 3) 8:00am | 5:00pm |
+ // 4) 8:00am | Mon May 18 | 5:00pm
+
+ if (evt.data[M.IsAllDay.name]) {
+ if (startDt.getTime() == endDt.getTime()) {
+ // Case 1
+ startHourStr = this.allDayText;
+ untilStr = '';
+ } else {
+ // Case 2
+ startHourStr = this.allDayText;
+ endDtStr = Ext.Date.format(endDt, this.dayDateFormat);
+ }
+ } else {
+ if (startDt.getTime() == endDt.getTime()) {
+ // Case 3
+ startHourStr = Ext.Date.format(startDtTime, this.hourFormat);
+ endDtStr = Ext.Date.format(endDtTime, this.hourFormat);
+ } else {
+ // Case 4
+ startHourStr = Ext.Date.format(startDtTime, this.hourFormat);
+ endDtStr = Ext.Date.format(endDt, this.dayDateFormat);
+ endHourStr = Ext.Date.format(endDtTime, this.hourFormat);
+ }
+ }
+
+ result = [
+ '
', startHourStr, '
', untilStr, '
',
+ '
', endDtStr, '
', endHourStr, '
'];
+ return result.join('');
+ },
+
+ /**
+ * Returns the markup for the event title.
+ * @param {Extensible.calendar.data.EventModel} evt
+ * @return {String}
+ */
+ getTitleMarkup: function(evt) {
+ var result,
+ M = Extensible.calendar.data.EventMappings,
+ title = evt.data[M.Title.name];
+ result = [
+ '',
+ !title || title.length == 0 ? this.defaultEventTitleText : title,
+ this.getReminderFlagMarkup(evt),
+ this.getRecurrenceFlagMarkup(evt),
+ ''
+ ];
+ if (evt.data[M.Location.name] && evt.data[M.Location.name] != '' && !this.showEventDetails) {
+ result.push(
+ ' - ',
+ evt.data[M.Location.name]
+ );
+ }
+ return result.join('');
+ },
+
+ /**
+ * Returns the markup for the reminder flag, if a reminder is active. Otherwise an empty string is returned.
+ * @param {Extensible.calendar.data.EventModel} evt
+ * @return {String}
+ */
+ getReminderFlagMarkup: function(evt) {
+ var M = Extensible.calendar.data.EventMappings;
+ return evt.data[M.Reminder.name] && evt.data[M.Reminder.name] != '' ? '' : '';
+ },
+
+ /**
+ * Returns the markup for the recurrence flag, if recurrence is active. Otherwise an empty string is returned.
+ * @param {Extensible.calendar.data.EventModel} evt
+ * @return {String}
+ */
+ getRecurrenceFlagMarkup: function(evt) {
+ var M = Extensible.calendar.data.EventMappings;
+ return evt.data[M.RRule.name] && evt.data[M.RRule.name] != '' ? '' : '';
+ },
+
+ /**
+ * Returns the markup for the web link. If no web link is defined, an empty string is returned.
+ * @param {Extensible.calendar.data.EventModel} evt
+ * @param {Boolean} removeProtocol If true the 'http://' string is removed from the web link. This can be useful
+ * to display the web link in a user friendly way. If the web link is missing the protocol string and this
+ * parameter is false, then the protocol string is prepended. Defaults to false.
+ * @return {String}
+ */
+ getWebLinkMarkup: function(evt, removeProtocol) {
+ var M = Extensible.calendar.data.EventMappings,
+ l = evt.data[M.Url.name];
+ if (l && l != "") {
+ if (l.indexOf('http://') == 0) {
+ l = l.substr(7);
+ }
+ if (removeProtocol) {
+ return l;
+ } else {
+ return 'http://' + l;
+ }
+ } else {
+ return '';
+ }
+ },
+
+ /**
+ * Returns the markup for the event notes. If no event notes are defined, an empty string is returned. The notes
+ * are limited to the number of characters specified by configuration option {@link #maxNotesLength}.
+ * @param {Extensible.calendar.data.EventModel} evt
+ * @return {String}
+ */
+ getNotesMarkup: function(evt) {
+ var M = Extensible.calendar.data.EventMappings,
+ n = evt.data[M.Notes.name];
+ return n.length > this.maxNotesLength ? n.substring(0, this.maxNotesLength-3) + '...' : n;
+ },
+
+
+ /**
+ * Returns the markup for an event group header. The type of group header returned depends on the configured
+ * event grouping (see {@link #groupBy}). For example:
+ * Monthly grouping: June 2012
+ * Weekly grouping: Week 23: Mon Jun 3 - Sun Jun 10
+ * No grouping: Empty string
+ * @param {Object} group
+ * @return {String}
+ */
+ getGroupHeaderMarkup: function(group) {
+ var result;
+
+ if (this.groupBy == 'month') {
+ result = [Ext.Date.format(group.startDt, "F Y")];
+ } else if (this.groupBy == 'week') {
+ if (Ext.Date.clearTime(group.startDt, true).getTime() == Ext.Date.clearTime(group.endDt, true).getTime()) {
+ // This is a partical week with only one day left. Don't show date range, just current date.
+ result = ['Week ', group.weekNo, ': ', Ext.Date.format(group.startDt, this.dayDateFormat)];
+ } else {
+ result = ['Week ', group.weekNo, ': ', Ext.Date.format(group.startDt, this.dayDateFormat), ' - ', Ext.Date.format(group.endDt, this.dayDateFormat)];
+ }
+ } else {
+ result = [''];
+ }
+ return result.join('');
+ },
+
+ /**
+ * Returns true if passed event has notes, false otherwise. This is a small helper function for the template.
+ * @param {Extensible.calendar.data.EventModel} An event record.
+ * @return {Boolean}
+ */
+ eventHasNotes: function(evt) {
+ var n = evt.data[Extensible.calendar.data.EventMappings.Notes.name];
+ return n && n != "";
+ },
+
+ /**
+ * Returns true if passed event has a location assigned, false otherwise. This is a small helper function for the template.
+ * @param {Extensible.calendar.data.EventModel} An event record.
+ * @return {Boolean}
+ */
+ eventHasLocation: function(evt) {
+ var l = evt.data[Extensible.calendar.data.EventMappings.Location.name];
+ return l && l != "";
+ },
+
+ /**
+ * Returns true if passed event has a link assigned, false otherwise. This is a small helper function for the template.
+ * @param {Extensible.calendar.data.EventModel} An event record.
+ * @return {Boolean}
+ */
+ eventHasLink: function(evt) {
+ var url = evt.data[Extensible.calendar.data.EventMappings.Url.name];
+ return url && url != "";
+ },
+
+ /**
+ * Returns true if group titles are to be displayed. This is a small helper function for the template.
+ * @return {Boolean}
+ */
+ hasGroupTitle: function() {
+ return this.groupBy == 'month' || this.groupBy == 'week' ? true : false;
+ }
+
+},
+function() {
+ this.createAlias('apply', 'applyTemplate');
+});
\ No newline at end of file
diff --git a/src/calendar/view/AbstractCalendar.js b/src/calendar/view/AbstractCalendar.js
index 1053729d..67a7db86 100644
--- a/src/calendar/view/AbstractCalendar.js
+++ b/src/calendar/view/AbstractCalendar.js
@@ -9,7 +9,7 @@
*/
Ext.define('Extensible.calendar.view.AbstractCalendar', {
extend: 'Ext.Component',
-
+
requires: [
'Ext.CompositeElement',
'Extensible.calendar.form.EventDetails',
@@ -1441,8 +1441,27 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
// remain sorted sequentially by start time. This seems more proper
// but can make for a less visually-compact layout when there are
// many such events mixed together closely on the calendar.
- return a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime();
+
+ // Events are sorted by three criteria: Start time, end time and
+ // calendar id. The calendar id is used as the third sort criteria
+ // to ensure that events are always ordered the same way. Without
+ // that third criteria, events that start at the same time and end at
+ // the same time would be ordered randomly.
+ var sortStartDate = a[M.StartDate.name].getTime() - b[M.StartDate.name].getTime()
+ if (sortStartDate){
+ return sortStartDate;
+ }
+ var sortEndDate = b[M.EndDate.name].getTime() - a[M.EndDate.name].getTime(); //descending
+ if (sortEndDate){
+ return sortEndDate;
+ }
+ var sortCalendar = a[M.CalendarId.name] - b[M.CalendarId.name];//ascending
+ if (sortCalendar){
+ return sortCalendar;
+ }
+ return 0;
}
+
}, this));
},
@@ -1558,14 +1577,29 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
Ext.each(operation.records, function(rec) {
if (rec.dirty) {
if (rec.phantom) {
- rec.unjoin(this.eventStore);
+ this.store.remove(rec);
}
else {
rec.reject();
}
}
}, this);
-
+
+ // Restore deleted records back to their original positions.
+ // This code was copied from ExtJS V4.2.2 Ext.data.Store, function rejectChanges(). In order to maintain
+ // backwards compatibility with version 4.0.7, this function cannot be called directly.
+ var recs = this.store.removed,
+ len = recs.length,
+ i = 0, rec;
+
+ for (i = len-1; i >= 0; i--) {
+ rec = recs[i];
+ this.store.insert(rec.removedFrom || 0, rec);
+ rec.reject();
+ }
+ // Since removals are cached in a simple array we can simply reset it here.
+ this.store.removed.length = 0;
+
if (this.fireEvent('eventexception', this, response, operation) !== false) {
this.notifyOnException(response, operation);
}
diff --git a/src/calendar/view/Agenda.js b/src/calendar/view/Agenda.js
new file mode 100644
index 00000000..5a6b81be
--- /dev/null
+++ b/src/calendar/view/Agenda.js
@@ -0,0 +1,331 @@
+/**
+ * @class Extensible.calendar.view.Agenda
+ * @extends Ext.Container
+ *
+ *
This class is currently beta code and the API is still subject to change before the next release.
+ *
+ *
Agenda view display events as a chronologically sorted list. It supports two types of list:
+ *
+ *
1) Agenda lists: An agenda list is a list where for each day of the view period all events that are taking place
+ * on that day are listed. For example, an event that lasts seven days is listed seven times, once for each day.
+ * This view is very similar to the agenda view in Google calendar.
+ *
+ *
2) Simple lists: A simple list is a list where each event is listed once, independent of the duration of the
+ * event. This is suited for event calendars or to present the results of a search for events. Simple list mode is
+ * activated by setting property {@link #simpleList} to true.
+ * Additionally, simple lists support grouping of events by month and week. Grouping is enabled with property
+ * {@link #groupBy}. If grouping is enabled, each group of events starts with a group header displaying the
+ * month or week.
+ *
+ *
Agenda view supports CRUD operations on events, filtering of events based on calendar and a selectable date
+ * range. The view can be switched between a summary view and a details view.
+ *
+ *
The view is divided into two main sections: the {@link Extensible.calendar.view.AgendaHeader header} and the
+ * {@link Extensible.calendar.view.AgendaBody event list}. The header hosts a form and a toolbar that can be
+ * used to filter events, choose display options, apply action on events, etc. Both header and toolbar are
+ * easily configurable.
+ *
+ *
Unlike other calendar views, this view is not actually a subclass of {@link Extensible.calendar.view.AbstractCalendar AbstractCalendar}.
+ * Instead it is a {@link Ext.Container} subclass that internally creates and manages the layouts of
+ * a {@link Extensible.calendar.view.AgendaHeader AgendaHeader} and a {@link Extensible.calendar.view.AgendaBody AgendaBody}.
+ * As such this class accepts any config values that are valid for AgendaHeaderView and AgendaBodyView and passes those through
+ * to the contained views. It also supports the interface required of any calendar view and in turn calls methods
+ * on the contained views as necessary.
How this component should be hidden. Supported values are 'visibility'
+ * (css visibility), 'offsets' (negative offset position) and 'display'
+ * (css display).
+ *
Note: For calendar views the default is 'offsets' rather than the Ext JS default of
+ * 'display' in order to preserve scroll position after hiding/showing a scrollable view like Day or Week.
If true, a simple list of events is displayed, else, an agenda-style list is displayed. See the introduction
+ * of this class for more details. Defaults to false.
Defines the grouping to be applied to the list of events. This property only has an effect if property
+ * {@link #simpleList} is true. Supported values are month, week and none. Any other
+ * values will disable grouping. Default value is none.
+ */
+ groupBy: 'none',
+
+ /**
+ * @property ownerCalendarPanel
+ * @type Extensible.calendar.CalendarPanel
+ * If this view is hosted inside a {@link Extensible.calendar.CalendarPanel} this property will reference
+ * it. If the view was created directly outside of a CalendarPanel this property will be undefined. Read-only.
+ */
+
+ // private
+ layout: {
+ type: 'vbox',
+ align: 'stretch'
+ },
+
+ // private
+ isAgendaView: true,
+
+ // private
+ initComponent : function(){
+
+ // Pass on initial configuration to sub-components
+ var cfg = Ext.apply({}, this.initialConfig);
+
+ var header = Ext.applyIf({
+ xtype: 'extensible.agendaheaderview',
+ id: this.id+'-hd',
+ stateful: this.stateful,
+ stateId: this.id+'-hd',
+ ownerCalendarView: this,
+ listeners: {
+ formchange: {fn: this.onFormChange, scope: this},
+ addevent: {fn: this.onAddEvent, scope: this}
+ }
+ }, cfg);
+
+ var body = Ext.applyIf({
+ xtype: 'extensible.agendabodyview',
+ id: this.id+'-bd',
+ simpleList: this.simpleList,
+ groupBy: this.groupBy,
+ ownerCalendarPanel: this.ownerCalendarPanel,
+ ownerCalendarView: this
+ }, cfg);
+
+ this.items = [header, body];
+ this.addCls('ext-cal-agenda ext-cal-ct');
+
+ this.callParent(arguments);
+ },
+
+ // private
+ afterRender : function(){
+ var filterConfig;
+
+ this.callParent(arguments);
+
+ this.header = Ext.getCmp(this.id+'-hd');
+ this.body = Ext.getCmp(this.id+'-bd');
+
+ this.body.on('eventsrendered', this.forceSize, this);
+ this.on('resize', this.onResize, this);
+
+ filterConfig = this.header.getForm().getFieldValues();
+ this.body.setFilterConfig(filterConfig);
+ },
+
+ // private
+ refresh : function(){
+ Extensible.log('refresh (AgendaView)');
+ // this.header.refresh();
+ this.body.refresh();
+ },
+
+
+ // private
+ onFormChange: function(header, form, field) {
+ var filterConfig = form.getFieldValues();
+
+ this.body.setFilterConfig(filterConfig);
+ if (field.getId() == this.id + '-hd-showdetails') {
+ // Refresh the header form without reloading the events
+ this.body.refresh(false);
+ } else {
+ // Reset start date. This will trigger a reload of the events with the changed filter settings.
+ this.setStartDate(this.getStartDate());
+ }
+ },
+
+ // private
+ onAddEvent: function(hd, bt) {
+ var M = Extensible.calendar.data.EventMappings,
+ D = Ext.Date,
+ data = {},
+ // now = new Date(),
+ now = this.body.getStartDate(),
+ today = D.clearTime(now, true);
+
+ data[M.StartDate.name] = D.add(today, D.HOUR, now.getHours() + 1);
+ data[M.EndDate.name] = D.add(today, D.HOUR, now.getHours() + 2);
+ data[M.IsAllDay.name] = true;
+
+ this.body.showEventEditor(data, bt.getEl());
+ },
+
+ // private
+ forceSize: function(){
+ // The defer call is mainly for good ol' IE, but it doesn't hurt in
+ // general to make sure that the window resize is good and done first
+ // so that we can properly calculate sizes.
+ /*
+ Ext.defer(function(){
+ var ct = this.el.up('.x-panel-body'),
+ hd = this.el.down('.ext-cal-agenda-header'),
+ h = ct.getHeight() - hd.getHeight();
+
+ this.el.down('.ext-cal-body-ct').setHeight(h-1);
+ }, 1, this);
+ */
+ },
+
+ // private
+ onResize : function(){
+ this.forceSize();
+ Ext.defer(this.refresh, Ext.isIE ? 1 : 0, this); //IE needs the defer
+ },
+
+ /*
+ * We have to "relay" this Component method so that the hidden
+ * state will be properly reflected when the views' active state changes
+ */
+ doHide: function(){
+ this.header.doHide.apply(this, arguments);
+ this.body.doHide.apply(this, arguments);
+ },
+
+ /**
+ * Returns the start and end boundary dates currently displayed in the view. The method
+ * returns an object literal that contains the following properties:
+ * @return {Object} An object literal containing the start and end values
+ */
+ getViewBounds : function(){
+ return this.body.getViewBounds();
+ },
+
+ /**
+ * Returns the start date of the view, as set by {@link #setStartDate}. Note that this may not
+ * be the first date displayed in the rendered calendar -- to get the start and end dates displayed
+ * to the user use {@link #getViewBounds}.
+ * @return {Date} The start date
+ */
+ getStartDate : function(){
+ return this.body.getStartDate();
+ },
+
+ /**
+ * Sets the start date used to calculate the view boundaries to display. The displayed view will be the
+ * earliest and latest dates that match the view requirements and contain the date passed to this function.
+ * @param {Date} dt The date used to calculate the new view boundaries
+ */
+ setStartDate: function(dt){
+ this.body.setStartDate(dt, true);
+ },
+
+ // private
+ renderItems: function(){
+ this.body.renderItems();
+ },
+
+ /**
+ * Returns true if the view is currently displaying today's date, else false.
+ * @return {Boolean} True or false
+ */
+ isToday : function(){
+ return this.body.isToday();
+ },
+
+ /**
+ * Updates the view to contain the passed date
+ * @param {Date} dt The date to display
+ * @return {Date} The new view start date
+ */
+ moveTo : function(dt){
+ var newDt = this.body.moveTo(dt, true);
+ this.header.moveTo(newDt);
+ return newDt;
+ },
+
+ /**
+ * Updates the view to the next consecutive date(s)
+ * @return {Date} The new view start date
+ */
+ moveNext : function(){
+ var newDt = this.body.moveNext(true);
+ this.header.moveTo(newDt);
+ return newDt;
+ },
+
+ /**
+ * Updates the view to the previous consecutive date(s)
+ * @return {Date} The new view start date
+ */
+ movePrev : function(){
+ var newDt = this.body.movePrev(true);
+ this.header.moveTo(newDt);
+ return newDt;
+ },
+
+ /**
+ * Shifts the view by the passed number of days relative to the currently set date
+ * @param {Number} value The number of days (positive or negative) by which to shift the view
+ * @return {Date} The new view start date
+ */
+ moveDays : function(value){
+ var newDt = this.body.moveDays(value, true);
+ this.header.moveTo(newDt);
+ return newDt;
+ },
+
+ /**
+ * Updates the view to show today
+ * @return {Date} Today's date
+ */
+ moveToday : function(){
+ var newDt = this.body.moveToday(true);
+ this.header.moveTo(newDt);
+ return newDt;
+ },
+
+ /**
+ * Show the currently configured event editor view (by default the shared instance of
+ * {@link Extensible.calendar.form.EventWindow EventEditWindow}).
+ * @param {Extensible.calendar.data.EventModel} rec The event record
+ * @param {Ext.Element/HTMLNode} animateTarget The reference element that is being edited. By default this is
+ * used as the target for animating the editor window opening and closing. If this method is being overridden to
+ * supply a custom editor this parameter can be ignored if it does not apply.
+ * @return {Extensible.calendar.view.Day} this
+ */
+ showEventEditor : function(rec, animateTarget){
+ return Extensible.calendar.view.AbstractCalendar.prototype.showEventEditor.apply(this, arguments);
+ },
+
+ /**
+ * Dismiss the currently configured event editor view (by default the shared instance of
+ * {@link Extensible.calendar.form.EventWindow EventEditWindow}, which will be hidden).
+ * @param {String} dismissMethod (optional) The method name to call on the editor that will dismiss it
+ * (defaults to 'hide' which will be called on the default editor window)
+ * @return {Extensible.calendar.view.Day} this
+ */
+ dismissEventEditor : function(dismissMethod){
+ return Extensible.calendar.view.AbstractCalendar.prototype.dismissEventEditor.apply(this, arguments);
+ }
+});
\ No newline at end of file
diff --git a/src/calendar/view/AgendaBody.js b/src/calendar/view/AgendaBody.js
new file mode 100644
index 00000000..973b0140
--- /dev/null
+++ b/src/calendar/view/AgendaBody.js
@@ -0,0 +1,670 @@
+/**
+ * @class Extensible.calendar.view.AgendaBody
+ * @extends Extensible.calendar.view.AbstractCalendar
+ *
+ *
This class is currently beta code and the API is still subject to change before the next release.
+ *
+ *
This is the body area view within the agenda view. Normally you should not need to use this class directly
+ * -- instead you should use {@link Extensible.calendar.view.Agenda Agenda} view which aggregates this class and the
+ * {@link Extensible.calendar.view.AgendaHeader AgendaHeader} view into a single unified view
+ * presented by {@link Extensible.calendar.CalendarPanel CalendarPanel}.
+ *
+ *
This component displays the list of events and supports CRUD operations on events. The layout of the events
+ * is controlled by template {@link Extensible.calendar.template.AgendaBody}.
+ *
+ * @author Gabriel Sidler, sidler@teamup.com
+ * @constructor
+ * @param {Object} config The config object
+ */
+Ext.define('Extensible.calendar.view.AgendaBody', {
+ extend: 'Extensible.calendar.view.AbstractCalendar',
+ alias: 'widget.extensible.agendabodyview',
+
+ requires: [
+ 'Ext.XTemplate',
+ 'Extensible.calendar.template.AgendaBody'
+ ],
+
+ /**
+ * @cfg {Boolean} linkDatesToDayView
+ * True to link dates to the {@link Extensible.calendar.view.Day day view}.
+ */
+ linkDatesToDayView: true,
+
+ /**
+ * @cfg {String} dateRangeDefault
+ * Defines the default value for the date range. Supported values are: day, week, month,
+ * 3months and year. Defaults to month.
+ */
+ dateRangeDefault: 'month',
+
+ /**
+ * @cfg {Boolean} simpleList
+ *
If true, a simple list of events is displayed, else, an agenda-style list is displayed.
+ * Defaults to false.
Defines the grouping to be applied to the list of events. This property only has an effect if property
+ * {@link #simpleList} is true. Supported values are month, week and none. Any other
+ * values will disable grouping. Default value is none.
+ */
+ groupBy: 'none',
+
+ /**
+ * @property ownerCalendarView
+ * @type {Ext.Container}
+ * A reference to the calendar view that hosts this view. Read-only.
+ */
+
+ // private properties
+ /*
+ * Private
+ * @property filterConfig
+ * @type {Object}
+ * An object that contains key/value pairs to be used as the filtering configuration when loading events.
+ * Use method {@link #setFilterConfig} to set this property. This ensures that the new filter configuration is put
+ * into operation immediately.
+ */
+ dayLinkSelector: '.ext-cal-day-link',
+ dayLinkIdDelimiter: 'ext-cal-day-',
+ prevLinkSelector: 'ext-cal-agenda-bd-prev-link',
+ nextLinkSelector: 'ext-cal-agenda-bd-next-link',
+ flex: 1,
+ autoScroll: true,
+ padding: '10 0 10 0',
+
+ // private
+ initComponent : function(){
+
+ this.filterConfig = {
+ period: this.dateRangeDefault,
+ groupby: this.groupBy,
+ details: false
+ };
+
+ this.addEvents({
+ /**
+ * @event dayclick
+ * Fires after the user clicks on a day date
+ * @param {Extensible.calendar.view.AgendaBody} this
+ * @param {Date} dt The date that was clicked on.
+ */
+ dayclick: true
+ });
+
+ this.callParent(arguments);
+ },
+
+ // Private
+ renderTemplate : function(){
+ var templateParams = this.getTemplateParams();
+
+ if (this.simpleList){
+ templateParams.groups = this.getTemplateEventDataForSimpleList();
+ } else {
+ templateParams.days = this.getTemplateEventDataForAgenda();
+ }
+ if(this.tpl){
+ this.tpl.overwrite(this.el, templateParams);
+ this.lastRenderStart = Ext.Date.clone(this.viewStart);
+ this.lastRenderEnd = Ext.Date.clone(this.viewEnd);
+ }
+ },
+
+ /**
+ *
Returns the template event data for rendering events as a simple list (property {@link #simpleList} is true).
+ * Optionally, the events can be grouped by month or week. The grouping is controlled by parameter {@link #groupBy}.
+ *
+ *
Returns an array of group objects containing an array of day objects containing and array of events.
+ * If grouping is disabled, then the top-level array contains only one group object. The following illustrates the
+ * returned data structure.
+ *
+ * @return {Array}
+ */
+ getTemplateEventDataForSimpleList : function(){
+ var M = Extensible.calendar.data.EventMappings,
+ events,
+ event,
+ groups = {},
+ groupsArray = [],
+ daysArray,
+ viewBounds = this.getViewBounds(),
+ startDtView = viewBounds.start,
+ endDtView = viewBounds.end,
+ startDtGroup, endDtGroup,
+ startDtEvent, endDtEvent,
+ Dt = Extensible.Date,
+ group,
+ groupKey,
+ dayKey,
+ weekInfo;
+
+ // Loop over all events of within view period.
+ events = this.getEvents();
+ for (var i=0; i endDtView.getTime()) {
+ endDtGroup = endDtView;
+ }
+ } else if (this.groupBy == 'week') {
+ // End date is the end of the current week or the end date of the view, whichever is earlier.
+ // Take into consideration that week start day is configurable.
+ dayOfWeek = startDtGroup.getDay();
+ endDtGroup = Ext.Date.add(Ext.Date.add(startDtGroup, Ext.Date.DAY, (6 - dayOfWeek + this.startDay) % 7 + 1), Ext.Date.SECOND, -1);
+ if (endDtGroup.getTime() > endDtView.getTime()) {
+ endDtGroup = endDtView;
+ }
+ } else {
+ // There is only one group, therefore end date of group is end date of view
+ endDtGroup = endDtView;
+ }
+
+ // Check if we have reached the end of the viewing period
+ if (startDtGroup.getTime() > endDtEvent.getTime() || startDtGroup.getTime() > endDtView.getTime()) {
+ break;
+ }
+
+ // Create group key. The group key is used to find a group in the list of groups. The
+ // format of the group key depends on the configured grouping.
+ if (this.groupBy == 'month') {
+ groupKey = Ext.Date.format(startDtGroup, 'my');
+ } else if (this.groupBy == 'week') {
+ weekInfo = this.getWeekInfo(Ext.Date.clearTime(endDtGroup));
+ groupKey = weekInfo.weekNo + '_' + weekInfo.weekYear;
+ } else {
+ groupKey = 'defaultKey';
+ }
+
+ // Create a day key
+ dayKey = Ext.Date.format(startDtEvent, 'dmy');
+
+ // Check if an array representing the current group already exists
+ group = groups[groupKey];
+ if (typeof group == 'undefined') {
+ group = {
+ startDt: startDtGroup,
+ endDt: endDtGroup,
+ days: {}
+ };
+ if (this.groupBy == 'week') {
+ group.weekNo = weekInfo.weekNo;
+ group.weekYear = weekInfo.weekYear;
+ }
+ groups[groupKey] = group;
+ }
+
+ // Add event to list of events for the day on which the event starts.
+ day = group.days[dayKey];
+ if (typeof day == 'undefined') {
+ day = {
+ date: startDtEvent,
+ events: Ext.create(Ext.util.MixedCollection) // Use a MixedCollection here such that we can use the existing event sorting function that works with MixedCollections
+ };
+ group.days[dayKey] = day;
+ }
+ day.events.add(event.data[M.EventId.name], event);
+ }
+ }
+
+ // Sort events within days, days within groups and groups among themselves. To be able to sort, the groups and days
+ // objects are converted to arrays.
+ for (groupKey in groups) {
+ group = groups[groupKey];
+
+ daysArray = [];
+ for (dayKey in group.days) {
+ var day = group.days[dayKey];
+ this.sortEventRecordsForDay(day.events);
+ daysArray.push(day);
+ }
+ // Sort days within group
+ daysArray.sort(function(a, b) {
+ return Dt.compare(b.date, a.date);
+ });
+ group.days = daysArray;
+ groupsArray.push(group);
+ }
+ // Sort groups
+ groupsArray.sort(function(a, b) {
+ return Dt.compare(b.startDt, a.startDt);
+ });
+
+ return groupsArray;
+ },
+
+ /**
+ *
Returns the template event data for rendering events in agenda style (property {@link #simpleList} is false).
+ *
+ *
Returns an array of day objects containing an array of events. The following illustrates the returned data structure.
+ *
+ * @return {Array}
+ */
+ getTemplateEventDataForAgenda : function(){
+ var M = Extensible.calendar.data.EventMappings,
+ events,
+ event,
+ days = {},
+ daysArray = [],
+ viewBounds = this.getViewBounds(),
+ startDtView = viewBounds.start,
+ endDtView = viewBounds.end,
+ startDtEvent, endDtEvent,
+ currDt,
+ Dt = Extensible.Date;
+
+ // Loop over all events within view period. Single-day events produce one item per event.
+ // Multi-day events produce multiple items, one for each day.
+ events = this.getEvents();
+ for (var i=0; i 0 ? currDt = startDtView : currDt = startDtEvent;
+
+ // Loop over each day the event spans and that is within the view period.
+ while (Dt.compare(currDt, endDtEvent) >= 0 && Dt.compare(currDt, endDtView) >= 0) {
+ var day;
+
+ // Check if already a day record exists for the current day
+ day = days[Ext.Date.format(currDt, 'dmy')];
+ if (typeof day == 'undefined') {
+ day = {
+ date: currDt,
+ events: Ext.create(Ext.util.MixedCollection) // Use a MixedCollection here such that we can use the existing event sorting function that works with MixedCollections
+ };
+ days[Ext.Date.format(currDt, 'dmy')] = day;
+ }
+ day.events.add(event.data[M.EventId.name], event);
+
+ currDt = Ext.Date.add(currDt, Ext.Date.DAY, 1);
+ }
+ }
+
+ // Convert days from object to array and sort events within a day
+ for (date in days) {
+ this.sortEventRecordsForDay(days[date].events);
+ daysArray.push(days[date]);
+ }
+ // Sort days
+ daysArray.sort(function(a, b) {
+ return Dt.compare(b.date, a.date);
+ });
+
+ return daysArray;
+ },
+
+ /**
+ * Returns a list of events for the current view period. Attributes needed for processing the
+ * template are applied to the events (event colors, custom classes).
+ *
+ * @return {Ext.util.MixedCollection} Returns a collection of events objects of class
+ * Extensible.calendar.data.EventModel.
+ */
+ getEvents : function(){
+ var M = Extensible.calendar.data.EventMappings,
+ CM = Extensible.calendar.data.CalendarMappings,
+ events,
+ extraClasses,
+ colorCls = 'x-cal-default',
+
+ events = this.store.queryBy(function(rec){
+ return this.isEventVisible(rec.data);
+ }, this);
+
+ // Loop over all events of view period and apply additional attributes to events that are needed for output.
+ for (var i=0; iFor a given date, returns the number of the week and the year to which the week belongs.
+ *
In different parts of the world weeks are numbered differently. This function covers the
+ * three major conventions. The convention used is determined by the configuration of the first
+ * day of the week.
+ *
+ * First day of week Convention Region
+ * Sunday Week cont. Jan 1 is first week of year USA, Canada, Mexico
+ * Monday Week cont. first Thur is first week of year Most of Europe (ISO 8601 standard)
+ * Saturday Week cont. Jan 1 is first week of year Most of the Middle East
+ *
+ *
+ * @param {Date} date The date for which the week information is calculated.
+ * @return {Object} An object literal with two attributes: weekNo and weekYear.
+ */
+ getWeekInfo : function(date){
+ var weekNo,
+ weekYear,
+ oneJan;
+
+ // Determine week number
+ if (this.startDay == 0) {
+ // Week starts on Sunday
+ // Code from http://javascript.about.com/library/blweekyear.htm
+ oneJan = new Date(date.getFullYear(), 0, 1);
+ weekNo = Math.ceil((((date.getTime() - oneJan.getTime()) / 86400000) + oneJan.getDay() + 1) / 7);
+ } else if (this.startDay == 6) {
+ // Week starts on Saturday
+ oneJan = new Date(date.getFullYear(), 0, 1);
+ weekNo = Math.ceil((((date.getTime() - oneJan.getTime()) / 86400000) + oneJan.getDay() + 1) / 7);
+ } else {
+ // Week starts on Monday
+ weekNo = parseInt(Ext.Date.format(date, 'W'), 10)
+ }
+
+ // Determine year to which week belongs.
+ if (date.getMonth() == 11 && weekNo == 1) {
+ // Date is at the end of December but week belongs to next year.
+ weekYear = date.getFullYear() + 1;
+ } else if (date.getMonth() == 0 && weekNo > 50) {
+ // Date is at the beginning of January but week belongs to previous year.
+ weekYear = date.getFullYear() - 1;
+ } else {
+ weekYear = date.getFullYear();
+ }
+
+ return {
+ weekNo: weekNo,
+ weekYear: weekYear
+ }
+ },
+
+
+ // private
+ afterRender : function(){
+ if(!this.tpl){
+ this.tpl = Ext.create('Extensible.calendar.template.AgendaBody', {
+ id: this.id,
+ simpleList: this.simpleList,
+ groupBy: this.groupBy,
+ linkDatesToDayView: this.linkDatesToDayView,
+ defaultEventTitleText: this.defaultEventTitleText,
+ prevLinkSelector: this.prevLinkSelector,
+ nextLinkSelector: this.nextLinkSelector
+ });
+ this.tpl.compile();
+ }
+ this.addCls('ext-cal-agenda-bd ext-cal-ct');
+
+ this.callParent(arguments);
+ },
+
+ /**
+ * Returns an object containing all key/value params to be passed when loading the event store.
+ * Override this function if you need to pass additional parameters when loading the store.
+ * @return {Object} An object containing all params to be sent when loading the event store
+ */
+ getStoreParams : function(){
+ // This is needed if you require the default start and end dates to be included
+ var params = this.getStoreDateParams();
+
+ // Apply filter settings from the header form
+ Ext.applyIf(params, this.filterConfig);
+
+ // Here is where you can add additional custom params, e.g.:
+ // params.now = Ext.Date.format(new Date(), this.dateParamFormat);
+ // params.foo = 'bar';
+ // params.number = 123;
+
+ return params;
+ },
+
+ // private
+ refresh : function(reloadData){
+ Extensible.log('refresh (AgendaView)');
+ this.callParent(arguments);
+ },
+
+ /**
+ * This method is here to fulfill the interface of {@link Extensible.view.AbstractCalendar}. It does not
+ * do anything except to confirm that events have been rendered. For this view, events are rendered by method
+ * {@link #renderTemplate}.
+ */
+ renderItems : function(){
+ this.fireEvent('eventsrendered', this);
+ },
+
+ /**
+ * Sets the filter configuration to be used when calculating view bounds and loading events.
+ * @param {Object} filterConfig An object of key/value pairs representing filter conditions.
+ */
+ setFilterConfig: function(filterConfig) {
+ this.filterConfig = filterConfig;
+ this.tpl.showEventDetails = this.filterConfig.details ? true: false;
+ this.groupBy = this.filterConfig.groupby;
+ this.tpl.groupBy = this.filterConfig.groupby;
+ },
+
+ /**
+ * Helper function that converts a string expressing a date period into an object that can be used as
+ * parameter for the {@link Extensible.Date#add} function.
+ * @param {String} period Supported values are: day, week, month, 3months and
+ * year. If an unknown value is passed for period, then months is used.
+ * @param {Boolean} subtract If true, then the return object specifies a subtraction operation instead of an
+ * addition operation. Defaults to false.
+ */
+ getDateAddParam: function(period, subtract) {
+ subtract = subtract || false;
+
+ if (period == 'day') {
+ return subtract ? {days: -1} : {days: 1};
+ } else if (period == 'week') {
+ return subtract ? {days: -7} : {days: 7};
+ } else if (period == '3months') {
+ return subtract ? {months: -3} : {months: 3};
+ } else if (period == 'year') {
+ return subtract ? {years: -1} : {years: 1};
+ } else {
+ return subtract ? {months: -1} : {months: 1};
+ }
+ },
+
+ // private
+ setViewBounds : function(startDate){
+ var me = this,
+ Dt = Extensible.Date,
+ start = startDate || me.startDate,
+ period = me.filterConfig.period || this.dateRangeDefault,
+ addParam;
+
+ addParam = this.getDateAddParam(period, false);
+ addParam.seconds = -1;
+
+ me.viewStart = Dt.add(start, {days: 0, clearTime: true});
+ me.viewEnd = Dt.add(me.viewStart, addParam);
+ },
+
+ // private
+ getDayEl : function(dt){
+ return Ext.get(this.getDayId(dt));
+ },
+
+ // private
+ getDayId : function(dt){
+ if(Ext.isDate(dt)){
+ dt = Ext.Date.format(dt, 'Ymd');
+ }
+ return this.id + this.dayElIdDelimiter + dt;
+ },
+
+ /**
+ * Moves the view one period forward.
+ * @return {Date} The new view start date
+ */
+ moveNext : function(/*private*/reload){
+ var me = this,
+ period = me.filterConfig.period || this.dateRangeDefault,
+ addParam;
+ addParam = this.getDateAddParam(period);
+ return this.moveTo(Extensible.Date.add(this.viewStart, addParam), reload);
+ },
+
+ /**
+ * Moves the view one day backwards.
+ * @return {Date} The new view start date
+ */
+ movePrev : function(/*private*/reload){
+ var me = this,
+ period = me.filterConfig.period || this.dateRangeDefault,
+ addParam;
+ addParam = this.getDateAddParam(period, true);
+ return this.moveTo(Extensible.Date.add(this.viewStart, addParam), reload);
+ },
+
+ /**
+ * Returns true if the view is currently displaying today's date, else false.
+ * @return {Boolean} True or false
+ */
+ isToday : function(){
+ var today = Ext.Date.clearTime(new Date()).getTime();
+ return this.viewStart.getTime() == today;
+ },
+
+ // private
+ onClick : function(e, t){
+ var el;
+
+ // Handle click on an existing event
+ if(Extensible.calendar.view.AgendaBody.superclass.onClick.apply(this, arguments)){
+ // The superclass handled the click already so exit
+ return;
+ }
+
+ // Handle click on a date. Jump to day view if active.
+ if(el = e.getTarget(this.dayLinkSelector, 3)){
+ var dt = el.id.split(this.dayLinkIdDelimiter)[1];
+ this.fireEvent('dayclick', this, Ext.Date.parseDate(dt, 'Ymd'));
+ }
+
+ // Handle click on next or previous links
+ // ext-cal-bd-prev-link
+ if(el = e.getTarget('.' + this.prevLinkSelector, 3)){
+ this.ownerCalendarView.movePrev(true);
+ }
+ if(el = e.getTarget('.' + this.nextLinkSelector, 3)){
+ this.ownerCalendarView.moveNext(true);
+ }
+
+ },
+
+ // inherited docs
+ isActiveView: function() {
+ var calendarPanel = this.ownerCalendarPanel,
+ calendarView = this.ownerCalendarView;
+ return (calendarPanel && calendarView && calendarPanel.getActiveView().id === calendarView.id);
+ }
+
+});
\ No newline at end of file
diff --git a/src/calendar/view/AgendaHeader.js b/src/calendar/view/AgendaHeader.js
new file mode 100644
index 00000000..a441400f
--- /dev/null
+++ b/src/calendar/view/AgendaHeader.js
@@ -0,0 +1,476 @@
+/**
+ * @class Extensible.calendar.view.AgendaHeader
+ * @extends Ext.form.Panel
+ *
+ *
This class is currently beta code and the API is still subject to change before the next release.
+ *
+ *
This is the header area container within the {@link Extensible.calendar.view.Agenda Agenda} view. Normally you should
+ * not need to use this class directly -- instead you should use {@link Extensible.calendar.view.Agenda Agenda} view which
+ * aggregates this class and the {@link Extensible.calendar.view.AgendaBody AgendaBody} view into a single unified view
+ * presented by {@link Extensible.calendar.CalendarPanel CalendarPanel}.
+ *
+ *
This header consists of a form and a toolbar. Both can easily be extended or hidden. The header form is intended
+ * to host filter and display configuration settings while the toolbar is useful to offers actions that can be applied
+ * to the list of events, for example to add events or print events.
+ *
+ *
To modify or hide the form and the toolbar, override functions {@link #getFormConfig} and {@link #getToolbarConfig}.
+ * The form field values will be submitted automatically as parameters of requests to load the event store. They can
+ * be used on the backend to select or filter events.
If true, a simple list of events is displayed, else, an agenda-style list is displayed.
+ * Defaults to false.
+ */
+ simpleList: false,
+
+ /**
+ * @cfg {Boolean} readOnly
+ * True to prevent the view from providing CRUD capabilities, false to enable CRUD (the default).
+ */
+
+ /**
+ * @cfg {String} dateRangeOneDay
+ * The text used for date range option one day.
+ */
+ dateRangeOneDay: 'One day',
+
+ /**
+ * @cfg {String} dateRangeOneWeek
+ * The text used for date range option one week.
+ */
+ dateRangeOneWeek: 'One week',
+
+ /**
+ * @cfg {String} dateRangeOneMonth
+ * The text used for date range option one month.
+ */
+ dateRangeOneMonth: 'One month',
+
+ /**
+ * @cfg {String} dateRangeThreeMonths
+ * The text used for date range option 3 months.
+ */
+ dateRangeThreeMonths: 'Three months',
+
+ /**
+ * @cfg {String} dateRangeOneYear
+ * The text used for date range option one year.
+ */
+ dateRangeOneYear: 'One year',
+
+ /**
+ * @cfg {String} dateRangeText
+ * The label text used for the date range field.
+ */
+ dateRangeText: 'Date range',
+
+ /**
+ * @cfg {String} groupByMonths
+ * The text used for group by option Month.
+ */
+ groupByMonths: 'Month',
+
+ /**
+ * @cfg {String} groupByWeek
+ * The text used for group by option Week.
+ */
+ groupByWeek: 'Week',
+
+ /**
+ * @cfg {String} groupByNone
+ * The text used for group by option None.
+ */
+ groupByNone: 'None',
+
+ /**
+ * @cfg {String} groupByText
+ * The label text used for the group by field.
+ */
+ groupByText: 'Group by',
+
+ /**
+ * @cfg {String} showDetailsText
+ * The label text used for the details field.
+ */
+ showDetailsText: 'Show details',
+
+ /**
+ * @cfg {String} addBtnText
+ * The caption used for the add button.
+ */
+ addBtnText: 'Add event',
+
+ /**
+ * @cfg {String} resetBtnText
+ * The caption used for the reset button.
+ */
+ resetBtnText: 'Reset',
+
+ /**
+ * @cfg {String} dateRangeDefault
+ * Defines the default value for the date range input field. Defaults to month. See
+ * {@link #getDateRangeOptions} for a list of supported date range default values.
+ */
+ dateRangeDefault: 'month',
+
+ /**
+ * @cfg {String} groupBy
+ * Defines the default value for the groupby input field. Defaults to none. See
+ * {@link #getGroupByOptions} for a list of supported default values.
+ */
+ groupBy: 'none',
+
+ /**
+ * @cfg {Boolean} showDetailsDefault
+ * Defines the default value for the checkbox to show details. Defaults to false.
+ */
+ showDetailsDefault: false,
+
+ // private configs
+ cls: 'ext-cal-agenda-hd',
+ preventHeader: true,
+ autoHeight: true,
+ border: 0,
+ defaults: {
+ labelWidth: 100
+ },
+
+
+ // private
+ initComponent : function(){
+ var tbItems = this.getToolbarConfig();
+
+ this.dateRangeOptions = this.getDateRangeOptions();
+ this.groupByOptions = this.getGroupByOptions();
+
+ this.items = this.getFormConfig();
+ if (this.items.length == 0) {
+ this.bodyStyle = {padding: '0px', border: 0};
+ } else {
+ this.bodyStyle = {padding: '10px 10px 5px 10px'};
+ }
+
+ if (tbItems.length > 0) {
+ this.dockedItems = [{
+ xtype: 'toolbar',
+ dock: 'bottom',
+ ui: 'default',
+ cls: 'ext-cal-agenda-hd-tb',
+ items: tbItems
+ }];
+ }
+
+ if (this.items.length == 0 && tbItems.length == 0) {
+ this.style = {borderBottom: '0px'};
+ }
+
+ this.callParent(arguments);
+
+ this.addEvents({
+ /**
+ * @event formchange
+ * Fires after the filter form changes.
+ * @param {Extensible.calendar.view.AgendaHeader} this
+ * @param {Ext.form.Basic} form The filter form.
+ * @param {Ext.form.field.Field} field Form field that changed.
+ * @param {Object} newValue New form field value.
+ * @param {Object} oldValue Old form field value.
+ * @param {Object} eOpts The options object passed to {@link Ext.util.Observable.addListener}.
+ */
+ formchange: true,
+
+ /**
+ * @event addevent
+ * Fires after the user clicks the add event button.
+ * @param {Extensible.calendar.view.AgendaHeader} this
+ * @param {Ext.button.Button} button The button clicked.
+ * @param {Event} event
+ * @param {Object} eOpts The options object passed to {@link Ext.util.Observable.addListener}.
+ */
+ addevent: true
+ });
+
+ },
+
+ /**
+ *
This function is called by this form panel to obtain the definition of form fields. Override this function to
+ * modify the form fields displayed by this panel.
+ * @return {Array} An array of Object
+ */
+ getFormConfig: function() {
+ var formItems = {
+ xtype: 'fieldcontainer',
+ labelWidth: 100,
+ height: 45,
+ fieldDefaults: {
+ // padding: 20,
+ labelAlign: 'top',
+ width: 150,
+ margins: '0 20 0 0'
+ },
+ layout: 'hbox',
+ items: [{
+ xtype: 'combo',
+ id: this.id+'-daterange',
+ mode: 'local',
+ value: this.dateRangeDefault,
+ triggerAction: 'all',
+ forceSelection: true,
+ editable: false,
+ fieldLabel: this.dateRangeText,
+ name: 'period',
+ displayField: 'name',
+ valueField: 'value',
+ queryMode: 'local',
+ store: Ext.create('Ext.data.Store', {
+ fields : ['name', 'value'],
+ data : this.dateRangeOptions
+ }),
+ // This fixes a bug that a blank item is not properly supported. See Sencha forum and source of Ext.view.BoundList.
+ // http://www.sencha.com/forum/showthread.php?41431-Empty-string-as-ComboBox-entry-text&p=195882
+ tpl: '
{name}
'
+ }]
+ };
+
+ if (this.simpleList) {
+ formItems.items.push({
+ xtype: 'combo',
+ id: this.id+'-groupby',
+ mode: 'local',
+ value: this.groupBy,
+ triggerAction: 'all',
+ forceSelection: true,
+ editable: false,
+ fieldLabel: this.groupByText,
+ name: 'groupby',
+ displayField: 'name',
+ valueField: 'value',
+ queryMode: 'local',
+ store: Ext.create('Ext.data.Store', {
+ fields : ['name', 'value'],
+ data : this.groupByOptions
+ }),
+ // This fixes a bug that a blank item is not properly supported. See Sencha forum and source of Ext.view.BoundList.
+ // http://www.sencha.com/forum/showthread.php?41431-Empty-string-as-ComboBox-entry-text&p=195882
+ tpl: '
This function is called by this form panel to obtain the definition of the toolbar content. Override this function to
+ * modify what goes into the toolbar. If no toolbar is required, return an empty array from this function.
Returns the options available in the date range combo box. Override this function to change the available
+ * options for the date range select list.
+ *
Returns an array of objects where each object has two attributes name and value. The
+ * attribute name is the display string, the attribute value is the value returned as the
+ * field value of the combo box. The default configuration is:
Returns the options available in the group by combo box. Override this function to change the available
+ * options for the group by select list.
+ *
Returns an array of objects where each object has two attributes name and value. The
+ * attribute name is the display string, the attribute value is the value returned as the
+ * field value of the combo box. The default configuration is: