diff --git a/Extensible-config.js b/Extensible-config.js
index 2aa58c01..2086a574 100644
--- a/Extensible-config.js
+++ b/Extensible-config.js
@@ -3,15 +3,15 @@ Extensible = {
};
/**
* =================================================================================================
- *
+ *
* THIS FILE IS FOR *DEV* MODE ONLY, NOT FOR PRODUCTION USE!
- *
+ *
* =================================================================================================
- *
+ *
* This is intended as a development mode only convenience so that you can configure all include
* paths for all Extensible examples in one place. For production deployment you should configure
* your application with your own custom includes and/or Ext.Loader configuration directly.
- *
+ *
* =================================================================================================
*/
Extensible.Config = {
@@ -21,26 +21,29 @@ Extensible.Config = {
defaults: {
/**
* The mode to use for loading framework files. Valid values are:
- *
+ *
* - 'release': minified single file (e.g. ext-all.js)
* - 'debug': (default) non-minifed single file (e.g. ext-all-debug.js)
* - 'dynamic': uses Ext.Loader to load classes individually (e.g., ext.js). NOTE: this
* option does not work for IE, which will be defaulted to the 'debug' option.
- *
+ * - 'dynamic-extensible': Loads the Extensible framework dynically and the EXT JS framework from a
+ * non-minified single file. This loads much faster than the 'dynamic' mode. NOTE: This
+ * option does not work for IE, which will be defaulted to the 'debug' option.
+ *
* Typically the default of 'debug' is the best trade-off between code readability and
* load/execution speed. If you need to step into framework files frequently during
* debugging you might switch to 'dynamic' mode -- it is much slower during initial
* page load but generally provides a faster and easier debugging experience.
- *
+ *
* Note that for debug and release modes to reflect any code or CSS changes made to Extensible
* files you must rebuild the framework after each change using the scripts provided under
* the `/build` folder (requires Java). If you cannot build the framework and you've made any
* changes to Extensible files you should use dynamic mode to ensure that changes are reflected.
- *
+ *
* @config {String} mode
*/
mode: 'debug',
-
+
/**
* The root path to the Ext JS framework (defaults to loading 4.2.0 from the Sencha CDN via
* `http://cdn.sencha.com/ext/gpl/4.2.0/`). Path should be absolute and should end with a '/'.
@@ -61,23 +64,23 @@ Extensible.Config = {
*
* @config {String} extJsRoot
*/
- extJsRoot: 'http://cdn.sencha.com/ext/gpl/4.2.0/',
+ extJsRoot: 'http://cdn.sencha.com/ext/gpl/5.1.0/',
/**
* The root path to the Extensible framework (defaults to the current url of this script file,
* 'Extensible-config.js', which is shipped in the root folder of Extensible). Path should
* be absolute and should end with a '/'.
- *
+ *
* Alternate example values:
- *
+ *
* // A custom absolute path:
* http://localhost/extensible/
* http://mydomain/extensible/1.0.1/
- *
+ *
* @config {String} extensibleRoot
*/
extensibleRoot: null, // initialized dynamically in getSdkPath()
-
+
/**
* True to allow the default browser behavior of caching the Extensible JS and CSS files
* after initial load (defaults to true), or false to append a unique cache-buster parameter
@@ -85,17 +88,17 @@ Extensible.Config = {
* actively changing and debugging Extensible code). If true, the current version number of
* Extensible will still be used to force a reload with each new version of the framework, but
* after the initial load of each version the cached files will be used.
- *
+ *
* This option only applies when using `debug` or `dynamic` modes. In `release` mode the Extensible
* version number will be used to ensure that Extensible files are always cached after the initial
* load of each release and this option will be ignored. Note that when using `dynamic` mode you
* would additionally have to ensure that the Ext.Loader's `disableCaching` option is true in order
- * to add the cache buster parameter to each dynamically-loaded class.
- *
+ * to add the cache buster parameter to each dynamically-loaded class.
+ *
* Note that this option does not affect the caching of Ext JS files in any way. If you are
* using dynamic loading, the Ext Loader will govern caching, otherwise the default browser
* caching will be in effect.
- *
+ *
* @config {Boolean} cacheExtensible
*/
cacheExtensible: true,
@@ -103,13 +106,13 @@ Extensible.Config = {
/**
* Language files to load for the Ext JS and Extensible frameworks. Valid values are ISO language codes of
* supported languages. See directory src/locale for a list of supported languages. Examples are:
- *
+ *
* - 'en'
* - 'en_GB'
* - 'de'
* - 'fr'
* - etc...
- *
+ *
* NOTE: This setting will NOT work for Ext versions < 4.1 due to how the locale files were written
* in 4.0.x. Because the 4.0.x locale files check for existence of classes by reference rather than
* by name, they do not play nicely when loaded asynchronously (Ext may load later, causing runtime
@@ -119,37 +122,43 @@ Extensible.Config = {
* work consistently with all Ext 4.x versions (just uses the Ext default English strings). As long
* as you are using 4.1+ feel free to enable this by setting the value to any supported locale code.
*/
- language: null
+ language: null,
+
+ /**
+ * Name of theme used. Supported values are: 'neptune', nepture-touch', 'crisp', 'crisp-touch'.
+ */
+ theme: 'neptune'
},
-
+
/**
* Sets up all configurable properties and writes all includes to the document.
*/
init: function() {
var me = this,
config = window.ExtensibleDefaults || {};
-
+
me.isIE = /msie/.test(navigator.userAgent.toLowerCase());
-
+
me.mode = config.mode || me.defaults.mode;
me.extJsRoot = config.extJsRoot || me.defaults.extJsRoot;
me.extensibleRoot = config.extensibleRoot || me.defaults.extensibleRoot || me.getSdkPath();
me.cacheExtensible = config.cacheExtensible || me.defaults.cacheExtensible;
me.language = config.language || me.defaults.language;
+ me.theme = config.theme || me.defaults.theme;
me.adjustPaths();
me.writeIncludes();
},
-
+
// private -- returns the current url to this script file, which is shipped in the SDK root folder
getSdkPath: function() {
var scripts = document.getElementsByTagName('script'),
thisScriptSrc = scripts[scripts.length - 1].src,
sdkPath = thisScriptSrc.substring(0, thisScriptSrc.lastIndexOf('/') + 1);
-
+
return sdkPath;
},
-
+
// private -- helper function for ease of deployment
adjustPaths: function() {
if (this.extensibleRoot.indexOf('ext.ensible.com') > -1) {
@@ -157,58 +166,54 @@ Extensible.Config = {
this.mode = 'release';
}
},
-
+
includeStylesheet: function(filePath) {
document.write('');
},
-
+
includeScript: function(filePath) {
document.write('');
},
-
+
// private -- write out the CSS and script includes to the document
writeIncludes: function() {
var me = this,
- cacheBuster = '?_dc=' + (me.cacheExtensible ? Extensible.version : (+new Date)),
- suffixExt = '',
- suffixExtensible = '';
-
- switch (me.mode) {
- case 'debug':
- suffixExt = '-all-debug';
- suffixExtensible = '-all-debug';
- break;
-
- case 'release':
- suffixExt = '-all';
- suffixExtensible = '-all'
- // For release we want to refresh the cache on first load, but allow caching
- // after that, so use the version number instead of a unique string
- cacheBuster = '?_dc=' + Extensible.version;
- break;
-
- default:
- // IE does not work in dynamic mode for the Extensible examples currently
- // based on how it (mis)handles loading of scripts when mixing includes
- // and in-page scripts. Make sure IE always uses the regular debug versions.
- if (me.isIE) {
- suffixExt = '-all-debug';
- suffixExtensible = '-all-debug';
- }
- else {
- suffixExt = '-debug';
- suffixExtensible = '-bootstrap';
- }
+ cacheBuster = '?_dc=' + (me.cacheExtensible ? Extensible.version : (+new Date));
+
+ // Include style sheets
+ me.includeStylesheet(me.extJsRoot + '/build/packages/ext-theme-' + me.theme + '/build/resources/ext-theme-' + me.theme + '-all.css');
+ if (me.mode === 'release') {
+ me.includeStylesheet(me.extensibleRoot + 'resources/css/extensible-all.css' + cacheBuster);
+ } else {
+ me.includeStylesheet(me.extensibleRoot + 'resources/css/calendar.css' + cacheBuster);
+ me.includeStylesheet(me.extensibleRoot + 'resources/css/calendar-colors.css' + cacheBuster);
+ me.includeStylesheet(me.extensibleRoot + 'resources/css/recurrence.css' + cacheBuster);
+ }
+ me.includeStylesheet(me.extensibleRoot + 'examples/examples.css' + cacheBuster);
+
+ // Include JS files
+ if (me.mode === 'debug' || me.isIE) {
+ // IE does not work in dynamic mode for the Extensible examples currently
+ // based on how it (mis)handles loading of scripts when mixing includes
+ // and in-page scripts. Make sure IE always uses the regular debug versions.
+ me.includeScript(me.extJsRoot + 'build/ext-all-debug.js');
+ me.includeScript(me.extensibleRoot + 'lib/extensible-all-debug.js' + cacheBuster);
+ } else if (me.mode === 'release') {
+ // For release we want to refresh the cache on first load, but allow caching
+ // after that, so use the version number instead of a unique string
+ cacheBuster = '?_dc=' + Extensible.version;
+ me.includeScript(me.extJsRoot + 'build/ext-all.js');
+ me.includeScript(me.extensibleRoot + 'lib/extensible-all.js' + cacheBuster);
+ } else {
+ if (me.mode === 'dynamic-extensible') {
+ me.includeScript(me.extJsRoot + 'build/ext-all-debug.js');
+ } else {
+ me.includeScript(me.extJsRoot + 'build/ext-debug.js');
+ }
+ me.includeScript(me.extensibleRoot + 'lib/extensible-bootstrap.js' + cacheBuster);
}
-
- me.includeStylesheet(me.extJsRoot + 'resources/css/ext-all.css');
- me.includeStylesheet(me.extensibleRoot + 'resources/css/extensible-all.css' + cacheBuster);
- me.includeStylesheet(me.extensibleRoot + 'examples/examples.css?_dc=' + Extensible.version);
-
- me.includeScript(me.extJsRoot + 'ext' + suffixExt + '.js');
- me.includeScript(me.extensibleRoot + 'lib/extensible' + suffixExtensible + '.js' + cacheBuster);
me.includeScript(me.extensibleRoot + 'examples/examples.js?_dc=' + Extensible.version);
-
+
if (me.language) {
me.includeScript(me.extJsRoot + 'locale/ext-lang-' + me.language + '.js');
me.includeScript(me.extensibleRoot + 'src/locale/extensible-lang-' + me.language + '.js' + cacheBuster);
diff --git a/build/resources/extensible.jsb2 b/build/resources/extensible.jsb2
index 36777d8a..a9da86d8 100644
--- a/build/resources/extensible.jsb2
+++ b/build/resources/extensible.jsb2
@@ -38,6 +38,9 @@
},{
"text": "Month.js",
"path": "../../src/calendar/template/"
+ },{
+ "text": "AgendaBody.js",
+ "path": "../../src/calendar/template/"
},{
"text": "CalendarScrollManager.js",
"path": "../../src/calendar/dd/"
@@ -128,6 +131,15 @@
},{
"text": "MultiWeek.js",
"path": "../../src/calendar/view/"
+ },{
+ "text": "AgendaHeader.js",
+ "path": "../../src/calendar/view/"
+ },{
+ "text": "AgendaBody.js",
+ "path": "../../src/calendar/view/"
+ },{
+ "text": "Agenda.js",
+ "path": "../../src/calendar/view/"
},{
"text": "CalendarPanel.js",
"path": "../../src/calendar/"
diff --git a/examples/calendar/TestApp/App.js b/examples/calendar/TestApp/App.js
index c2b18f5d..21382730 100644
--- a/examples/calendar/TestApp/App.js
+++ b/examples/calendar/TestApp/App.js
@@ -1,6 +1,6 @@
Ext.Loader.setConfig({
enabled: true,
- //disableCaching: false,
+ disableCaching: false,
paths: {
"Extensible": "../../../src",
"Extensible.example": "../.."
@@ -40,7 +40,11 @@ Ext.define('Extensible.example.calendar.TestApp.App', {
// of MemoryEventStore to see how automatic store messaging is implemented.
autoMsg: false
});
-
+
+ // Make the calendar stateful. This is optional. If set, the application will remember hidden
+ // calendars in the calendar list panel.
+ Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
+
// This is the app UI layout code. All of the calendar views are subcomponents of
// CalendarPanel, but the app title bar and sidebar/navigation calendar are separate
// pieces that are composed in app-specific layout code since they could be omitted
@@ -105,6 +109,7 @@ Ext.define('Extensible.example.calendar.TestApp.App', {
//viewStartHour: 6,
//viewEndHour: 18,
//minEventDisplayMinutes: 15
+ startDay: 0,
showTime: false
},
@@ -119,7 +124,19 @@ Ext.define('Extensible.example.calendar.TestApp.App', {
multiWeekViewCfg: {
//weekCount: 3
},
-
+
+ agendaViewCfg: {
+ linkDatesToDayView: true,
+ dateRangeDefault: '3months'
+ },
+
+ listViewCfg: {
+ linkDatesToDayView: true,
+ dateRangeDefault: '3months',
+ simpleList: true,
+ groupBy: 'month'
+ },
+
// Some optional CalendarPanel configs to experiment with:
//readOnly: true,
//showDayView: false,
@@ -127,6 +144,8 @@ Ext.define('Extensible.example.calendar.TestApp.App', {
//showWeekView: false,
//showMultiWeekView: false,
//showMonthView: false,
+ showAgendaView: true,
+ showListView: true,
//showNavBar: false,
//showTodayText: false,
//showTime: false,
@@ -135,6 +154,12 @@ Ext.define('Extensible.example.calendar.TestApp.App', {
//title: 'My Calendar', // the header of the calendar, could be a subtitle for the app
listeners: {
+ 'datechange': {
+ fn: function(vw, startDt, viewStart, viewEnd){
+ this.updateTitle(viewStart, viewEnd);
+ },
+ scope: this
+ },
'eventclick': {
fn: function(vw, rec, el){
this.clearMsg();
diff --git a/examples/calendar/basic.js b/examples/calendar/basic.js
index d7574900..f4a8c7f2 100644
--- a/examples/calendar/basic.js
+++ b/examples/calendar/basic.js
@@ -26,7 +26,10 @@ Ext.onReady(function(){
renderTo: 'simple',
title: 'Basic Calendar',
width: 700,
- height: 500
+ height: 500,
+ activeItem: 3, // default to month view
+ showAgendaView: true,
+ showListView: true
});
//
@@ -38,6 +41,8 @@ Ext.onReady(function(){
eventStore: eventStore,
renderTo: 'panel',
title: 'Calendar with Panel Configs',
+ showAgendaView: true,
+ showListView: true,
activeItem: 1, // default to week view
width: 700,
height: 500,
diff --git a/examples/calendar/custom-mappings.js b/examples/calendar/custom-mappings.js
index e66e8272..9d2e5abd 100644
--- a/examples/calendar/custom-mappings.js
+++ b/examples/calendar/custom-mappings.js
@@ -81,6 +81,9 @@ Ext.onReady(function(){
calendarStore: calendarStore,
renderTo: 'cal',
title: 'Custom Event Mappings',
+ showAgendaView: true,
+ showListView: true,
+ activeItem: 3, // default to month view
width: 800,
height: 700
});
diff --git a/examples/calendar/custom-views.html b/examples/calendar/custom-views.html
index edf67f9d..90a293ee 100644
--- a/examples/calendar/custom-views.html
+++ b/examples/calendar/custom-views.html
@@ -31,7 +31,7 @@
.x-cal-default-ad .ext-cal-evm,
.x-cal-default .ext-cal-picker-icon,
.x-cal-default-x dl,
- .x-calendar-list-menu li em .x-cal-default {
+ .x-calendar-agenda-menu li em .x-cal-default {
background: #59638F;
}
diff --git a/examples/calendar/data/Events.js b/examples/calendar/data/Events.js
index 6dc74aac..6887d2b8 100644
--- a/examples/calendar/data/Events.js
+++ b/examples/calendar/data/Events.js
@@ -8,7 +8,7 @@ Ext.define('Extensible.example.calendar.data.Events', {
s = (s || 0);
return Ext.Date.add(today, Ext.Date.SECOND, d + h + m + s);
};
-
+
return {
"evts" : [{
"id" : 1001,
@@ -24,7 +24,7 @@ Ext.define('Extensible.example.calendar.data.Events', {
"start" : makeDate(0, 11, 30),
"end" : makeDate(0, 13),
"loc" : "Chuy's!",
- "url" : "http : //chuys.com",
+ "url" : "http://chuys.com",
"notes" : "Order the queso",
"rem" : "15"
},{
diff --git a/examples/calendar/data/EventsCustom.js b/examples/calendar/data/EventsCustom.js
index 2713bee4..58317c19 100644
--- a/examples/calendar/data/EventsCustom.js
+++ b/examples/calendar/data/EventsCustom.js
@@ -16,7 +16,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1001",
"cal_id" : "C1",
"evt_title" : "Vacation",
- "start_dt" : makeDate(-20, 10),
+ "start_dt" : makeDate(-20, 10),
"end_dt" : makeDate(-10, 15),
"full_desc" : "Have fun",
"created_by" : "Brian"
@@ -24,7 +24,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1002",
"cal_id" : "C2",
"evt_title" : "Lunch with Matt",
- "start_dt" : makeDate(0, 11, 30),
+ "start_dt" : makeDate(0, 11, 30),
"end_dt" : makeDate(0, 13),
"location" : "Chuy's!",
"link_url" :"http://chuys.com",
@@ -36,14 +36,14 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1003",
"cal_id" : "C3",
"evt_title" : "Project due",
- "start_dt" : makeDate(0, 15),
+ "start_dt" : makeDate(0, 15),
"end_dt" : makeDate(0, 15),
"created_by" : "Brian"
},{
"evt_id" : "A-1004",
"cal_id" : "C1",
"evt_title" : "Sarah's birthday",
- "start_dt" : Ext.Date.clone(today),
+ "start_dt" : Ext.Date.clone(today),
"end_dt" : Ext.Date.clone(today),
"full_desc" : "Need to get a gift",
"all_day" : true,
@@ -52,7 +52,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1005",
"cal_id" : "C2",
"evt_title" : "A long one...",
- "start_dt" : makeDate(-12),
+ "start_dt" : makeDate(-12),
"end_dt" : makeDate(10, 0, 0, -1),
"all_day" : true,
"created_by" : "Brian",
@@ -61,7 +61,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1006",
"cal_id" : "C3",
"evt_title" : "School holiday",
- "start_dt" : makeDate(5),
+ "start_dt" : makeDate(5),
"end_dt" : makeDate(5),
"all_day" : true,
"reminder" : "2880",
@@ -70,7 +70,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1007",
"cal_id" : "C1",
"evt_title" : "Haircut",
- "start_dt" : makeDate(0, 9),
+ "start_dt" : makeDate(0, 9),
"end_dt" : makeDate(0, 9, 0, 30),
"full_desc" : "Get cash on the way",
"created_by" : "Brian"
@@ -78,7 +78,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1008",
"cal_id" : "C3",
"evt_title" : "An old event",
- "start_dt" : makeDate(-30),
+ "start_dt" : makeDate(-30),
"end_dt" : makeDate(-28),
"all_day" : true,
"created_by" : "Brian"
@@ -86,7 +86,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1009",
"cal_id" : "C2",
"evt_title" : "Board meeting",
- "start_dt" : makeDate(-2, 13),
+ "start_dt" : makeDate(-2, 13),
"end_dt" : makeDate(-2, 18),
"location" : "ABC Inc.",
"reminder" : "60",
@@ -95,7 +95,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1010",
"cal_id" : "C3",
"evt_title" : "Jenny's final exams",
- "start_dt" : makeDate(-2),
+ "start_dt" : makeDate(-2),
"end_dt" : makeDate(3, 0, 0, -1),
"all_day" : true,
"created_by" : "Brian"
@@ -103,7 +103,7 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1011",
"cal_id" : "C1",
"evt_title" : "Movie night",
- "start_dt" : makeDate(2, 19),
+ "start_dt" : makeDate(2, 19),
"end_dt" : makeDate(2, 23),
"full_desc" : "Don't forget the tickets!",
"reminder" : "60",
@@ -112,14 +112,14 @@ Ext.define('Extensible.example.calendar.data.EventsCustom', {
"evt_id" : "A-1012",
"cal_id" : "C4",
"evt_title" : "Gina's basketball tournament",
- "start_dt" : makeDate(8, 8),
+ "start_dt" : makeDate(8, 8),
"end_dt" : makeDate(10, 17),
"created_by" : "Brian"
},{
"evt_id" : "A-1013",
"cal_id" : "C4",
"evt_title" : "Toby's soccer game",
- "start_dt" : makeDate(5, 10),
+ "start_dt" : makeDate(5, 10),
"end_dt" : makeDate(5, 12),
"created_by" : "Brian"
}]
diff --git a/examples/calendar/doc-types.js b/examples/calendar/doc-types.js
index d6416410..8eb2c152 100644
--- a/examples/calendar/doc-types.js
+++ b/examples/calendar/doc-types.js
@@ -49,7 +49,6 @@ Ext.onReady(function(){
listeners: {
'select': {
fn: function(cbo, rec){
- rec = rec[0];
window.location = 'doc-types.php?doctype='+rec.data.name+'&dtd='+rec.data.dtd;
}
}
@@ -75,6 +74,8 @@ Ext.onReady(function(){
}),
renderTo: 'cal',
title: 'Doctype Tester',
+ showAgendaView: true,
+ showListView: true,
activeItem: 1,
width: 800,
height: 700
diff --git a/examples/calendar/localization.js b/examples/calendar/localization.js
index 2270d14d..2d2f9ae7 100644
--- a/examples/calendar/localization.js
+++ b/examples/calendar/localization.js
@@ -60,7 +60,6 @@ Ext.onReady(function() {
listeners: {
'select': {
fn: function(cbo, rec){
- rec = rec[0];
calendarPanel.getEl().mask('Loading '+rec.data.desc+'...');
loadLocale(rec.data.code);
locale = rec.data.desc;
@@ -108,6 +107,13 @@ Ext.onReady(function() {
multiDayViewCfg: {
dayCount: 5
},
+ showAgendaView: true,
+ showListView: true,
+ listViewCfg: {
+ dateRangeDefault: '3months',
+ groupBy: 'month'
+ },
+ activeItem: 4, // default to month view
eventStore: Ext.create('Extensible.calendar.data.MemoryEventStore', {
// defined in ../data/Events.js
data: Ext.create('Extensible.example.calendar.data.Events')
diff --git a/examples/calendar/remote/php/app.php b/examples/calendar/remote/php/app.php
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/calendar/remote/recurrence.js b/examples/calendar/remote/recurrence.js
index 0cca44a7..ce07ffcc 100644
--- a/examples/calendar/remote/recurrence.js
+++ b/examples/calendar/remote/recurrence.js
@@ -1,6 +1,6 @@
Ext.Loader.setConfig({
enabled: true,
- //disableCaching: false,
+ disableCaching: false,
paths: {
"Extensible": "../../../src",
"Extensible.example": "../../"
@@ -49,17 +49,17 @@ Ext.onReady(function() {
// the recurrence-specific data mappings. Typically RRule and Duration are the only
// values that need to be persisted and returned with events, and they are the only ones
// mapped to columns in the MySQL database:
- RRule: {name: 'RRule', mapping: 'rrule', type: 'string', useNull: true},
- Duration: {name: 'Duration', mapping: 'duration', defaultValue: -1, useNull: true, type: 'int'},
+ RRule: {name: 'RRule', mapping: 'rrule', type: 'string', allowNull: true},
+ Duration: {name: 'Duration', mapping: 'duration', defaultValue: -1, allowNull: true, type: 'int'},
// These additional values are required for processing recurring events properly,
// but are either calculated or used only during editing. They still must be mapped
// to whatever the server expects, but typically aren't persisted in the DB. For additional
// details see the comments in src/calendar/data/EventMappings.
- OriginalEventId: {name: 'OriginalEventId', mapping: 'origid', type: 'string', useNull: true},
- RSeriesStartDate: {name: 'RSeriesStartDate', mapping: 'rsstart', type: 'date', dateFormat: 'c', useNull: true},
- RInstanceStartDate: {name: 'RInstanceStartDate', mapping: 'ristart', type: 'date', dateFormat: 'c', useNull: true},
- REditMode: {name: 'REditMode', mapping: 'redit', type: 'string', useNull: true}
+ OriginalEventId: {name: 'OriginalEventId', mapping: 'origid', type: 'string', allowNull: true},
+ RSeriesStartDate: {name: 'RSeriesStartDate', mapping: 'rsstart', type: 'date', dateFormat: 'c', allowNull: true},
+ RInstanceStartDate: {name: 'RInstanceStartDate', mapping: 'ristart', type: 'date', dateFormat: 'c', allowNull: true},
+ REditMode: {name: 'REditMode', mapping: 'redit', type: 'string', allowNull: true}
};
Extensible.calendar.data.EventModel.reconfigure();
@@ -73,7 +73,7 @@ Ext.onReady(function() {
reader: {
type: 'json',
- root: 'calendars'
+ rootProperty: 'calendars'
}
}
});
@@ -100,11 +100,54 @@ Ext.onReady(function() {
},
reader: {
type: 'json',
- root: 'data'
+ rootProperty: 'data',
+ transform: {
+ fn: function(data) {
+ // Manipulate raw data object: start and end date are strings; Convert to Date()
+ Ext.iterate(data.data, function(event, key){
+ var startDate = (event['start']) ? new Date(event['start'].replace(/-/g , "/")) : new Date();
+ event[Extensible.calendar.data.EventMappings.StartDate.mapping] = startDate;
+
+ var endDate = (event['end']) ? new Date(event['end'].replace(/-/g , "/")) : new Date();
+ event[Extensible.calendar.data.EventMappings.EndDate.mapping] = endDate;
+
+ if (event['rsstart'] && event['rsstart'] != ''){
+ var rsstartDt = new Date(event['rsstart'].replace(/-/g , "/"));
+ event[Extensible.calendar.data.EventMappings.RSeriesStartDate.mapping] = rsstartDt;
+ }
+
+ if (event['ristart'] && event['ristart'] != ''){
+ var ristartDt = new Date(event['ristart'].replace(/-/g , "/"));
+ event[Extensible.calendar.data.EventMappings.RInstanceStartDate.mapping] = ristartDt;
+ }
+
+ // MySQL returns an int. Convert it to boolean, otherwise all events will be marked as all-day.
+ var allDay = (event['all_day'] == 1) ? true: false;
+ event[Extensible.calendar.data.EventMappings.IsAllDay.mapping] = allDay;
+ });
+
+ return data;
+ },
+ scope: this
+ }
},
writer: {
type: 'json',
- nameProperty: 'mapping'
+ nameProperty: 'mapping',
+ writeAllFields: true, // send all fields to server
+ transform: {
+ fn: function(data, request) {
+ var postData = {};
+
+ // Remove mapped fields from data sent to server and keep only the ones required in php script
+ Ext.iterate(Extensible.calendar.data.EventMappings, function(key, value){
+ postData[value.mapping] = data[value.name] ? data[value.name] : null;
+ });
+
+ return postData;
+ },
+ scope: this
+ }
}
},
@@ -114,8 +157,18 @@ Ext.onReady(function() {
// NOT that your changes were actually persisted correctly in the back end. The 'write' event is the best
// option for generically messaging after CRUD persistence has succeeded.
listeners: {
- 'write': function(store, operation) {
- var title = Ext.value(operation.records[0].data[Extensible.calendar.data.EventMappings.Title.name], '(No title)');
+ write: function(store, operation) {
+ var record, title;
+
+ if ('Ext.data.operation.Destroy' == Ext.getClass(operation).getName()){
+ record = operation.getRequest().getJsonData();
+ title = record[Extensible.calendar.data.EventMappings.Title.mapping] || '(No title)';
+ } else {
+ var records = operation.getRecords(),
+ record = records[0],
+ title = record.get(Extensible.calendar.data.EventMappings.Title.name) || '(No title)';
+ }
+
switch(operation.action){
case 'create':
Extensible.example.msg('Add', 'Added "' + title + '"');
@@ -149,6 +202,20 @@ Ext.onReady(function() {
eventStore: eventStore,
calendarStore: calendarStore,
title: 'Recurrence Calendar',
+ showAgendaView: true,
+ showListView: true,
+
+ agendaViewCfg: {
+ linkDatesToDayView: true,
+ dateRangeDefault: '3months'
+ },
+
+ listViewCfg: {
+ linkDatesToDayView: true,
+ dateRangeDefault: '3months',
+ groupBy: 'week'
+ },
+
// This is the magical config that enables the recurrence edit
// widget to appear in the event form. Without it, any existing
diff --git a/examples/calendar/remote/remote.js b/examples/calendar/remote/remote.js
index df53ccad..5d2a8162 100644
--- a/examples/calendar/remote/remote.js
+++ b/examples/calendar/remote/remote.js
@@ -1,6 +1,6 @@
Ext.Loader.setConfig({
enabled: true,
- //disableCaching: false,
+ disableCaching: false,
paths: {
"Extensible": "../../../src",
"Extensible.example": "../../"
@@ -46,7 +46,7 @@ Ext.onReady(function() {
Reminder: {name: 'Reminder', mapping: 'reminder'}
};
Extensible.calendar.data.EventModel.reconfigure();
-
+
// Calendars are loaded remotely from a static JSON file
var calendarStore = Ext.create('Extensible.calendar.data.MemoryCalendarStore', {
autoLoad: true,
@@ -57,7 +57,7 @@ Ext.onReady(function() {
reader: {
type: 'json',
- root: 'calendars'
+ rootProperty: 'calendars'
}
}
});
@@ -84,11 +84,44 @@ Ext.onReady(function() {
},
reader: {
type: 'json',
- root: 'data'
+ rootProperty: 'data',
+ transform: {
+ fn: function(data) {
+ // Manipulate raw data object: start and end date are strings; Convert to Date()
+ Ext.iterate(data.data, function(event,key){
+ var startDate = (event['start']) ? new Date(event['start'].replace(/-/g , "/")) : new Date();
+ event[Extensible.calendar.data.EventMappings.StartDate.mapping] = startDate;
+
+ var endDate = (event['end']) ? new Date(event['end'].replace(/-/g , "/")) : new Date();
+ event[Extensible.calendar.data.EventMappings.EndDate.mapping] = endDate;
+
+ // MySQL returns it an int. Convert it to boolean, otherwise all events will be marked as all-day.
+ var allDay = (event['all_day'] == 1) ? true: false;
+ event[Extensible.calendar.data.EventMappings.IsAllDay.mapping] = allDay;
+ });
+
+ return data;
+ },
+ scope: this
+ }
},
writer: {
type: 'json',
- nameProperty: 'mapping'
+ nameProperty: 'mapping',
+ writeAllFields: true, // send all fields to server
+ transform: {
+ fn: function(data, request) {
+ var postData = {};
+
+ // Remove mapped fields from data sent to server and keep only the ones required in php script
+ Ext.iterate(Extensible.calendar.data.EventMappings, function(key, value){
+ postData[value.mapping] = data[value.name] ? data[value.name] : null;
+ });
+
+ return postData;
+ },
+ scope: this
+ }
}
},
@@ -98,8 +131,18 @@ Ext.onReady(function() {
// NOT that your changes were actually persisted correctly in the back end. The 'write' event is the best
// option for generically messaging after CRUD persistence has succeeded.
listeners: {
- 'write': function(store, operation) {
- var title = Ext.value(operation.records[0].data[Extensible.calendar.data.EventMappings.Title.name], '(No title)');
+ write: function(store, operation) {
+ var record, title;
+
+ if ('Ext.data.operation.Destroy' == Ext.getClass(operation).getName()){
+ record = operation.getRequest().getJsonData();
+ title = record[Extensible.calendar.data.EventMappings.Title.mapping] || '(No title)';
+ } else {
+ var records = operation.getRecords(),
+ record = records[0],
+ title = record.get(Extensible.calendar.data.EventMappings.Title.name) || '(No title)';
+ }
+
switch(operation.action){
case 'create':
Extensible.example.msg('Add', 'Added "' + title + '"');
@@ -121,7 +164,26 @@ Ext.onReady(function() {
region: 'center', // it will be used in a border layout below
eventStore: eventStore,
calendarStore: calendarStore,
- title: 'Remote Calendar'
+ title: 'Remote Calendar',
+ showAgendaView: true,
+ showListView: true,
+ activeItem: 3, // month view
+
+ // Any generic view options that should be applied to all sub views:
+ viewConfig: {
+ startDay: 0
+ },
+
+ agendaViewCfg: {
+ linkDatesToDayView: true,
+ dateRangeDefault: '3months'
+ },
+
+ listViewCfg: {
+ linkDatesToDayView: true,
+ dateRangeDefault: '3months',
+ groupBy: 'month'
+ }
});
Ext.create('Ext.container.Viewport', {
@@ -133,6 +195,7 @@ Ext.onReady(function() {
collapsible: true,
split: true,
autoScroll: true,
+ showListView: true,
contentEl: 'sample-overview' // from remote.html
},
calendarPanel
diff --git a/examples/calendar/tabpanel.js b/examples/calendar/tabpanel.js
index e469fd55..77924c1d 100644
--- a/examples/calendar/tabpanel.js
+++ b/examples/calendar/tabpanel.js
@@ -33,9 +33,17 @@ Ext.onReady(function(){
width: 700,
height: 500,
activeItem: 1,
+ showAgendaView: true,
+ showListView: true,
// this is a good idea since we are in a TabPanel and we don't want
// the user switching tabs on us while we are editing an event:
- editModal: true
+ editModal: true,
+
+ listViewCfg: {
+ dateRangeDefault: '3months',
+ groupBy: 'week'
+ }
+
};
//
diff --git a/examples/calendar/window.js b/examples/calendar/window.js
index 87d651fd..c50b6006 100644
--- a/examples/calendar/window.js
+++ b/examples/calendar/window.js
@@ -26,6 +26,9 @@ Ext.onReady(function(){
items: {
// xtype is supported:
xtype: 'extensible.calendarpanel',
+ activeItem: 3, // default to month view
+ showAgendaView: true,
+ showListView: true,
eventStore: Ext.create('Extensible.calendar.data.MemoryEventStore', {
// defined in ../data/Events.js
data: Ext.create('Extensible.example.calendar.data.Events')
@@ -36,7 +39,7 @@ Ext.onReady(function(){
this.calendarWin.show();
};
- Ext.fly('cal-win').on('click', showWindow, this);
+ Ext.get('cal-win').on('click', showWindow, this);
showWindow();
});
diff --git a/examples/examples.js b/examples/examples.js
index 310ecd16..6807b30a 100644
--- a/examples/examples.js
+++ b/examples/examples.js
@@ -7,8 +7,8 @@ Ext.define('Extensible.example', {
this.msgCt.alignTo(document, 't-t');
var s = Ext.String.format.apply(String, Array.prototype.slice.call(arguments, 1));
var m = Ext.core.DomHelper.append(this.msgCt, {html:'
' + title + '
' + s + '
'}, true);
-
- m.slideIn('t').pause(3000).ghost('t', {remove:true});
+
+ m.slideIn('t').ghost('t', {remove:true, duration: 3000});
},
insertExamplesMenuLink: function() {
diff --git a/examples/server/php/api/events-common.php b/examples/server/php/api/events-common.php
index 9a76a2ca..c1cfaca8 100644
--- a/examples/server/php/api/events-common.php
+++ b/examples/server/php/api/events-common.php
@@ -26,7 +26,7 @@
// Set the app_id to allow each example to reuse this API with its own data.
// In a real application this would not be needed.
- $app_id = $event['app_id'] = isset($_REQUEST['app_id']) ? strtolower($_REQUEST['app_id']) : null;
+ $app_id = $event['app_id'] = isset($_REQUEST['app_id']) ? strtolower($_REQUEST['app_id']) : 'remote';
// The demos support simulating server failure for testing purposes
$fail = isset($_REQUEST['fail']) ? TRUE : FALSE;
diff --git a/examples/server/php/api/events-recurrence.php b/examples/server/php/api/events-recurrence.php
index e1f3c36e..6b0d7143 100644
--- a/examples/server/php/api/events-recurrence.php
+++ b/examples/server/php/api/events-recurrence.php
@@ -257,15 +257,15 @@ function calculateEndDate($event) {
$max_date = new DateTime('9999-12-31');
$recurrence = new When();
$recurrence->rrule($rrule);
-
if (isset($recurrence->end_date) && $recurrence->end_date < $max_date) {
// The RRULE includes an explicit end date, so use that
- $end = $recurrence->end_date->format($date_format).'Z';
+ $recurrence->end_date->setTimezone(new DateTimeZone('UTC'));
+ $end = $recurrence->end_date->format($date_format);
}
else if (isset($recurrence->count) && $recurrence->count > 0) {
// The RRULE has a limit, so calculate the end date based on the instance count
$count = 0;
- $newEnd;
+ $newEnd = null;
$rdates = $recurrence->recur($event[$mappings['start_date']])->rrule($rrule);
while ($rdate = $rdates->next()) {
@@ -276,11 +276,14 @@ function calculateEndDate($event) {
}
// The 'minutes' portion should match Extensible.calendar.data.EventModel.resolution:
$newEnd->modify('+'.$event[$mappings['duration']].' minutes');
- $end = $newEnd->format($date_format).'Z';
+ $newEnd->setTimezone(new DateTimeZone('UTC'));
+ $end = $newEnd->format($date_format);
}
else {
// The RRULE does not specify an end date or count, so default to max date
- $end = date($date_format, PHP_INT_MAX).'Z';
+ $newEnd = new DateTime();
+ $newEnd->setTimestamp(PHP_INT_MAX);
+ $end = $newEnd->format($date_format);
}
}
return $end;
@@ -428,7 +431,7 @@ function deleteEvent($event) {
*/
function updateEvent($event) {
global $db, $mappings, $date_format;
-
+
$editMode = $event[$mappings['recur_edit_mode']];
if ($editMode) {
diff --git a/resources/css/calendar.css b/resources/css/calendar.css
index d003db7d..9db93d6e 100644
--- a/resources/css/calendar.css
+++ b/resources/css/calendar.css
@@ -485,7 +485,6 @@ td.ext-cal-dtitle-today div {
}
.ext-cal-evt dl {
margin: 0;
- border: 0 1px;
overflow: hidden;
border-width: 0 1px;
border-style: solid;
@@ -755,6 +754,66 @@ td.ext-cal-dtitle-today div {
display: none;
}
+/* -----------------------------------------
+ * Agenda view specific styles
+ */
+.ext-cal-agenda {
+ border-top: 1px solid #99BBE8;
+}
+
+.ext-cal-agenda-hd {
+ border-bottom: 1px solid #99BBE8;
+}
+
+.ext-cal-agenda-hd .x-panel-body {
+ background-color: #F0F4FA !important;
+}
+.ext-cal-agenda-hd .x-toolbar {
+ background-color: #F0F4FA !important;
+ background-image: none !important;
+}
+
+.ext-cal-icon-evt-add {
+ background-image: url(../images/default/ext/add.gif) !important;
+}
+
+.ext-cal-agenda-bd .ext-cal-evt-agenda td {
+ padding: 2px 10px;
+ font-size: 12px;
+ vertical-align: top;
+}
+
+.ext-cal-agenda-bd .ext-cal-evt-agenda-details td {
+ padding: 1px 10px 1px 0;
+}
+
+.ext-cal-agenda-bd .ext-cal-day-link {
+ cursor: pointer;
+}
+
+.ext-cal-agenda-bd hr {
+ margin:10px 0;
+ color: #CCC;
+ background-color:#CCC;
+ height:1px;
+ border:0;
+}
+
+/* new styles, adjust for white skin */
+.ext-cal-agenda-bd td.ext-cal-agenda-group-header {
+ color: #A7C6DF;
+ font-size: 16px;
+ line-height: 14px;
+ font-weight: bold;
+ padding-top: 10px;
+ padding-bottom: 0px;
+}
+
+.ext-cal-agenda-bd .ext-cal-evt-agenda td.ext-cal-evt-hours {
+ padding: 2px 2px;
+}
+
+
/*******************************************
*
* Calendar navigation picker styles
@@ -763,14 +822,7 @@ td.ext-cal-dtitle-today div {
.ext-cal-nav-picker {
border-style: none none solid;
border-color: #99BBE8;
-}
-.ext-cal-nav-picker .x-datepicker-header {
- background: #D3E1F1 url(../images/default/ext/toolbar-bg.gif) repeat-x;
-}
-.ext-cal-nav-picker .x-datepicker-month,
-.ext-cal-nav-picker .x-datepicker-prev,
-.ext-cal-nav-picker .x-datepicker-next {
- background: transparent;
+ max-width: 179px;
}
.ext-cal-nav-picker .x-datepicker-prev a {
background-image: url(../images/default/ext/page-prev.gif);
@@ -786,10 +838,6 @@ td.ext-cal-dtitle-today div {
font-weight: bold;
font-family: arial,tahoma,verdana,helvetica;
}
-.ext-cal-nav-picker .x-datepicker-month .x-btn-split-right {
- background-image: url(../images/default/ext/button-arrow.gif) !important;
- background-position: right 4px;
-}
.ext-cal-nav-picker .x-datepicker-inner {
border-top: 1px solid #BBCCFF;
}
@@ -969,9 +1017,6 @@ td.ext-cal-dtitle-today div {
.extensible-cal-icon-cal-show {
background-image:url(../images/default/silk/calendar_view_month.png) !important;
}
-.extensible-cal-icon-cal-colors {
- background-image:url(../images/default/silk/color_wheel.png) !important;
-}
/*******************************************
*
@@ -1007,4 +1052,28 @@ td.ext-cal-dtitle-today div {
height: 14px;
width: 14px;
line-height: 10px;
+}
+
+
+
+/******************************************** EXTJS 5 **********************************************/
+.x-monthpicker-months {
+ width: 90px;
+}
+
+.x-monthpicker-months .x-monthpicker-item {
+ width: 44px;
+}
+
+.x-monthpicker-years {
+ width: 89px;
+}
+
+
+.x-monthpicker-yearnav-button-ct {
+ width: 44px;
+}
+
+.x-monthpicker-years .x-monthpicker-item {
+ width: 44px;
}
\ No newline at end of file
diff --git a/resources/images/default/ext/add.gif b/resources/images/default/ext/add.gif
new file mode 100644
index 00000000..93195256
Binary files /dev/null and b/resources/images/default/ext/add.gif differ
diff --git a/src/calendar/CalendarPanel.js b/src/calendar/CalendarPanel.js
index 002b6130..461e6abd 100644
--- a/src/calendar/CalendarPanel.js
+++ b/src/calendar/CalendarPanel.js
@@ -14,7 +14,8 @@ Ext.define('Extensible.calendar.CalendarPanel', {
'Extensible.calendar.view.Week',
'Extensible.calendar.view.Month',
'Extensible.calendar.view.MultiDay',
- 'Extensible.calendar.view.MultiWeek'
+ 'Extensible.calendar.view.MultiWeek',
+ 'Extensible.calendar.view.Agenda'
],
/**
@@ -58,6 +59,18 @@ Ext.define('Extensible.calendar.CalendarPanel', {
* If all other views are hidden, the month view will show by default even if this config is false.
*/
showMonthView: true,
+ /**
+ * @cfg {Boolean} showAgendaView
+ * True to include the agenda view (and toolbar button), false to hide them (defaults to false).
+ */
+ showAgendaView: false,
+ /**
+ * @cfg {Boolean} showListView
+ * True to include the list view (and toolbar button), false to hide them (defaults to false). The list view
+ * is an instance of {@link Extensible.calendar.view.Agenda agenda view} that is preconfigured to show a simple list
+ * of events rather than an agenda style list of events.
+ */
+ showListView: false,
/**
* @cfg {Boolean} showNavBar
* True to display the calendar navigation toolbar, false to hide it (defaults to true). Note that
@@ -151,6 +164,16 @@ Ext.define('Extensible.calendar.CalendarPanel', {
* Text to use for the 'Month' nav bar button.
*/
monthText: 'Month',
+ /**
+ * @cfg {String} agendaText
+ * Text to use for the 'Agenda' nav bar button.
+ */
+ agendaText: 'Agenda',
+ /**
+ * @cfg {String} listText
+ * Text to use for the 'List' nav bar button.
+ */
+ listText: 'List',
/**
* @cfg {Boolean} editModal
* True to show the default event editor window modally over the entire page, false to allow user
@@ -215,6 +238,17 @@ Ext.define('Extensible.calendar.CalendarPanel', {
* A config object that will be applied only to the {@link Extensible.calendar.view.Month MonthView}
* managed by this CalendarPanel.
*/
+ /**
+ * @cfg {Object} agendaViewCfg
+ * A config object that will be applied only to the {@link Extensible.calendar.view.Agenda agenda view} managed
+ * by this CalendarPanel.
+ */
+ /**
+ * @cfg {Object} listViewCfg
+ * A config object that will be applied only to the {@link Extensible.calendar.view.Agenda list view} managed
+ * by this CalendarPanel. List view is an instance of {@link Extensible.calendar.view.Agenda agenda view} that is
+ * preconfigured to show a simple list of events rather than a agenda style list of events.
+ */
/**
* @cfg {Object} editViewCfg
* A config object that will be applied only to the {@link Extensible.calendar.form.EventDetails
@@ -294,14 +328,26 @@ Ext.define('Extensible.calendar.CalendarPanel', {
});
this.viewCount++;
}
- if(this.showMonthView || this.viewCount === 0) {
+ if(this.showMonthView) {
this.tbar.items.push({
id: this.id+'-tb-month', text: this.monthText, handler: this.onMonthNavClick, scope: this, toggleGroup: this.id+'-tb-views'
});
this.viewCount++;
this.showMonthView = true;
}
-
+ if(this.showAgendaView){
+ this.tbar.items.push({
+ id: this.id+'-tb-agenda', text: this.agendaText, handler: this.onAgendaNavClick, scope: this, toggleGroup: this.id+'-tb-views'
+ });
+ this.viewCount++;
+ }
+ if(this.showListView || this.viewCount === 0){
+ this.tbar.items.push({
+ id: this.id+'-tb-list', text: this.listText, handler: this.onListNavClick, scope: this, toggleGroup: this.id+'-tb-views'
+ });
+ this.viewCount++;
+ }
+
var idx = this.viewCount-1;
this.activeItem = (this.activeItem === undefined ? idx : (this.activeItem > idx ? idx : this.activeItem));
@@ -312,238 +358,238 @@ Ext.define('Extensible.calendar.CalendarPanel', {
this.callParent(arguments);
- this.addEvents({
- /**
- * @event eventadd
- * Fires after a new event is added to the underlying store
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The new
- * {@link Extensible.calendar.data.EventModel record} that was added
- */
- eventadd: true,
- /**
- * @event eventupdate
- * Fires after an existing event is updated
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The new
- * {@link Extensible.calendar.data.EventModel record} that was updated
- */
- eventupdate: true,
- /**
- * @event beforeeventdelete
- * Fires before an event is deleted by the user. This is a cancelable event, so returning
- * false from a handler will cancel the delete operation.
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
- * for the event that was deleted
- * @param {Ext.Element} el The target element
- */
- beforeeventdelete: true,
- /**
- * @event eventdelete
- * Fires after an event is deleted by the user.
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that was deleted
- * @param {Ext.Element} el The target element
- */
- eventdelete: true,
- /**
- * @event eventcancel
- * Fires after an event add/edit operation is canceled by the user and no store update took place
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The new
- * {@link Extensible.calendar.data.EventModel record} that was canceled
- */
- eventcancel: true,
- /**
- * @event viewchange
- * Fires after a different calendar view is activated (but not when the event edit form is activated)
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.CalendarView} view The view being activated (any valid
- * {@link Extensible.calendar.view.AbstractCalendar CalendarView} subclass)
- * @param {Object} info Extra information about the newly activated view. This is a plain object
- * with following properties:
- *
- * * **activeDate**
- * * The currently selected date
- * * **viewStart**
- * * The first date in the new view range
- * * **viewEnd**
- * * The last date in the new view range
- */
- viewchange: true,
- /**
- * @event editdetails
- * Fires when the user selects the option to edit the selected event in the detailed edit form
- * (by default, an instance of {@link Extensible.calendar.form.EventDetails}). Handling code
- * should hide the active event editor and transfer the current event record to the appropriate
- * instance of the detailed form by showing it and calling
- * {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
- * @param {Extensible.calendar.CalendarPanel} this The CalendarPanel
- * @param {Extensible.calendar.view.AbstractCalendar} view The currently active
- * {@link Extensible.calendar.view.AbstractCalendar CalendarView} subclass
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} that is currently being edited
- * @param {Ext.Element} el The target element
- */
- editdetails: true
-
-
- //
- // NOTE: CalendarPanel also relays the following events from contained views as if
- // they originated from this:
- //
-
- /**
- * @event eventsrendered
- * Fires after events are finished rendering in the view
- * @param {Extensible.calendar.CalendarPanel} this
- */
- /**
- * @event eventclick
- * Fires after the user clicks on an event element.
- *
- * **NOTE:** This version of eventclick differs from the same
- * event fired directly by {@link Extensible.calendar.view.AbstractCalendar CalendarView}
- * subclasses in that it provides a default implementation (showing the default edit window)
- * and is also cancelable (if a handler returns false the edit window will not be
- * shown). This event when fired from a view class is simply a notification that an event was
- * clicked and has no default behavior.
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that was clicked on
- * @param {HTMLNode} el The DOM node that was clicked on
- */
- /**
- * @event rangeselect
- * Fires after the user drags on the calendar to select a range of dates/times in which to
- * create an event
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Object} dates An object containing the start (StartDate property) and end (EndDate
- * property) dates selected
- * @param {Function} callback A callback function that MUST be called after the event handling
- * is complete so that the view is properly cleaned up (shim elements are persisted in
- * the view while the user is prompted to handle the range selection). The callback is
- * already created in the proper scope, so it simply needs to be executed as a standard
- * function call (e.g., callback()).
- */
- /**
- * @event eventover
- * Fires anytime the mouse is over an event element
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that the cursor is over
- * @param {HTMLNode} el The DOM node that is being moused over
- */
- /**
- * @event eventout
- * Fires anytime the mouse exits an event element
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that the cursor exited
- * @param {HTMLNode} el The DOM node that was exited
- */
- /**
- * @event beforedatechange
- * Fires before the start date of the view changes, giving you an opportunity to save state or
- * anything else you may need to do prior to the UI view changing. This is a cancelable event, so
- * returning false from a handler will cancel both the view change and the setting of the start date.
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Date} startDate The current start date of the view (as explained in {@link #getStartDate}
- * @param {Date} newStartDate The new start date that will be set when the view changes
- * @param {Date} viewStart The first displayed date in the current view
- * @param {Date} viewEnd The last displayed date in the current view
- */
- /**
- * @event dayclick
- * Fires after the user clicks within a day/week view container and not on an event element
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Date} dt The date/time that was clicked on
- * @param {Boolean} allday True if the day clicked on represents an all-day box, else false.
- * @param {Ext.Element} el The Element that was clicked on
- */
- /**
- * @event datechange
- * Fires after the start date of the view changes
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}
- * @param {Date} viewStart The first displayed date in the view
- * @param {Date} viewEnd The last displayed date in the view
- */
- /**
- * @event beforeeventmove
- * Fires before an event element is dragged by the user and dropped in a new position. This is
- * a cancelable event, so returning false from a handler will cancel the move operation.
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that will be moved
- */
- /**
- * @event eventmove
- * Fires after an event element is dragged by the user and dropped in a new position
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that was moved with
- * updated start and end dates
- */
- /**
- * @event initdrag
- * Fires when a drag operation is initiated in the view
- * @param {Extensible.calendar.CalendarPanel} this
- */
- /**
- * @event dayover
- * Fires while the mouse is over a day element
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Date} dt The date that is being moused over
- * @param {Ext.Element} el The day Element that is being moused over
- */
- /**
- * @event dayout
- * Fires when the mouse exits a day element
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Date} dt The date that is exited
- * @param {Ext.Element} el The day Element that is exited
- */
- /**
- * @event beforeeventresize
- * Fires after the user drags the resize handle of an event to resize it, but before the
- * resize operation is carried out. This is a cancelable event, so returning false from a
- * handler will cancel the resize operation. **NOTE:** This event is only fired
- * from views that support event resizing.
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that was resized
- * containing the updated start and end dates
- */
- /**
- * @event eventresize
- * Fires after the user drags the resize handle of an event and the resize operation is
- * complete. **NOTE:** This event is only fired from views that support event resizing.
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Extensible.calendar.data.EventModel} rec The
- * {@link Extensible.calendar.data.EventModel record} for the event that was resized
- * containing the updated start and end dates
- */
- /**
- * @event eventexception
- * Fires after an event has been processed via an Ext proxy and returned with an exception. This
- * could be because of a server error, or because the data returned success: false.
- *
- * The view provides default handling via the overrideable
- * {@link Extensible.calendar.view.AbstractCalendar#notifyOnException notifyOnException} method. If
- * any function handling this event returns false, the notifyOnException method will not be called.
- *
- * Note that only Server proxy and subclasses (including Ajax proxy) will raise this event.
- *
- * @param {Extensible.calendar.CalendarPanel} this
- * @param {Object} response The raw response object returned from the server
- * @param {Ext.data.Operation} operation The operation that was processed
- * @since 1.6.0
- */
- });
-
+ //this.addEvents({
+ // /**
+ // * @event eventadd
+ // * Fires after a new event is added to the underlying store
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new
+ // * {@link Extensible.calendar.data.EventModel record} that was added
+ // */
+ // eventadd: true,
+ // /**
+ // * @event eventupdate
+ // * Fires after an existing event is updated
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new
+ // * {@link Extensible.calendar.data.EventModel record} that was updated
+ // */
+ // eventupdate: true,
+ // /**
+ // * @event beforeeventdelete
+ // * Fires before an event is deleted by the user. This is a cancelable event, so returning
+ // * false from a handler will cancel the delete operation.
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
+ // * for the event that was deleted
+ // * @param {Ext.Element} el The target element
+ // */
+ // beforeeventdelete: true,
+ // /**
+ // * @event eventdelete
+ // * Fires after an event is deleted by the user.
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that was deleted
+ // * @param {Ext.Element} el The target element
+ // */
+ // eventdelete: true,
+ // /**
+ // * @event eventcancel
+ // * Fires after an event add/edit operation is canceled by the user and no store update took place
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new
+ // * {@link Extensible.calendar.data.EventModel record} that was canceled
+ // */
+ // eventcancel: true,
+ // /**
+ // * @event viewchange
+ // * Fires after a different calendar view is activated (but not when the event edit form is activated)
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.CalendarView} view The view being activated (any valid
+ // * {@link Extensible.calendar.view.AbstractCalendar CalendarView} subclass)
+ // * @param {Object} info Extra information about the newly activated view. This is a plain object
+ // * with following properties:
+ // *
+ // * * **activeDate**
+ // * * The currently selected date
+ // * * **viewStart**
+ // * * The first date in the new view range
+ // * * **viewEnd**
+ // * * The last date in the new view range
+ // */
+ // viewchange: true,
+ // /**
+ // * @event editdetails
+ // * Fires when the user selects the option to edit the selected event in the detailed edit form
+ // * (by default, an instance of {@link Extensible.calendar.form.EventDetails}). Handling code
+ // * should hide the active event editor and transfer the current event record to the appropriate
+ // * instance of the detailed form by showing it and calling
+ // * {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
+ // * @param {Extensible.calendar.CalendarPanel} this The CalendarPanel
+ // * @param {Extensible.calendar.view.AbstractCalendar} view The currently active
+ // * {@link Extensible.calendar.view.AbstractCalendar CalendarView} subclass
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} that is currently being edited
+ // * @param {Ext.Element} el The target element
+ // */
+ // editdetails: true
+ //
+ //
+ // //
+ // // NOTE: CalendarPanel also relays the following events from contained views as if
+ // // they originated from this:
+ // //
+ //
+ // /**
+ // * @event eventsrendered
+ // * Fires after events are finished rendering in the view
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // */
+ // /**
+ // * @event eventclick
+ // * Fires after the user clicks on an event element.
+ // *
+ // * **NOTE:** This version of eventclick differs from the same
+ // * event fired directly by {@link Extensible.calendar.view.AbstractCalendar CalendarView}
+ // * subclasses in that it provides a default implementation (showing the default edit window)
+ // * and is also cancelable (if a handler returns false the edit window will not be
+ // * shown). This event when fired from a view class is simply a notification that an event was
+ // * clicked and has no default behavior.
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that was clicked on
+ // * @param {HTMLNode} el The DOM node that was clicked on
+ // */
+ // /**
+ // * @event rangeselect
+ // * Fires after the user drags on the calendar to select a range of dates/times in which to
+ // * create an event
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Object} dates An object containing the start (StartDate property) and end (EndDate
+ // * property) dates selected
+ // * @param {Function} callback A callback function that MUST be called after the event handling
+ // * is complete so that the view is properly cleaned up (shim elements are persisted in
+ // * the view while the user is prompted to handle the range selection). The callback is
+ // * already created in the proper scope, so it simply needs to be executed as a standard
+ // * function call (e.g., callback()).
+ // */
+ // /**
+ // * @event eventover
+ // * Fires anytime the mouse is over an event element
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that the cursor is over
+ // * @param {HTMLNode} el The DOM node that is being moused over
+ // */
+ // /**
+ // * @event eventout
+ // * Fires anytime the mouse exits an event element
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that the cursor exited
+ // * @param {HTMLNode} el The DOM node that was exited
+ // */
+ // /**
+ // * @event beforedatechange
+ // * Fires before the start date of the view changes, giving you an opportunity to save state or
+ // * anything else you may need to do prior to the UI view changing. This is a cancelable event, so
+ // * returning false from a handler will cancel both the view change and the setting of the start date.
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Date} startDate The current start date of the view (as explained in {@link #getStartDate}
+ // * @param {Date} newStartDate The new start date that will be set when the view changes
+ // * @param {Date} viewStart The first displayed date in the current view
+ // * @param {Date} viewEnd The last displayed date in the current view
+ // */
+ // /**
+ // * @event dayclick
+ // * Fires after the user clicks within a day/week view container and not on an event element
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Date} dt The date/time that was clicked on
+ // * @param {Boolean} allday True if the day clicked on represents an all-day box, else false.
+ // * @param {Ext.Element} el The Element that was clicked on
+ // */
+ // /**
+ // * @event datechange
+ // * Fires after the start date of the view changes
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}
+ // * @param {Date} viewStart The first displayed date in the view
+ // * @param {Date} viewEnd The last displayed date in the view
+ // */
+ // /**
+ // * @event beforeeventmove
+ // * Fires before an event element is dragged by the user and dropped in a new position. This is
+ // * a cancelable event, so returning false from a handler will cancel the move operation.
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that will be moved
+ // */
+ // /**
+ // * @event eventmove
+ // * Fires after an event element is dragged by the user and dropped in a new position
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that was moved with
+ // * updated start and end dates
+ // */
+ // /**
+ // * @event initdrag
+ // * Fires when a drag operation is initiated in the view
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // */
+ // /**
+ // * @event dayover
+ // * Fires while the mouse is over a day element
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Date} dt The date that is being moused over
+ // * @param {Ext.Element} el The day Element that is being moused over
+ // */
+ // /**
+ // * @event dayout
+ // * Fires when the mouse exits a day element
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Date} dt The date that is exited
+ // * @param {Ext.Element} el The day Element that is exited
+ // */
+ // /**
+ // * @event beforeeventresize
+ // * Fires after the user drags the resize handle of an event to resize it, but before the
+ // * resize operation is carried out. This is a cancelable event, so returning false from a
+ // * handler will cancel the resize operation. **NOTE:** This event is only fired
+ // * from views that support event resizing.
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that was resized
+ // * containing the updated start and end dates
+ // */
+ // /**
+ // * @event eventresize
+ // * Fires after the user drags the resize handle of an event and the resize operation is
+ // * complete. **NOTE:** This event is only fired from views that support event resizing.
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Extensible.calendar.data.EventModel} rec The
+ // * {@link Extensible.calendar.data.EventModel record} for the event that was resized
+ // * containing the updated start and end dates
+ // */
+ // /**
+ // * @event eventexception
+ // * Fires after an event has been processed via an Ext proxy and returned with an exception. This
+ // * could be because of a server error, or because the data returned success: false.
+ // *
+ // * The view provides default handling via the overrideable
+ // * {@link Extensible.calendar.view.AbstractCalendar#notifyOnException notifyOnException} method. If
+ // * any function handling this event returns false, the notifyOnException method will not be called.
+ // *
+ // * Note that only Server proxy and subclasses (including Ajax proxy) will raise this event.
+ // *
+ // * @param {Extensible.calendar.CalendarPanel} this
+ // * @param {Object} response The raw response object returned from the server
+ // * @param {Ext.data.Operation} operation The operation that was processed
+ // * @since 1.6.0
+ // */
+ //});
+
this.addCls('x-cal-panel');
if(this.eventStore) {
@@ -630,7 +676,62 @@ Ext.define('Extensible.calendar.CalendarPanel', {
this.initEventRelay(month);
this.add(month);
}
+ if(this.showAgendaView){
+ var agenda = Ext.applyIf({
+ xtype: 'extensible.agendaview',
+ title: this.agendaText,
+ listeners: {
+ 'dayclick': {
+ fn: function(vw, dt){
+ this.showDay(dt);
+ },
+ scope: this
+ },
+ 'datechange': {
+ fn: function(){
+ // AgendaView allows the changing of start and end dates from within in the view. Update
+ // the nav state this happens.
+ this.updateNavState();
+ },
+ scope: this
+ }
+ }
+ }, sharedViewCfg);
+
+ agenda = Ext.apply(Ext.apply(agenda, this.viewConfig), this.agendaViewCfg);
+ agenda.id = this.id+'-agenda';
+ this.initEventRelay(agenda);
+ this.add(agenda);
+ }
+ if(this.showListView){
+ var list = Ext.applyIf({
+ xtype: 'extensible.agendaview',
+ title: this.listText,
+ simpleList: true,
+ groupBy: 'month',
+ listeners: {
+ 'dayclick': {
+ fn: function(vw, dt){
+ this.showDay(dt);
+ },
+ scope: this
+ },
+ 'datechange': {
+ fn: function(){
+ // AgendaView allows the changing of start and end dates from within in the view. Update
+ // the nav state this happens.
+ this.updateNavState();
+ },
+ scope: this
+ }
+ }
+ }, sharedViewCfg);
+ list = Ext.apply(Ext.apply(list, this.viewConfig), this.listViewCfg);
+ list.id = this.id+'-list';
+ this.initEventRelay(list);
+ this.add(list);
+ }
this.add(Ext.applyIf({
xtype: 'extensible.eventeditform',
id: this.id+'-edit',
@@ -720,8 +821,8 @@ Ext.define('Extensible.calendar.CalendarPanel', {
},
onWrite: function(store, operation) {
- var rec = operation.records[0];
-
+ var records = operation.getRecords(), rec = records[0];
+
switch(operation.action) {
case 'create':
this.onStoreAdd(store, rec);
@@ -827,7 +928,6 @@ Ext.define('Extensible.calendar.CalendarPanel', {
// Activate the new view and refresh the layout
layout.setActiveItem(id || me.activeItem);
- me.doComponentLayout();
me.activeView = layout.getActiveItem();
if (id !== editViewId) {
@@ -900,6 +1000,11 @@ Ext.define('Extensible.calendar.CalendarPanel', {
this.fireViewChange();
return this;
},
+
+ // private
+ showDay: function(dt) {
+ this.setActiveView(this.id+'-day', dt);
+ },
showWeek: function(dt) {
this.setActiveView(this.id+'-week', dt);
@@ -952,7 +1057,17 @@ Ext.define('Extensible.calendar.CalendarPanel', {
onMonthNavClick: function() {
this.setActiveView(this.id+'-month');
},
-
+
+ // private
+ onAgendaNavClick: function(){
+ this.setActiveView(this.id+'-agenda');
+ },
+
+ // private
+ onListNavClick: function(){
+ this.setActiveView(this.id+'-list');
+ },
+
/**
* Return the calendar view that is currently active, which will be a subclass of
* {@link Extensible.calendar.view.AbstractCalendar AbstractCalendar}.
diff --git a/src/calendar/data/EventMappings.js b/src/calendar/data/EventMappings.js
index e3493f39..99cbefe4 100644
--- a/src/calendar/data/EventMappings.js
+++ b/src/calendar/data/EventMappings.js
@@ -77,54 +77,54 @@ Ext.ns('Extensible.calendar.data');
// @define Extensible.calendar.data.EventMappings
Extensible.calendar.data.EventMappings = {
EventId: {
- name: 'EventId',
+ name: 'id',
mapping: 'id',
type: 'string'
},
CalendarId: {
- name: 'CalendarId',
+ name: 'cid',
mapping: 'cid',
type: 'string'
},
Title: {
- name: 'Title',
+ name: 'title',
mapping: 'title',
type: 'string'
},
StartDate: {
- name: 'StartDate',
+ name: 'start',
mapping: 'start',
type: 'date',
dateFormat: 'c'
},
EndDate: {
- name: 'EndDate',
+ name: 'end',
mapping: 'end',
type: 'date',
dateFormat: 'c'
},
Location: {
- name: 'Location',
+ name: 'loc',
mapping: 'loc',
type: 'string'
},
Notes: {
- name: 'Notes',
+ name: 'notes',
mapping: 'notes',
type: 'string'
},
Url: {
- name: 'Url',
+ name: 'url',
mapping: 'url',
type: 'string'
},
IsAllDay: {
- name: 'IsAllDay',
+ name: 'ad',
mapping: 'ad',
type: 'boolean'
},
Reminder: {
- name: 'Reminder',
+ name: 'rem',
mapping: 'rem',
type: 'string'
},
@@ -145,10 +145,10 @@ Extensible.calendar.data.EventMappings = {
// choose to provide a custom implementation, but out of the box only
// the iCal RRULE format is handled by the components.
RRule: {
- name: 'RRule',
+ name: 'rrule',
mapping: 'rrule',
type: 'string',
- useNull: true
+ allowNull: true
},
// When using recurrence, the standard EndDate value will be the end date
@@ -159,10 +159,10 @@ Extensible.calendar.data.EventMappings = {
// recurrence so that the end date of each event instance can be
// properly calculated.
Duration: {
- name: 'Duration',
+ name: 'duration',
mapping: 'duration',
defaultValue: -1, // the standard int default of 0 is actually a valid duration
- useNull: true, // Without this, the null returned from the server is coerced to 0
+ allowNull: true, // Without this, the null returned from the server is coerced to 0
type: 'int'
},
@@ -173,19 +173,19 @@ Extensible.calendar.data.EventMappings = {
// typically these will be generated from the RRULE pattern, not real events
// that exist in the DB.
OriginalEventId: {
- name: 'OriginalEventId',
+ name: 'origid',
mapping: 'origid',
type: 'string',
- useNull: true
+ allowNull: true
},
// The start date for the recurring series.
RSeriesStartDate: {
- name: 'RSeriesStartDate',
+ name: 'rsstart',
mapping: 'rsstart',
type: 'date',
dateFormat: 'c',
- useNull: true
+ allowNull: true
},
// If the start date of a recurring event instance is changed and then saved
@@ -195,11 +195,11 @@ Extensible.calendar.data.EventMappings = {
// the updated start date, you need a way to pass the original unedited start date
// to be used as the exception date, which is what this instance start date is for.
RInstanceStartDate: {
- name: 'RInstanceStartDate',
+ name: 'ristart',
mapping: 'ristart',
type: 'date',
dateFormat: 'c',
- useNull: true
+ allowNull: true
},
// Recurrence edit mode ('single', 'future' or 'all'). This is transient data
@@ -207,9 +207,9 @@ Extensible.calendar.data.EventMappings = {
// display purposes), but it's kept on the record for ease of transmission to
// the server, and because multiple batched events could have different edit modes.
REditMode: {
- name: 'REditMode',
+ name: 'redit',
mapping: 'redit',
type: 'string',
- useNull: true
+ allowNull: true
}
};
\ No newline at end of file
diff --git a/src/calendar/data/EventModel.js b/src/calendar/data/EventModel.js
index 9527601d..36f43525 100644
--- a/src/calendar/data/EventModel.js
+++ b/src/calendar/data/EventModel.js
@@ -34,7 +34,7 @@ Ext.define('Extensible.calendar.data.EventModel', {
mappingClass: 'Extensible.calendar.data.EventMappings',
- mappingIdProperty: 'EventId',
+ mappingIdProperty: 'id',
// Experimental, not currently used:
// associations: [{
diff --git a/src/calendar/data/MemoryCalendarStore.js b/src/calendar/data/MemoryCalendarStore.js
index 90fa5560..2beee123 100644
--- a/src/calendar/data/MemoryCalendarStore.js
+++ b/src/calendar/data/MemoryCalendarStore.js
@@ -18,7 +18,7 @@ Ext.define('Extensible.calendar.data.MemoryCalendarStore', {
type: 'memory',
reader: {
type: 'json',
- root: 'calendars'
+ rootProperty: 'calendars'
},
writer: {
type: 'json'
@@ -35,7 +35,7 @@ Ext.define('Extensible.calendar.data.MemoryCalendarStore', {
this.idProperty = this.idProperty || Extensible.calendar.data.CalendarMappings.CalendarId.name || 'id';
- this.fields = Extensible.calendar.data.CalendarModel.prototype.fields.getRange();
+ this.fields = Extensible.calendar.data.CalendarModel.prototype.fields;
this.callParent(arguments);
}
diff --git a/src/calendar/data/MemoryEventStore.js b/src/calendar/data/MemoryEventStore.js
index abf44084..076a98c7 100644
--- a/src/calendar/data/MemoryEventStore.js
+++ b/src/calendar/data/MemoryEventStore.js
@@ -28,7 +28,7 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', {
type: 'memory',
reader: {
type: 'json',
- root: 'evts'
+ rootProperty: 'evts'
},
writer: {
type: 'json'
@@ -53,7 +53,7 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', {
this.idProperty = this.idProperty || Extensible.calendar.data.EventMappings.EventId.mapping || 'id';
- this.fields = Extensible.calendar.data.EventModel.prototype.fields.getRange();
+ this.fields = Extensible.calendar.data.EventModel.prototype.fields;
// By default this shared example store will monitor its own CRUD events and
// automatically show a page-level message for each event. This is simply a shortcut
@@ -76,24 +76,9 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', {
}
this.autoMsg = config.autoMsg;
- this.onCreateRecords = Ext.Function.createInterceptor(this.onCreateRecords, this.interceptCreateRecords);
this.initRecs();
},
- // private - override to make sure that any records added in-memory
- // still get a unique PK assigned at the data level
- interceptCreateRecords: function(records, operation, success) {
- if (success) {
- var i = 0,
- rec,
- len = records.length;
-
- for (; i < len; i++) {
- records[i].data[Extensible.calendar.data.EventMappings.EventId.name] = this.idSeed++;
- }
- }
- },
-
// If the store started with preloaded inline data, we have to make sure the records are set up
// properly as valid "saved" records otherwise they may get "added" on initial edit.
initRecs: function() {
@@ -108,19 +93,19 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', {
var me = this;
if (Extensible.example && Extensible.example.msg) {
- var success = operation.wasSuccessful(),
- rec = operation.records[0],
- title = rec.data[Extensible.calendar.data.EventMappings.Title.name];
+ var records = 'Ext.data.operation.Destroy' == Ext.getClass(operation).getName()? operation.getResultSet().getRecords() : operation.getRecords(),
+ record = records[0],
+ title = record.get(Extensible.calendar.data.EventMappings.Title.mapping) || '(No title)';
switch (operation.action) {
case 'create':
- Extensible.example.msg('Add', 'Added "' + Ext.value(title, '(No title)') + '"');
+ Extensible.example.msg('Add', 'Added "' + title + '"');
break;
case 'update':
- Extensible.example.msg('Update', 'Updated "' + Ext.value(title, '(No title)') + '"');
+ Extensible.example.msg('Update', 'Updated "' + title + '"');
break;
case 'destroy':
- Extensible.example.msg('Delete', 'Deleted "' + Ext.value(title, '(No title)') + '"');
+ Extensible.example.msg('Delete', 'Deleted "' + title + '"');
break;
}
}
@@ -152,5 +137,52 @@ Ext.define('Extensible.calendar.data.MemoryEventStore', {
me.loading = false;
me.fireEvent('load', me, records, successful);
+ },
+ listeners: {
+ add: {
+ fn: function(store, records) {
+ var record = records[0],
+ id = this.idSeed++;
+
+ record.phantom = false;
+ record.data[Extensible.calendar.data.EventMappings.EventId.name] = id;
+
+ var operation = Ext.create('Ext.data.operation.Create',{
+ success: true,
+ complete: true,
+ request: Ext.create('Ext.data.Request', { jsonData: record }),
+ records: [record]
+ });
+
+ store.fireAction('write', [store, operation], function(){});
+ }
+ },
+ update: {
+ fn: function(store, record){
+ var operation = Ext.create('Ext.data.operation.Update',{
+ success: true,
+ complete: true,
+ request: Ext.create('Ext.data.Request', { jsonData: record }),
+ records: [record]
+ });
+
+ store.fireAction('write', [store, operation], function(){});
+ }
+ },
+ remove: {
+ fn: function(store, records){
+ var record = records[0];
+
+ var operation = Ext.create('Ext.data.operation.Destroy',{
+ success: true,
+ complete: true,
+ request: Ext.create('Ext.data.Request', { jsonData: record }),
+ _resultSet: Ext.create('Ext.data.ResultSet', { records: [record]})
+ });
+
+ store.fireAction('write', [store, operation], function(){});
+
+ }
+ }
}
});
\ No newline at end of file
diff --git a/src/calendar/dd/DropZone.js b/src/calendar/dd/DropZone.js
index 9d7a932d..0cc17e3d 100644
--- a/src/calendar/dd/DropZone.js
+++ b/src/calendar/dd/DropZone.js
@@ -20,8 +20,8 @@ Ext.define('Extensible.calendar.dd.DropZone', {
getTargetFromEvent: function(e) {
var dragOffset = this.dragOffset || 0,
- y = e.getPageY() - dragOffset,
- d = this.view.getDayAt(e.getPageX(), y);
+ y = e.getY() - dragOffset,
+ d = this.view.getDayAt(e.getX(), y);
return d.el ? d: null;
},
diff --git a/src/calendar/dd/StatusProxy.js b/src/calendar/dd/StatusProxy.js
index 42b7d204..02423baa 100644
--- a/src/calendar/dd/StatusProxy.js
+++ b/src/calendar/dd/StatusProxy.js
@@ -21,10 +21,10 @@ Ext.define('Extensible.calendar.dd.StatusProxy', {
// Overridden to add a separate message element inside the ghost area.
// Applies only to Ext 4.1 and above, see notes in constructor
renderTpl: [
- '',
- '
',
- '',
- '',
+ '' +
+ '
'+
+ '' +
+ '' +
'
'
],
@@ -82,11 +82,17 @@ Ext.define('Extensible.calendar.dd.StatusProxy', {
* @protected
*/
update: function(html) {
- this.callParent(arguments);
-
- // If available, set the ghosted event el to autoHeight for visual consistency
- var el = this.ghost.dom.firstChild;
- if(el) {
+ var el = this.getGhost().dom;
+ if (typeof html == "string") {
+ this.getGhost().setHtml(html);
+ } else {
+ this.getGhost().setHtml('');
+ html.style.margin = "0";
+ this.getGhost().dom.appendChild(html);
+ }
+ if (el) {
+ Ext.fly(el).setStyle('float', 'none');
+ // If available, set the ghosted event el to autoHeight for visual consistency
Ext.fly(el).setHeight('auto');
}
},
@@ -96,6 +102,6 @@ Ext.define('Extensible.calendar.dd.StatusProxy', {
* @param {String} msg The new status message
*/
updateMsg: function(msg) {
- this.message.update(msg);
+ this.update(msg);
}
});
\ No newline at end of file
diff --git a/src/calendar/form/EventDetails.js b/src/calendar/form/EventDetails.js
index 5022aa84..aa770e5e 100644
--- a/src/calendar/form/EventDetails.js
+++ b/src/calendar/form/EventDetails.js
@@ -108,42 +108,42 @@ Ext.define('Extensible.calendar.form.EventDetails', {
layout: 'column',
initComponent: function() {
-
- this.addEvents({
- /**
- * @event eventadd
- * Fires after a new event is added
- * @param {Extensible.calendar.form.EventDetails} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was added
- */
- eventadd: true,
- /**
- * @event eventupdate
- * Fires after an existing event is updated
- * @param {Extensible.calendar.form.EventDetails} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was updated
- */
- eventupdate: true,
- /**
- * @event eventdelete
- * Fires after an event is deleted
- * @param {Extensible.calendar.form.EventDetails} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was deleted
- */
- eventdelete: true,
- /**
- * @event eventcancel
- * Fires after an event add/edit operation is canceled by the user and no store update took place
- * @param {Extensible.calendar.form.EventDetails} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was canceled
- */
- eventcancel: true
- });
-
+
+ //this.addEvents({
+ // /**
+ // * @event eventadd
+ // * Fires after a new event is added
+ // * @param {Extensible.calendar.form.EventDetails} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was added
+ // */
+ // eventadd: true,
+ // /**
+ // * @event eventupdate
+ // * Fires after an existing event is updated
+ // * @param {Extensible.calendar.form.EventDetails} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was updated
+ // */
+ // eventupdate: true,
+ // /**
+ // * @event eventdelete
+ // * Fires after an event is deleted
+ // * @param {Extensible.calendar.form.EventDetails} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was deleted
+ // */
+ // eventdelete: true,
+ // /**
+ // * @event eventcancel
+ // * Fires after an event add/edit operation is canceled by the user and no store update took place
+ // * @param {Extensible.calendar.form.EventDetails} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was canceled
+ // */
+ // eventcancel: true
+ //});
+
this.titleField = Ext.create('Ext.form.field.Text', {
fieldLabel: this.titleLabelText,
name: Extensible.calendar.data.EventMappings.Title.name,
@@ -320,7 +320,7 @@ Ext.define('Extensible.calendar.form.EventDetails', {
name,
obj = {};
- fields.each(function(f) {
+ Ext.each(fields, function(f) {
name = f.name;
if (name in values) {
obj[name] = values[name];
diff --git a/src/calendar/form/EventWindow.js b/src/calendar/form/EventWindow.js
index f289ab73..879c3251 100644
--- a/src/calendar/form/EventWindow.js
+++ b/src/calendar/form/EventWindow.js
@@ -66,6 +66,7 @@ Ext.define('Extensible.calendar.form.EventWindow', {
// General configs
closeAction: 'hide',
+ closable: false,
modal: false,
resizable: false,
constrain: true,
@@ -77,7 +78,18 @@ Ext.define('Extensible.calendar.form.EventWindow', {
formPanelConfig: {
border: false
},
-
+
+ /**
+ * Add close tool to panel header. When closing the editor it is important to cleanup the record if dirty.
+ * Handle it the same way as the cancel button.
+ */
+ tools: [{
+ type:'close',
+ handler: function(evt, el, header){
+ header.ownerCt.onCancel();
+ }
+ }],
+
/**
* @cfg {Boolean} allowDefaultAdd
* @since 1.6.0
@@ -95,57 +107,57 @@ Ext.define('Extensible.calendar.form.EventWindow', {
allowDefaultAdd: true,
initComponent: function() {
- this.addEvents({
- /**
- * @event eventadd
- * Fires after a new event is added
- * @param {Extensible.calendar.form.EventWindow} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was added
- * @param {Ext.Element} el The target element
- */
- eventadd: true,
- /**
- * @event eventupdate
- * Fires after an existing event is updated
- * @param {Extensible.calendar.form.EventWindow} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was updated
- * @param {Ext.Element} el The target element
- */
- eventupdate: true,
- /**
- * @event eventdelete
- * Fires after an event is deleted
- * @param {Extensible.calendar.form.EventWindow} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was deleted
- * @param {Ext.Element} el The target element
- */
- eventdelete: true,
- /**
- * @event eventcancel
- * Fires after an event add/edit operation is canceled by the user and no store update took place
- * @param {Extensible.calendar.form.EventWindow} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
- * record} that was canceled
- * @param {Ext.Element} el The target element
- */
- eventcancel: true,
- /**
- * @event editdetails
- * Fires when the user selects the option in this window to continue editing in the detailed edit form
- * (by default, an instance of {@link Extensible.calendar.form.EventDetails}. Handling code should hide
- * this window and transfer the current event record to the appropriate instance of the detailed form by
- * showing it and calling {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
- * @param {Extensible.calendar.form.EventWindow} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
- * that is currently being edited
- * @param {Ext.Element} el The target element
- */
- editdetails: true
- });
-
+ //this.addEvents({
+ // /**
+ // * @event eventadd
+ // * Fires after a new event is added
+ // * @param {Extensible.calendar.form.EventWindow} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was added
+ // * @param {Ext.Element} el The target element
+ // */
+ // eventadd: true,
+ // /**
+ // * @event eventupdate
+ // * Fires after an existing event is updated
+ // * @param {Extensible.calendar.form.EventWindow} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was updated
+ // * @param {Ext.Element} el The target element
+ // */
+ // eventupdate: true,
+ // /**
+ // * @event eventdelete
+ // * Fires after an event is deleted
+ // * @param {Extensible.calendar.form.EventWindow} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was deleted
+ // * @param {Ext.Element} el The target element
+ // */
+ // eventdelete: true,
+ // /**
+ // * @event eventcancel
+ // * Fires after an event add/edit operation is canceled by the user and no store update took place
+ // * @param {Extensible.calendar.form.EventWindow} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel
+ // * record} that was canceled
+ // * @param {Ext.Element} el The target element
+ // */
+ // eventcancel: true,
+ // /**
+ // * @event editdetails
+ // * Fires when the user selects the option in this window to continue editing in the detailed edit form
+ // * (by default, an instance of {@link Extensible.calendar.form.EventDetails}. Handling code should hide
+ // * this window and transfer the current event record to the appropriate instance of the detailed form by
+ // * showing it and calling {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
+ // * @param {Extensible.calendar.form.EventWindow} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
+ // * that is currently being edited
+ // * @param {Ext.Element} el The target element
+ // */
+ // editdetails: true
+ //});
+
this.fbar = this.getFooterBarConfig();
this.callParent(arguments);
@@ -351,9 +363,9 @@ Ext.define('Extensible.calendar.form.EventWindow', {
},
cleanup: function(hide) {
- if (this.activeRecord) {
- this.activeRecord.reject();
- }
+ //if (this.activeRecord) {
+ // this.activeRecord.reject();
+ //}
delete this.activeRecord;
if (hide===true) {
@@ -371,7 +383,7 @@ Ext.define('Extensible.calendar.form.EventWindow', {
obj = {},
modified;
- fields.each(function(f) {
+ Ext.each(fields, function(f) {
name = f.name;
if (name in values) {
obj[name] = values[name];
@@ -427,7 +439,7 @@ Ext.define('Extensible.calendar.form.EventWindow', {
me.onCancel();
return;
}
-
+
if (me.activeRecord.phantom) {
me.fireEvent('eventadd', me, me.activeRecord, me.animateTarget);
}
diff --git a/src/calendar/gadget/CalendarListMenu.js b/src/calendar/gadget/CalendarListMenu.js
index 9f30114c..6e68c865 100644
--- a/src/calendar/gadget/CalendarListMenu.js
+++ b/src/calendar/gadget/CalendarListMenu.js
@@ -62,13 +62,13 @@ Ext.define('Extensible.calendar.gadget.CalendarListMenu', {
*/
initComponent: function() {
- this.addEvents(
- 'showcalendar',
- 'hidecalendar',
- 'radiocalendar',
- 'colorchange'
- );
-
+ //this.addEvents(
+ // 'showcalendar',
+ // 'hidecalendar',
+ // 'radiocalendar',
+ // 'colorchange'
+ //);
+
Ext.apply(this, {
plain: true,
items: [{
@@ -82,7 +82,7 @@ Ext.define('Extensible.calendar.gadget.CalendarListMenu', {
}]
});
- this.addClass('x-calendar-list-menu');
+ this.addCls('x-calendar-list-menu');
this.callParent(arguments);
},
diff --git a/src/calendar/gadget/CalendarListPanel.js b/src/calendar/gadget/CalendarListPanel.js
index 456ec153..640ea16b 100644
--- a/src/calendar/gadget/CalendarListPanel.js
+++ b/src/calendar/gadget/CalendarListPanel.js
@@ -18,7 +18,15 @@ Ext.define('Extensible.calendar.gadget.CalendarListPanel', {
layout: 'fit',
menuSelector: 'em',
width: 100, // this should be overridden by this container's layout
-
+
+ /**
+ * @cfg {bool} stateful
+ * If set to true, the object will remember the state of hidden and displayed calendars.
+ * Note that this works only, if a persistence provider has been passed to the state
+ * manager E.g. Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
+ */
+ stateful: true,
+
/**
* @cfg {Ext.data.Store} store
* A {@link Ext.data.Store store} containing records of type {@link Extensible.calendar.data.CalendarModel CalendarRecord}.
@@ -29,6 +37,10 @@ Ext.define('Extensible.calendar.gadget.CalendarListPanel', {
initComponent: function() {
this.addCls('x-calendar-list');
this.callParent(arguments);
+
+ if (this.stateful){
+ this.stateId = this.id + '-StateId';
+ }
},
afterRender: function(ct, position) {
@@ -124,34 +136,39 @@ Ext.define('Extensible.calendar.gadget.CalendarListPanel', {
}
this.getListTemplate().overwrite(this.body, data);
},
-
+
getColorCls: function(colorId) {
return 'x-cal-'+colorId+'-ad';
},
- toggleCalendar: function(id, commit) {
+ toggleCalendar: function(id, commit, saveState) {
var rec = this.store.findRecord(Extensible.calendar.data.CalendarMappings.CalendarId.name, id),
CM = Extensible.calendar.data.CalendarMappings,
isHidden = rec.data[CM.IsHidden.name];
-
+
rec.set(CM.IsHidden.name, !isHidden);
if(commit !== false) {
rec.commit();
}
+
+ // Saves the state of the calendars to the persistence store
+ if (saveState !== false) {
+ this.saveState();
+ }
},
- showCalendar: function(id, commit) {
+ showCalendar: function(id, commit, saveState) {
var rec = this.store.findRecord(Extensible.calendar.data.CalendarMappings.CalendarId.name, id);
- if(rec.data[Extensible.calendar.data.CalendarMappings.IsHidden.name] === true) {
- this.toggleCalendar(id, commit);
+ if(rec && rec.data[Extensible.calendar.data.CalendarMappings.IsHidden.name] === true) {
+ this.toggleCalendar(id, commit, saveState);
}
},
- hideCalendar: function(id, commit) {
+ hideCalendar: function(id, commit, saveState) {
var rec = this.store.findRecord(Extensible.calendar.data.CalendarMappings.CalendarId.name, id);
- if(rec.data[Extensible.calendar.data.CalendarMappings.IsHidden.name] !== true) {
- this.toggleCalendar(id, commit);
+ if(rec && rec.data[Extensible.calendar.data.CalendarMappings.IsHidden.name] !== true) {
+ this.toggleCalendar(id, commit, saveState);
}
},
@@ -160,11 +177,11 @@ Ext.define('Extensible.calendar.gadget.CalendarListPanel', {
calendarId = Extensible.calendar.data.CalendarMappings.CalendarId.name,
recs = this.store.getRange(),
len = recs.length;
-
+
for (; i < len; i++) {
recId = recs[i].data[calendarId];
// make a truthy check so that either numeric or string ids can match
- if(recId === id) {
+ if(recId == id) {
this.showCalendar(recId, false);
}
else{
@@ -234,5 +251,39 @@ Ext.define('Extensible.calendar.gadget.CalendarListPanel', {
}
this.menu.setCalendar(id, colorId);
this.menu.showAt(xy);
+ },
+
+ /**
+ * Returns the state to be persisted in a browser cookie. This implements function getState()
+ * from mixin Ext.state.Stateful.
+ * @return {Object}
+ */
+ getState: function() {
+ var state = [],
+ CM = Extensible.calendar.data.CalendarMappings,
+ recs = this.store.getRange(),
+ len = recs.length,
+ i = 0;
+
+ for(; i < len; i++){
+ // Check and save only the ids of hidden calendars
+ if (recs[i].data[CM.IsHidden.name]){
+ state.push(recs[i].data[CM.CalendarId.name]);
+ }
+ }
+
+ return state;
+ },
+
+ /**
+ * Function is called in the constructor to restore the state. This implements function applyState()
+ * from mixin Ext.state.Stateful.
+ * @param {Object} state See function getState() for the structure of state.
+ */
+ applyState: function(state) {
+ for (key in state) {
+ this.hideCalendar(state[key], false, false);
+ }
}
+
});
\ No newline at end of file
diff --git a/src/calendar/menu/Event.js b/src/calendar/menu/Event.js
index 15de3b7c..6f480d2e 100644
--- a/src/calendar/menu/Event.js
+++ b/src/calendar/menu/Event.js
@@ -73,58 +73,58 @@ Ext.define('Extensible.calendar.menu.Event', {
ownerCalendarPanel: {},
initComponent: function() {
- this.addEvents(
- /**
- * @event editdetails
- * Fires when the user selects the option to edit the event details
- * (by default, in an instance of {@link Extensible.calendar.form.EventDetails}. Handling code should
- * transfer the current event record to the appropriate instance of the detailed form by showing
- * the form and calling {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
- * @param {Extensible.calendar.menu.Event} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
- * record} that is currently being edited
- * @param {Ext.Element} el The element associated with this context menu
- */
- 'editdetails',
- /**
- * @event eventdelete
- * Fires after the user selectes the option to delete an event. Note that this menu does not actually
- * delete the event from the data store. This is simply a notification that the menu option was
- * selected -- it is the responsibility of handling code to perform the deletion and any clean
- * up required.
- * @param {Extensible.calendar.menu.Event} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
- * record} for the event to be deleted
- * @param {Ext.Element} el The element associated with this context menu
- */
- 'eventdelete',
- /**
- * @event eventmove
- * Fires after the user selects a date in the calendar picker under the "move event" menu option.
- * Note that this menu does not actually update the event in the data store. This is simply a
- * notification that the menu option was selected -- it is the responsibility of handling code
- * to perform the move action and any clean up required.
- * @param {Extensible.calendar.menu.Event} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
- * record} for the event to be moved
- * @param {Date} dt The new start date for the event (the existing event start time will be preserved)
- */
- 'eventmove',
- /**
- * @event eventcopy
- * Fires after the user selects a date in the calendar picker under the "copy event" menu option.
- * Note that this menu does not actually update the event in the data store. This is simply a
- * notification that the menu option was selected -- it is the responsibility of handling code
- * to perform the copy action.
- * @param {Extensible.calendar.menu.Event} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
- * record} for the event to be copied
- * @param {Date} dt The start date for the event copy (the existing event start time will
- * be preserved)
- */
- 'eventcopy'
- );
-
+ //this.addEvents(
+ // /**
+ // * @event editdetails
+ // * Fires when the user selects the option to edit the event details
+ // * (by default, in an instance of {@link Extensible.calendar.form.EventDetails}. Handling code should
+ // * transfer the current event record to the appropriate instance of the detailed form by showing
+ // * the form and calling {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
+ // * @param {Extensible.calendar.menu.Event} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
+ // * record} that is currently being edited
+ // * @param {Ext.Element} el The element associated with this context menu
+ // */
+ // 'editdetails',
+ // /**
+ // * @event eventdelete
+ // * Fires after the user selectes the option to delete an event. Note that this menu does not actually
+ // * delete the event from the data store. This is simply a notification that the menu option was
+ // * selected -- it is the responsibility of handling code to perform the deletion and any clean
+ // * up required.
+ // * @param {Extensible.calendar.menu.Event} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
+ // * record} for the event to be deleted
+ // * @param {Ext.Element} el The element associated with this context menu
+ // */
+ // 'eventdelete',
+ // /**
+ // * @event eventmove
+ // * Fires after the user selects a date in the calendar picker under the "move event" menu option.
+ // * Note that this menu does not actually update the event in the data store. This is simply a
+ // * notification that the menu option was selected -- it is the responsibility of handling code
+ // * to perform the move action and any clean up required.
+ // * @param {Extensible.calendar.menu.Event} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
+ // * record} for the event to be moved
+ // * @param {Date} dt The new start date for the event (the existing event start time will be preserved)
+ // */
+ // 'eventmove',
+ // /**
+ // * @event eventcopy
+ // * Fires after the user selects a date in the calendar picker under the "copy event" menu option.
+ // * Note that this menu does not actually update the event in the data store. This is simply a
+ // * notification that the menu option was selected -- it is the responsibility of handling code
+ // * to perform the copy action.
+ // * @param {Extensible.calendar.menu.Event} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
+ // * record} for the event to be copied
+ // * @param {Date} dt The start date for the event copy (the existing event start time will
+ // * be preserved)
+ // */
+ // 'eventcopy'
+ //);
+
this.buildMenu();
this.callParent(arguments);
},
diff --git a/src/calendar/template/AgendaBody.js b/src/calendar/template/AgendaBody.js
new file mode 100644
index 00000000..bc934ea2
--- /dev/null
+++ b/src/calendar/template/AgendaBody.js
@@ -0,0 +1,551 @@
+/**
+ * @class Extensible.calendar.template.AgendaBody
+ * @extends Ext.XTemplate
+ *
+ *
This class is currently beta code and the API is still subject to change before the next release.
+ *
+ *
This is the template used to render the {@link Extensible.calendar.view.AgendaBody AgendaBody}.
+ *
+ *
This template is automatically bound to the underlying event store by the
+ * calendar components and expects records of type {@link Extensible.calendar.data.EventModel}.
+ *
+ * @author Gabriel Sidler, sidler@teamup.com
+ * @constructor
+ * @param {Object} config The config object
+ */
+Ext.define('Extensible.calendar.template.AgendaBody', {
+ extend: 'Ext.XTemplate',
+
+ requires: [],
+
+ /**
+ * @cfg {Boolean} linkDatesToDayView
+ * True to link dates to the {@link Extensible.calendar.view.Day day view}.
+ */
+ linkDatesToDayView: true,
+
+ /**
+ * @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',
+
+ /**
+ * @cfg {String} dayDateFormat
+ * The date format for day's date in the list of events (defaults to 'D M j').
+ */
+ dayDateFormat: 'D M j',
+ /**
+ * @cfg {String} hourFormat
+ * The format for event start and end times (defaults to 'g:ia').
+ */
+ hourFormat: 'g:ia',
+ /**
+ * @cfg {String} allDayText
+ * Text used to display in times column for all-day events and for events that last the entire day.
+ */
+ allDayText: 'All day',
+ /**
+ * @cfg {String} locationText
+ * Label used for the event location output.
+ */
+ locationText: 'Location',
+ /**
+ * @cfg {String} webLinkText
+ * Label used for the web link output.
+ */
+ webLinkText: 'Web Link',
+ /**
+ * @cfg {String} notesText
+ * Label used for the event notes output.
+ */
+ notesText: 'Notes',
+ /**
+ * @cfg {String} noEventsText
+ * Text used where there are no events for the selected date range.
+ */
+ noEventsText: 'There are no events for the selected date range.',
+
+ /**
+ * @cfg {String} prevLinkText
+ * Text used for the previous link.
+ */
+ prevLinkText: 'Previous',
+
+ /**
+ * @cfg {String} nextLinkText
+ * Text used for the next link.
+ */
+ nextLinkText: 'Next',
+
+ /**
+ * @cfg {String} reminderTooltip
+ * Text used as tooltip for the reminder icon.
+ */
+ reminderTooltip: 'Reminder is activated',
+
+ /**
+ * @cfg {String} recurringTooltip
+ * Text used as tooltip for the reminder icon.
+ */
+ recurringTooltip: 'Recurring event',
+
+ /**
+ * @cfg {String} showEventDetails
+ * If true, events are displayed with all details, otherwise only a one-line summary is shown.
+ */
+ showEventDetails: false,
+ /**
+ * @cfg {Integer} maxNotesLength
+ * The maximum number of characters shown for the notes text.
+ */
+ maxNotesLength: 100,
+ /**
+ * @cfg {String} prevLinkSelector
+ * The class name applied to the previous link.
+ */
+ prevLinkSelector: 'ext-cal-agenda-bd-prev-link',
+ /**
+ * @cfg {String} nextLinkSelector
+ * The class name applied to the previous link.
+ */
+ nextLinkSelector: 'ext-cal-agenda-bd-next-link',
+
+
+ // private
+ constructor: function(config){
+
+ Ext.apply(this, config);
+
+ // AgendaBody support two templates, an agenda list template and a simple list template.
+ if (this.simpleList){
+ Extensible.calendar.template.AgendaBody.superclass.constructor.call(this, this.getTemplateForSimpleList());
+ } else {
+ Extensible.calendar.template.AgendaBody.superclass.constructor.call(this, this.getTemplateForAgendaList());
+ }
+ },
+
+ // private
+ applyTemplate : function(o){
+ if (Ext.getVersion().isLessThan('4.1')) {
+ return Extensible.calendar.template.AgendaBody.superclass.applyTemplate.call(this, o);
+ }
+ else {
+ return this.applyOut(o, []).join('');
+ }
+ },
+
+ /**
+ * Returns the template used for the agenda list.
+ * @return {Array} A array of strings making up the template.
+ */
+ getTemplateForAgendaList: function() {
+ return [
+ '
'
+ ];
+ },
+
+ /**
+ * 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..bc198fd0 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',
@@ -344,208 +344,208 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
this.addCls('ext-cal-readonly');
}
- this.addEvents({
- /**
- * @event eventsrendered
- * Fires after events are finished rendering in the view
- * @param {Extensible.calendar.view.AbstractCalendar} this
- */
- eventsrendered: true,
- /**
- * @event eventclick
- * Fires after the user clicks on an event element. This is a cancelable event, so returning false from a
- * handler will cancel the click without displaying the event editor view. This could be useful for
- * validating the rules by which events should be editable by the user.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that was clicked on
- * @param {HTMLNode} el The DOM node that was clicked on
- */
- eventclick: true,
- /**
- * @event eventover
- * Fires anytime the mouse is over an event element
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that the cursor is over
- * @param {HTMLNode} el The DOM node that is being moused over
- */
- eventover: true,
- /**
- * @event eventout
- * Fires anytime the mouse exits an event element
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that the cursor exited
- * @param {HTMLNode} el The DOM node that was exited
- */
- eventout: true,
- /**
- * @event beforedatechange
- * Fires before the start date of the view changes, giving you an opportunity to save state or anything else you may need
- * to do prior to the UI view changing. This is a cancelable event, so returning false from a handler will cancel both the
- * view change and the setting of the start date.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Date} startDate The current start date of the view (as explained in {@link #getStartDate}
- * @param {Date} newStartDate The new start date that will be set when the view changes
- * @param {Date} viewStart The first displayed date in the current view
- * @param {Date} viewEnd The last displayed date in the current view
- */
- beforedatechange: true,
- /**
- * @event datechange
- * Fires after the start date of the view has changed. If you need to cancel the date change you should handle the
- * {@link #beforedatechange} event and return false from your handler function.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}
- * @param {Date} viewStart The first displayed date in the view
- * @param {Date} viewEnd The last displayed date in the view
- */
- datechange: true,
- /**
- * @event rangeselect
- * Fires after the user drags on the calendar to select a range of dates/times in which to create an event. This is a
- * cancelable event, so returning false from a handler will cancel the drag operation and clean up any drag shim elements
- * without displaying the event editor view. This could be useful for validating that a user can only create events within
- * a certain range.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Object} dates An object containing the start (StartDate property) and end (EndDate property) dates selected
- * @param {Function} callback A callback function that MUST be called after the event handling is complete so that
- * the view is properly cleaned up (shim elements are persisted in the view while the user is prompted to handle the
- * range selection). The callback is already created in the proper scope, so it simply needs to be executed as a standard
- * function call (e.g., callback()).
- */
- rangeselect: true,
- /**
- * @event beforeeventcopy
- * Fires before an existing event is duplicated by the user via the "copy" command. This is a
- * cancelable event, so returning false from a handler will cancel the copy operation.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
- * record} for the event that will be copied
- * @param {Date} dt The new start date to be set in the copy (the end date will be automaticaly
- * adjusted to match the original event duration)
- */
- beforeeventcopy: true,
- /**
- * @event eventcopy
- * Fires after an event has been duplicated by the user via the "copy" command. If you need to
- * cancel the copy operation you should handle the {@link #beforeeventcopy} event and return
- * false from your handler function.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
- * record} for the event that was copied (with updated start and end dates)
- */
- eventcopy: true,
- /**
- * @event beforeeventmove
- * Fires after an event element has been dragged by the user and dropped in a new position, but before
- * the event record is updated with the new dates, providing a hook for canceling the update.
- * To cancel the move, return false from a handling function. This could be useful for validating
- * that a user can only move events within a certain date range, for example.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
- * for the event that will be moved. Start and end dates will be the original values before the move started.
- * @param {Date} dt The new start date to be set (the end date will be automaticaly calculated to match
- * based on the event duration)
- */
- beforeeventmove: true,
- /**
- * @event eventmove
- * Fires after an event element has been moved to a new position and its data updated. If you need to
- * cancel the move operation you should handle the {@link #beforeeventmove} event and return false
- * from your handler function.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
- * for the event that was moved with updated start and end dates
- */
- eventmove: true,
- /**
- * @event initdrag
- * Fires when a drag operation is initiated in the view
- * @param {Extensible.calendar.view.AbstractCalendar} this
- */
- initdrag: true,
- /**
- * @event dayover
- * Fires while the mouse is over a day element
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Date} dt The date that is being moused over
- * @param {Ext.Element} el The day Element that is being moused over
- */
- dayover: true,
- /**
- * @event dayout
- * Fires when the mouse exits a day element
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Date} dt The date that is exited
- * @param {Ext.Element} el The day Element that is exited
- */
- dayout: true,
- /**
- * @event editdetails
- * Fires when the user selects the option in this window to continue editing in the detailed edit form
- * (by default, an instance of {@link Extensible.calendar.form.EventDetails}. Handling code should hide this window
- * and transfer the current event record to the appropriate instance of the detailed form by showing it
- * and calling {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} that is currently being edited
- * @param {Ext.Element} el The target element
- */
- editdetails: true,
- /**
- * @event eventadd
- * Fires after a new event has been added to the underlying store
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel record} that was added
- */
- eventadd: true,
- /**
- * @event eventupdate
- * Fires after an existing event has been updated
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel record} that was updated
- */
- eventupdate: true,
- /**
- * @event eventcancel
- * Fires after an event add/edit operation has been canceled by the user and no store update took place
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel record} that was canceled
- */
- eventcancel: true,
- /**
- * @event beforeeventdelete
- * Fires before an event is deleted by the user. This is a cancelable event, so returning false from a handler
- * will cancel the delete operation.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that was deleted
- * @param {Ext.Element} el The target element
- */
- beforeeventdelete: true,
- /**
- * @event eventdelete
- * Fires after an event has been deleted by the user. If you need to cancel the delete operation you should handle the
- * {@link #beforeeventdelete} event and return false from your handler function.
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that was deleted
- * @param {Ext.Element} el The target element
- */
- eventdelete: true,
- /**
- * @event eventexception
- * Fires after an event has been processed via an Ext proxy and returned with an exception. This
- * could be because of a server error, or because the data returned success: false.
- *
- * The view provides default handling via the overrideable {@link #notifyOnException} method. If
- * any function handling this event returns false, the notifyOnException method will not be called.
- *
- * Note that only Server proxy and subclasses (including Ajax proxy) will raise this event.
- *
- * @param {Extensible.calendar.view.AbstractCalendar} this
- * @param {Object} response The raw response object returned from the server
- * @param {Ext.data.Operation} operation The operation that was processed
- * @since 1.6.0
- */
- eventexception: true
- });
+ //this.addEvents({
+ // /**
+ // * @event eventsrendered
+ // * Fires after events are finished rendering in the view
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // */
+ // eventsrendered: true,
+ // /**
+ // * @event eventclick
+ // * Fires after the user clicks on an event element. This is a cancelable event, so returning false from a
+ // * handler will cancel the click without displaying the event editor view. This could be useful for
+ // * validating the rules by which events should be editable by the user.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that was clicked on
+ // * @param {HTMLNode} el The DOM node that was clicked on
+ // */
+ // eventclick: true,
+ // /**
+ // * @event eventover
+ // * Fires anytime the mouse is over an event element
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that the cursor is over
+ // * @param {HTMLNode} el The DOM node that is being moused over
+ // */
+ // eventover: true,
+ // /**
+ // * @event eventout
+ // * Fires anytime the mouse exits an event element
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that the cursor exited
+ // * @param {HTMLNode} el The DOM node that was exited
+ // */
+ // eventout: true,
+ // /**
+ // * @event beforedatechange
+ // * Fires before the start date of the view changes, giving you an opportunity to save state or anything else you may need
+ // * to do prior to the UI view changing. This is a cancelable event, so returning false from a handler will cancel both the
+ // * view change and the setting of the start date.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Date} startDate The current start date of the view (as explained in {@link #getStartDate}
+ // * @param {Date} newStartDate The new start date that will be set when the view changes
+ // * @param {Date} viewStart The first displayed date in the current view
+ // * @param {Date} viewEnd The last displayed date in the current view
+ // */
+ // beforedatechange: true,
+ // /**
+ // * @event datechange
+ // * Fires after the start date of the view has changed. If you need to cancel the date change you should handle the
+ // * {@link #beforedatechange} event and return false from your handler function.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Date} startDate The start date of the view (as explained in {@link #getStartDate}
+ // * @param {Date} viewStart The first displayed date in the view
+ // * @param {Date} viewEnd The last displayed date in the view
+ // */
+ // datechange: true,
+ // /**
+ // * @event rangeselect
+ // * Fires after the user drags on the calendar to select a range of dates/times in which to create an event. This is a
+ // * cancelable event, so returning false from a handler will cancel the drag operation and clean up any drag shim elements
+ // * without displaying the event editor view. This could be useful for validating that a user can only create events within
+ // * a certain range.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Object} dates An object containing the start (StartDate property) and end (EndDate property) dates selected
+ // * @param {Function} callback A callback function that MUST be called after the event handling is complete so that
+ // * the view is properly cleaned up (shim elements are persisted in the view while the user is prompted to handle the
+ // * range selection). The callback is already created in the proper scope, so it simply needs to be executed as a standard
+ // * function call (e.g., callback()).
+ // */
+ // rangeselect: true,
+ // /**
+ // * @event beforeeventcopy
+ // * Fires before an existing event is duplicated by the user via the "copy" command. This is a
+ // * cancelable event, so returning false from a handler will cancel the copy operation.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
+ // * record} for the event that will be copied
+ // * @param {Date} dt The new start date to be set in the copy (the end date will be automaticaly
+ // * adjusted to match the original event duration)
+ // */
+ // beforeeventcopy: true,
+ // /**
+ // * @event eventcopy
+ // * Fires after an event has been duplicated by the user via the "copy" command. If you need to
+ // * cancel the copy operation you should handle the {@link #beforeeventcopy} event and return
+ // * false from your handler function.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
+ // * record} for the event that was copied (with updated start and end dates)
+ // */
+ // eventcopy: true,
+ // /**
+ // * @event beforeeventmove
+ // * Fires after an event element has been dragged by the user and dropped in a new position, but before
+ // * the event record is updated with the new dates, providing a hook for canceling the update.
+ // * To cancel the move, return false from a handling function. This could be useful for validating
+ // * that a user can only move events within a certain date range, for example.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
+ // * for the event that will be moved. Start and end dates will be the original values before the move started.
+ // * @param {Date} dt The new start date to be set (the end date will be automaticaly calculated to match
+ // * based on the event duration)
+ // */
+ // beforeeventmove: true,
+ // /**
+ // * @event eventmove
+ // * Fires after an event element has been moved to a new position and its data updated. If you need to
+ // * cancel the move operation you should handle the {@link #beforeeventmove} event and return false
+ // * from your handler function.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record}
+ // * for the event that was moved with updated start and end dates
+ // */
+ // eventmove: true,
+ // /**
+ // * @event initdrag
+ // * Fires when a drag operation is initiated in the view
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // */
+ // initdrag: true,
+ // /**
+ // * @event dayover
+ // * Fires while the mouse is over a day element
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Date} dt The date that is being moused over
+ // * @param {Ext.Element} el The day Element that is being moused over
+ // */
+ // dayover: true,
+ // /**
+ // * @event dayout
+ // * Fires when the mouse exits a day element
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Date} dt The date that is exited
+ // * @param {Ext.Element} el The day Element that is exited
+ // */
+ // dayout: true,
+ // /**
+ // * @event editdetails
+ // * Fires when the user selects the option in this window to continue editing in the detailed edit form
+ // * (by default, an instance of {@link Extensible.calendar.form.EventDetails}. Handling code should hide this window
+ // * and transfer the current event record to the appropriate instance of the detailed form by showing it
+ // * and calling {@link Extensible.calendar.form.EventDetails#loadRecord loadRecord}.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} that is currently being edited
+ // * @param {Ext.Element} el The target element
+ // */
+ // editdetails: true,
+ // /**
+ // * @event eventadd
+ // * Fires after a new event has been added to the underlying store
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel record} that was added
+ // */
+ // eventadd: true,
+ // /**
+ // * @event eventupdate
+ // * Fires after an existing event has been updated
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel record} that was updated
+ // */
+ // eventupdate: true,
+ // /**
+ // * @event eventcancel
+ // * Fires after an event add/edit operation has been canceled by the user and no store update took place
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The new {@link Extensible.calendar.data.EventModel record} that was canceled
+ // */
+ // eventcancel: true,
+ // /**
+ // * @event beforeeventdelete
+ // * Fires before an event is deleted by the user. This is a cancelable event, so returning false from a handler
+ // * will cancel the delete operation.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that was deleted
+ // * @param {Ext.Element} el The target element
+ // */
+ // beforeeventdelete: true,
+ // /**
+ // * @event eventdelete
+ // * Fires after an event has been deleted by the user. If you need to cancel the delete operation you should handle the
+ // * {@link #beforeeventdelete} event and return false from your handler function.
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel record} for the event that was deleted
+ // * @param {Ext.Element} el The target element
+ // */
+ // eventdelete: true,
+ // /**
+ // * @event eventexception
+ // * Fires after an event has been processed via an Ext proxy and returned with an exception. This
+ // * could be because of a server error, or because the data returned success: false.
+ // *
+ // * The view provides default handling via the overrideable {@link #notifyOnException} method. If
+ // * any function handling this event returns false, the notifyOnException method will not be called.
+ // *
+ // * Note that only Server proxy and subclasses (including Ajax proxy) will raise this event.
+ // *
+ // * @param {Extensible.calendar.view.AbstractCalendar} this
+ // * @param {Object} response The raw response object returned from the server
+ // * @param {Ext.data.Operation} operation The operation that was processed
+ // * @since 1.6.0
+ // */
+ // eventexception: true
+ //});
},
afterRender: function() {
@@ -962,9 +962,13 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
// edited or was recurring before being edited AND an event store reload has not been triggered already for
// this operation. If an event is not currently recurring (isRecurring = false) but still has an instance
// start date set, then it must have been recurring and edited to no longer recur.
- var RInstanceStartDate = Extensible.calendar.data.EventMappings.RInstanceStartDate,
- isInstance = RInstanceStartDate && !!operation.records[0].get(RInstanceStartDate.name),
- reload = (operation.records[0].isRecurring() || isInstance) && !operation.wasStoreReloadTriggered;
+ var records = 'Ext.data.operation.Destroy' == Ext.getClass(operation).getName()? operation.getResultSet().getRecords() : operation.getRecords(),
+ record = records[0],
+ RInstanceStartDate = Extensible.calendar.data.EventMappings.RInstanceStartDate,
+ isInstance = RInstanceStartDate && !!record.get(RInstanceStartDate.name),
+ reload = isInstance && !operation.wasStoreReloadTriggered;
+
+ //reload = (record.isRecurring() || isInstance) && !operation.wasStoreReloadTriggered;
if (reload) {
// For calendar views with a body and a header component (e.g. weekly view, day view), this function is
@@ -987,7 +991,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
this.refreshAfterEventChange('update', operation);
- var rec = operation.records[0];
+ var records = operation.getRecords(), rec = records[0];
if (this.enableFx && this.enableUpdateFx) {
this.doUpdateFx(this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]), {
@@ -1012,7 +1016,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
},
onAdd: function(store, operation) {
- var rec = operation.records[0];
+ var records = operation.getRecords(), rec = records[0];
if (this.hidden === true || this.ownerCt.hidden === true || this.monitorStoreEvents === false) {
// Hidden calendar view don't need to be refreshed. For views composed of header and body (for example
@@ -1064,18 +1068,20 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
Extensible.log('onRemove');
this.dismissEventEditor();
- var rec = operation.records[0];
+ if (operation.getResultSet()){
+ var records = operation.getResultSet().getRecords(), rec = records[0];
- if (this.enableFx && this.enableRemoveFx) {
- this.doRemoveFx(this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]), {
- remove: true,
- scope: this,
- callback: Ext.bind(this.refreshAfterEventChange, this, ['delete', operation])
- });
- }
- else {
- this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]).remove();
- this.refreshAfterEventChange('delete', operation);
+ if (this.enableFx && this.enableRemoveFx) {
+ this.doRemoveFx(this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]), {
+ remove: true,
+ scope: this,
+ callback: Ext.bind(this.refreshAfterEventChange, this, ['delete', operation])
+ });
+ }
+ else {
+ this.getEventEls(rec.data[Extensible.calendar.data.EventMappings.EventId.name]).remove();
+ this.refreshAfterEventChange('delete', operation);
+ }
}
},
@@ -1441,8 +1447,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 +1583,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);
}
@@ -1641,13 +1681,15 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
},
getEventRecord: function(id) {
- var idx = this.store.find(Extensible.calendar.data.EventMappings.EventId.name, id,
- 0, // start index
- false, // match any part of string
- true, // case sensitive
- true // force exact match
- );
- return this.store.getAt(idx);
+ //var idx = this.store.find(Extensible.calendar.data.EventMappings.EventId.name, id,
+ // 0, // start index
+ // false, // match any part of string
+ // true, // case sensitive
+ // true // force exact match
+ //);
+ //return this.store.getAt(idx);
+
+ return this.store.getById(id);
},
getEventRecordFromEl: function(el) {
@@ -1761,8 +1803,6 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
onWrite: function(store, operation) {
if (operation.wasSuccessful()) {
- //var rec = operation.records[0];
-
switch(operation.action) {
case 'create':
this.onAdd(store, operation);
@@ -2025,7 +2065,7 @@ Ext.define('Extensible.calendar.view.AbstractCalendar', {
if (el) {
var id = me.getEventIdFromEl(el),
rec = me.getEventRecord(id);
-
+
if (rec && me.fireEvent('eventclick', me, rec, el) !== false) {
if (me.readOnly !== true) {
me.showEventEditor(rec, el);
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..6a2ce91b
--- /dev/null
+++ b/src/calendar/view/AgendaBody.js
@@ -0,0 +1,667 @@
+/**
+ * @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
+ };
+
+ /**
+ * @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.
+ */
+
+ 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..3d39f0b6
--- /dev/null
+++ b/src/calendar/view/AgendaHeader.js
@@ -0,0 +1,472 @@
+/**
+ * @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);
+
+ /**
+ * @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}.
+ */
+
+ /**
+ * @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}.
+ */
+
+ },
+
+ /**
+ *
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:
+ * @return {Object}
+ */
+ getGroupByOptions: function() {
+ return [
+ {name : this.groupByNone, value: 'none'},
+ {name : this.groupByMonths, value: 'month'},
+ {name : this.groupByWeek, value: 'week'}
+ ];
+ },
+
+ /* Private
+ * Event handler that is called when the form changes.
+ * @param {Ext.form.field.Field} field
+ * @param {Object} newValue
+ * @param {Object} oldValue
+ * @param {Object} eOpts
+ */
+ onFormChange: function(field, newValue, oldValue, eOpts){
+ this.fireEvent('formchange', this, this.getForm(), field, newValue, oldValue, eOpts);
+ this.saveState();
+ },
+
+ /* Private
+ * Event handler that is called when the user clicks on the add event button.
+ * @param {Extensible.calendar.view.AgendaHeader} this
+ * @param {Ext.button.Button} bt
+ * @param {Event} e
+ * @param {Object} eOpts
+ */
+ onAddEvent: function(bt, e, eOpts){
+ this.fireEvent('addevent', this, bt, e, eOpts);
+ },
+
+ // private
+ afterRender : function(){
+ this.callParent(arguments);
+
+ this.dateRangeField = this.down('#' + this.id + '-daterange');
+ this.dateRangeField.setValue(this.dateRangeDefault);
+ this.dateRangeField.on('change', this.onFormChange, this);
+ this.groupByField = this.down('#' + this.id + '-groupby');
+ if (this.groupByField) {
+ this.groupByField.setValue(this.groupBy);
+ this.groupByField.on('change', this.onFormChange, this);
+ }
+ this.showDetailsCheckbox = this.down('#' + this.id + '-showdetails');
+ this.showDetailsCheckbox.setValue(this.showDetailsDefault);
+ this.showDetailsCheckbox.on('change', this.onFormChange, this);
+ },
+
+ // private
+ refresh : function(reloadData){
+ Extensible.log('refresh (AgendaHeader)');
+ this.callParent(arguments);
+ },
+
+ /**
+ * This method is called by the {@link Extensible.calendar.view.Agenda Agenda} view that hosts this header when the user chooses to
+ * move to a new date. The current implementation does nothing but can be overriden to update the header form if
+ * necessary.
+ * @param {Date} dt The new view start date.
+ */
+ moveTo : function(dt){
+ },
+
+ /**
+ * Returns the state to be persisted in a browser cookie. This implements function getState()
+ * from mixin Ext.state.Stateful.
+ * @return {Object}
+ */
+ getState: function() {
+ var state = {
+ daterange: this.dateRangeField.getValue(),
+ showdetails: this.showDetailsCheckbox.getValue()
+ };
+ if (this.groupByField) {
+ state.groupby = this.groupByField.getValue();
+ }
+ return state;
+ },
+
+ /**
+ * Function is called in the constructor to restore the state. This implements function applyState()
+ * from mixin Ext.state.Stateful.
+ * @param {Object} state See function getState() for the structure of state.
+ */
+ applyState: function(state) {
+ if (state) {
+ if (state.daterange) {
+ var dateRangeValues = this.getDateRangeOptions();
+ for (var i = 0; i < dateRangeValues.length; i++ ) {
+ var option = dateRangeValues[i];
+ if (option.value == state.daterange) {
+ this.dateRangeDefault = state.daterange;
+ break;
+ }
+ }
+ }
+ if (state.showdetails === true || state.showdetails === false) {
+ this.showDetailsDefault = state.showdetails;
+ }
+ if (state.groupby) {
+ var groupByValues = this.getGroupByOptions();
+ for (var i = 0; i < groupByValues.length; i++ ) {
+ var option = groupByValues[i];
+ if (option.value == state.groupby) {
+ this.groupBy = state.groupby;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+});
\ No newline at end of file
diff --git a/src/calendar/view/DayBody.js b/src/calendar/view/DayBody.js
index c1ef35a4..246a94fe 100644
--- a/src/calendar/view/DayBody.js
+++ b/src/calendar/view/DayBody.js
@@ -29,42 +29,42 @@ Ext.define('Extensible.calendar.view.DayBody', {
this.incrementsPerHour = this.hourIncrement / this.ddIncrement;
this.minEventHeight = this.minEventDisplayMinutes / (this.hourIncrement / this.hourHeight);
- this.addEvents({
- /**
- * @event beforeeventresize
- * Fires after the user drags the resize handle of an event to resize it, but before the resize
- * operation is carried out. This is a cancelable event, so returning false from a handler will
- * cancel the resize operation.
- * @param {Extensible.calendar.view.DayBody} this
- * @param {Extensible.calendar.data.EventModel} rec The original {@link
- * Extensible.calendar.data.EventModel record} for the event that was resized
- * @param {Object} data An object containing the new start and end dates that will be set into the
- * event record if the event is not canceled. Format of the object is: {StartDate: [date], EndDate: [date]}
- */
- beforeeventresize: true,
- /**
- * @event eventresize
- * Fires after the user has drag-dropped the resize handle of an event and the resize operation is
- * complete. If you need to cancel the resize operation you should handle the {@link #beforeeventresize}
- * event and return false from your handler function.
- * @param {Extensible.calendar.view.DayBody} this
- * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
- * record} for the event that was resized containing the updated start and end dates
- */
- eventresize: true,
- /**
- * @event dayclick
- * Fires after the user clicks within the view container and not on an event element. This is a
- * cancelable event, so returning false from a handler will cancel the click without displaying the event
- * editor view. This could be useful for validating that a user can only create events on certain days.
- * @param {Extensible.calendar.view.DayBody} this
- * @param {Date} dt The date/time that was clicked on
- * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks
- * within the DayBodyView always return false for this param.
- * @param {Ext.Element} el The Element that was clicked on
- */
- dayclick: true
- });
+ //this.addEvents({
+ // /**
+ // * @event beforeeventresize
+ // * Fires after the user drags the resize handle of an event to resize it, but before the resize
+ // * operation is carried out. This is a cancelable event, so returning false from a handler will
+ // * cancel the resize operation.
+ // * @param {Extensible.calendar.view.DayBody} this
+ // * @param {Extensible.calendar.data.EventModel} rec The original {@link
+ // * Extensible.calendar.data.EventModel record} for the event that was resized
+ // * @param {Object} data An object containing the new start and end dates that will be set into the
+ // * event record if the event is not canceled. Format of the object is: {StartDate: [date], EndDate: [date]}
+ // */
+ // beforeeventresize: true,
+ // /**
+ // * @event eventresize
+ // * Fires after the user has drag-dropped the resize handle of an event and the resize operation is
+ // * complete. If you need to cancel the resize operation you should handle the {@link #beforeeventresize}
+ // * event and return false from your handler function.
+ // * @param {Extensible.calendar.view.DayBody} this
+ // * @param {Extensible.calendar.data.EventModel} rec The {@link Extensible.calendar.data.EventModel
+ // * record} for the event that was resized containing the updated start and end dates
+ // */
+ // eventresize: true,
+ // /**
+ // * @event dayclick
+ // * Fires after the user clicks within the view container and not on an event element. This is a
+ // * cancelable event, so returning false from a handler will cancel the click without displaying the event
+ // * editor view. This could be useful for validating that a user can only create events on certain days.
+ // * @param {Extensible.calendar.view.DayBody} this
+ // * @param {Date} dt The date/time that was clicked on
+ // * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks
+ // * within the DayBodyView always return false for this param.
+ // * @param {Ext.Element} el The Element that was clicked on
+ // */
+ // dayclick: true
+ //});
},
initDD: function() {
@@ -405,11 +405,17 @@ Ext.define('Extensible.calendar.view.DayBody', {
evtData._height = Math.max(((endMins - startMins) * heightFactor), this.minEventHeight) + evtOffsets.height;
},
+ /**
+ * Render events.
+ * The event layout is based on this article: http://stackoverflow.com/questions/11311410/ and this sample
+ * implementation http://jsbin.com/detefuveta/5/edit?html,js,output *
+ */
renderItems: function() {
var day = 0,
evt,
- evts = [];
-
+ evts = [],
+ M = Extensible.calendar.data.EventMappings;
+
for (; day < this.dayCount; day++) {
var ev = 0,
emptyCells = 0,
@@ -423,7 +429,6 @@ Ext.define('Extensible.calendar.view.DayBody', {
continue;
}
var item = evt.data || evt.event.data,
- M = Extensible.calendar.data.EventMappings,
ad = item[M.IsAllDay.name] === true,
span = this.isEventSpanning(evt.event || evt),
renderAsAllDay = ad || span;
@@ -443,57 +448,116 @@ Ext.define('Extensible.calendar.view.DayBody', {
}
}
- // overlapping event pre-processing loop
+ // Layout events
var i = 0,
j = 0,
- overlapCols = [],
l = evts.length,
- prevDt,
- evt2,
- dt;
-
- for (; i= lastEventEnding) {
+ // This event does not overlap with the current event group. Start a new event group.
+ eventGroups.push(columns);
+ columns = [];
+ lastEventEnding = 0;
+ }
+ var placed = false;
+
+ for (j = 0; j < columns.length; j++) {
+ var col = columns[ j ];
+ if (!this.isOverlapping( col[col.length-1], evt ) ) {
+ col.push(evt);
+ placed = true;
+ break;
}
}
+
+ if (!placed) {
+ columns.push([evt]);
+ }
+
+ // Remember the last event time of the event group.
+ // Very short events have a minimum duration on screen (we can't see a one minute event).
+ var eventDuration = evt.data[M.EndDate.name].getTime() - evt.data[M.StartDate.name].getTime();
+ var eventEnding;
+ if (eventDuration < minEventDuration) {
+ eventEnding = evt.data[M.StartDate.name].getTime() + minEventDuration;
+ } else {
+ eventEnding = evt.data[M.EndDate.name].getTime();
+ }
+ if (eventEnding > lastEventEnding) {
+ lastEventEnding = eventEnding;
+ }
}
- // rendering loop
+ // Push the last event group, if there is one.
+ if(columns.length > 0){
+ eventGroups.push(columns);
+ }
+
+ // Rendering loop
+ l = eventGroups.length;
+ // Loop over all the event groups.
for (i = 0; i < l; i++) {
- evt = evts[i].data;
- dt = evt[Extensible.calendar.data.EventMappings.StartDate.name].getDate();
+ var evtGroup = eventGroups[i];
+ var numColumns = evtGroup.length;
+
+ // Loop over all the virtual columns of an event group
+ for (j = 0; j < numColumns; j++) {
+ col = evtGroup[j];
- if(evt._overlap !== undefined) {
- var colWidth = 100 / (overlapCols[dt]+1),
- evtWidth = 100 - (colWidth * evt._overlap);
+ // Loop over all the events of a virtual column
+ for (var k = 0; k < col.length; k++) {
+ evt = col[k];
- evt._width = colWidth;
- evt._left = colWidth * evt._overcol;
+ // Check if event is rightmost of a group and can be expanded to the right
+ var colSpan = this.expandEvent(evt, j, evtGroup);
+
+ evt.data._width = (100 * colSpan / numColumns);
+ evt.data._left = (j / numColumns) * 100;
+ var markup = this.getEventTemplate().apply(evt.data),
+ target = this.id + '-day-col-' + Ext.Date.format(evt.date, 'Ymd');
+ Ext.DomHelper.append(target, markup);
+ }
}
- var markup = this.getEventTemplate().apply(evt),
- target = this.id + '-day-col-' + Ext.Date.format(evts[i].date, 'Ymd');
-
- Ext.DomHelper.append(target, markup);
}
this.fireEvent('eventsrendered', this);
+ },
+
+ /**
+ * Expand events at the far right to use up any remaining space. This implements step 5 in the layout
+ * algorithm described here: http://stackoverflow.com/questions/11311410/
+ * @param {Object} evt Event to process.
+ * @param {int} iColumn Virtual column to where the event will be rendered.
+ * @param {Array} columns List of virtual colums for event group. Each column contains a list of events.
+ * @return {Number}
+ */
+ expandEvent: function(evt, iColumn, columns) {
+ var colSpan = 1;
+
+ // To see the output without event expansion, uncomment
+ // the line below. Watch column 3 in the output.
+ // return colSpan;
+
+ for (var i = iColumn + 1; i < columns.length; i++)
+ {
+ var col = columns[i];
+ for (var j = 0; j < col.length; j++)
+ {
+ var evt1 = col[j];
+ if (this.isOverlapping(evt, evt1))
+ {
+ return colSpan;
+ }
+ }
+ colSpan++;
+ }
+ return colSpan;
},
getDayEl: function(dt) {
diff --git a/src/calendar/view/Month.js b/src/calendar/view/Month.js
index a6f27567..7b39ac74 100644
--- a/src/calendar/view/Month.js
+++ b/src/calendar/view/Month.js
@@ -88,36 +88,36 @@ Ext.define('Extensible.calendar.view.Month', {
initComponent: function() {
this.callParent(arguments);
-
- this.addEvents({
- /**
- * @event dayclick
- * Fires after the user clicks within the view container and not on an event element. This is a
- * cancelable event, so returning false from a handler will cancel the click without displaying the event
- * editor view. This could be useful for validating that a user can only create events on certain days.
- * @param {Extensible.calendar.view.Month} this
- * @param {Date} dt The date/time that was clicked on
- * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks
- * within the MonthView always return true for this param.
- * @param {Ext.Element} el The Element that was clicked on
- */
- dayclick: true,
- /**
- * @event weekclick
- * Fires after the user clicks within a week link (when {@link #showWeekLinks is true)
- * @param {Extensible.calendar.view.Month} this
- * @param {Date} dt The start date of the week that was clicked on
- */
- weekclick: true,
- /**
- * @protected
- */
- dayover: true,
- /**
- * @protected
- */
- dayout: true
- });
+
+ //this.addEvents({
+ // /**
+ // * @event dayclick
+ // * Fires after the user clicks within the view container and not on an event element. This is a
+ // * cancelable event, so returning false from a handler will cancel the click without displaying the event
+ // * editor view. This could be useful for validating that a user can only create events on certain days.
+ // * @param {Extensible.calendar.view.Month} this
+ // * @param {Date} dt The date/time that was clicked on
+ // * @param {Boolean} allday True if the day clicked on represents an all-day box, else false. Clicks
+ // * within the MonthView always return true for this param.
+ // * @param {Ext.Element} el The Element that was clicked on
+ // */
+ // dayclick: true,
+ // /**
+ // * @event weekclick
+ // * Fires after the user clicks within a week link (when {@link #showWeekLinks is true)
+ // * @param {Extensible.calendar.view.Month} this
+ // * @param {Date} dt The start date of the week that was clicked on
+ // */
+ // weekclick: true,
+ // /**
+ // * @protected
+ // */
+ // dayover: true,
+ // /**
+ // * @protected
+ // */
+ // dayout: true
+ //});
},
initDD: function() {
@@ -527,7 +527,7 @@ Ext.define('Extensible.calendar.view.Month', {
p.setHeight(calculatedHeight);
p.show();
- p.getPositionEl().alignTo(dayEl, 't-t?');
+ p.alignTo(dayEl, 't-t?');
},
onHide: function() {
diff --git a/src/calendar/view/MonthDayDetail.js b/src/calendar/view/MonthDayDetail.js
index f3618e71..9ea755c6 100644
--- a/src/calendar/view/MonthDayDetail.js
+++ b/src/calendar/view/MonthDayDetail.js
@@ -15,10 +15,6 @@ Ext.define('Extensible.calendar.view.MonthDayDetail', {
initComponent: function() {
this.callParent(arguments);
-
- this.addEvents({
- eventsrendered: true
- });
},
afterRender: function() {
diff --git a/src/data/Model.js b/src/data/Model.js
index da35ae75..d8625dd4 100644
--- a/src/data/Model.js
+++ b/src/data/Model.js
@@ -5,7 +5,10 @@ Ext.define('Extensible.data.Model', {
extend: 'Ext.data.Model',
requires: [
- 'Ext.util.MixedCollection'
+ 'Ext.util.MixedCollection',
+ 'Ext.data.field.Date',
+ 'Ext.data.field.Boolean',
+ 'Ext.data.field.Field'
],
// *Must* be defined by subclasses
@@ -49,11 +52,19 @@ Ext.define('Extensible.data.Model', {
}
}
- proto.fields.clear();
+ proto.fields.length = 0;
len = fields.length;
-
+
for (; i < len; i++) {
- proto.fields.add(Ext.create('Ext.data.Field', fields[i]));
+ if ('date' == fields[i]['type']) {
+ proto.fields.push(Ext.create('Ext.data.field.Date', fields[i]));
+ } else if ('boolean' == fields[i]['type']){
+ proto.fields.push(Ext.create('Ext.data.field.Boolean', fields[i]));
+ } else if ('int' == fields[i]['type']){
+ proto.fields.push(Ext.create('Ext.data.field.Integer', fields[i]));
+ } else {
+ proto.fields.push(Ext.create('Ext.data.field.Field', fields[i]));
+ }
}
return this;
}
@@ -74,10 +85,10 @@ Ext.define('Extensible.data.Model', {
*/
clone: function(preserveId) {
var copy = Ext.create(this.$className),
- dataProp = this.persistenceProperty;
-
+ dataProp = 'data';
+
copy[dataProp] = Ext.Object.merge({}, this[dataProp]);
-
+
if (preserveId !== true) {
delete copy[dataProp][this.idProperty];
}
diff --git a/src/form/recurrence/AbstractOption.js b/src/form/recurrence/AbstractOption.js
index 7bcdaf2a..bec63743 100644
--- a/src/form/recurrence/AbstractOption.js
+++ b/src/form/recurrence/AbstractOption.js
@@ -56,18 +56,18 @@ Ext.define('Extensible.form.recurrence.AbstractOption', {
initComponent: function() {
var me = this;
-
- me.addEvents(
- /**
- * @event change
- * Fires when a user-initiated change is detected in the value of the field.
- * @param {Extensible.form.recurrence.AbstractOption} this
- * @param {Mixed} newValue The new value
- * @param {Mixed} oldValue The old value
- */
- 'change'
- );
-
+
+ //me.addEvents(
+ // /**
+ // * @event change
+ // * Fires when a user-initiated change is detected in the value of the field.
+ // * @param {Extensible.form.recurrence.AbstractOption} this
+ // * @param {Mixed} newValue The new value
+ // * @param {Mixed} oldValue The old value
+ // */
+ // 'change'
+ //);
+
me.initRRule();
me.items = me.getItemConfigs();
diff --git a/src/form/recurrence/Fieldset.js b/src/form/recurrence/Fieldset.js
index 03e80c42..f427f855 100644
--- a/src/form/recurrence/Fieldset.js
+++ b/src/form/recurrence/Fieldset.js
@@ -73,18 +73,18 @@ Ext.define('Extensible.form.recurrence.Fieldset', {
delete me.height;
me.autoHeight = true;
}
-
- this.addEvents(
- /**
- * @event startchange
- * Fires when the start date of the recurrence series is changed
- * @param {Extensible.form.recurrence.option.Interval} this
- * @param {Date} newDate The new start date
- * @param {Date} oldDate The previous start date
- */
- 'startchange'
- );
-
+
+ //this.addEvents(
+ // /**
+ // * @event startchange
+ // * Fires when the start date of the recurrence series is changed
+ // * @param {Extensible.form.recurrence.option.Interval} this
+ // * @param {Date} newDate The new start date
+ // * @param {Date} oldDate The previous start date
+ // */
+ // 'startchange'
+ //);
+
me.initRRule();
me.items = [{
diff --git a/src/form/recurrence/FrequencyCombo.js b/src/form/recurrence/FrequencyCombo.js
index b8ee7b01..f8f13f20 100644
--- a/src/form/recurrence/FrequencyCombo.js
+++ b/src/form/recurrence/FrequencyCombo.js
@@ -22,7 +22,7 @@ Ext.define('Extensible.form.recurrence.FrequencyCombo', {
initComponent: function() {
var me = this;
-
+
/**
* @event frequencychange
* Fires when a frequency list item is selected.
@@ -30,10 +30,10 @@ Ext.define('Extensible.form.recurrence.FrequencyCombo', {
* @param {String} value The selected frequency value (one of the names
* from {@link #frequencyOptions}, e.g. 'DAILY')
*/
- me.addEvents('frequencychange');
-
- var freq = Extensible.form.recurrence.Parser.strings.frequency;
-
+ //me.addEvents('frequencychange');
+
+ var freq = Extensible.form.recurrence.Parser.config.strings.frequency;
+
/**
* @cfg {Array} frequencyOptions
* An array of arrays, each containing the name/value pair that defines a recurring
@@ -71,7 +71,7 @@ Ext.define('Extensible.form.recurrence.FrequencyCombo', {
me.callParent(arguments);
},
- onSelect: function(combo, records) {
- this.fireEvent('frequencychange', records[0].data.id);
+ onSelect: function(combo, record) {
+ this.fireEvent('frequencychange', record.data.id);
}
});
\ No newline at end of file
diff --git a/src/form/recurrence/option/Interval.js b/src/form/recurrence/option/Interval.js
index 4c364637..36788ac6 100644
--- a/src/form/recurrence/option/Interval.js
+++ b/src/form/recurrence/option/Interval.js
@@ -27,16 +27,16 @@ Ext.define('Extensible.form.recurrence.option.Interval', {
cls: 'extensible-recur-interval',
initComponent: function() {
- this.addEvents(
- /**
- * @event startchange
- * Fires when the start date of the recurrence series is changed
- * @param {Extensible.form.recurrence.option.Interval} this
- * @param {Date} newDate The new start date
- * @param {Date} oldDate The previous start date
- */
- 'startchange'
- );
+ //this.addEvents(
+ // /**
+ // * @event startchange
+ // * Fires when the start date of the recurrence series is changed
+ // * @param {Extensible.form.recurrence.option.Interval} this
+ // * @param {Date} newDate The new start date
+ // * @param {Date} oldDate The previous start date
+ // */
+ // 'startchange'
+ //);
this.callParent(arguments);
},
diff --git a/src/form/recurrence/option/Weekly.js b/src/form/recurrence/option/Weekly.js
index 73ab7587..8453942a 100644
--- a/src/form/recurrence/option/Weekly.js
+++ b/src/form/recurrence/option/Weekly.js
@@ -41,7 +41,7 @@ Ext.define('Extensible.form.recurrence.option.Weekly', {
*/
getCheckboxGroupItems: function() {
var weekdaysId = Extensible.form.recurrence.Parser.byDayNames,
- weekdaysText = Extensible.form.recurrence.Parser.strings.dayNamesShortByIndex,
+ weekdaysText = Extensible.form.recurrence.Parser.config.strings.dayNamesShortByIndex,
checkboxArray = [],
i = this.startDay;
diff --git a/src/locale/extensible-lang-de.js b/src/locale/extensible-lang-de.js
index fe61a2a2..8465af1e 100644
--- a/src/locale/extensible-lang-de.js
+++ b/src/locale/extensible-lang-de.js
@@ -40,6 +40,8 @@ Ext.onReady(function() {
dayText: 'Tag',
weekText: 'Woche',
monthText: 'Monat',
+ agendaText: 'Agenda',
+ listText: 'Liste',
jumpToText: 'Springe zu:',
goText: 'Los',
multiDayText: '{0} Tage',
@@ -276,13 +278,13 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.FrequencyCombo) {
+ if (exists('Extensible.form.recurrence.FrequencyCombo')) {
Ext.apply(Extensible.form.recurrence.FrequencyCombo.prototype, {
fieldLabel: 'Wiederholen'
});
}
- if (Extensible.form.recurrence.RangeEditWindow) {
+ if (exists('Extensible.form.recurrence.RangeEditWindow')) {
Ext.apply(Extensible.form.recurrence.RangeEditWindow.prototype, {
title: 'Wiederkehrender Termin',
saveButtonText: 'Speichern',
@@ -290,7 +292,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.RangeEditPanel) {
+ if (exists('Extensible.form.recurrence.RangeEditPanel')) {
Ext.apply(Extensible.form.recurrence.RangeEditPanel.prototype, {
headerText: 'Auf welche Termine dieser Termin-Serie möchten Sie Ihre Änderungen anwenden?',
optionSingleButtonText: 'Nur diesen',
@@ -302,7 +304,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Interval) {
+ if (exists('Extensible.form.recurrence.option.Interval')) {
Ext.apply(Extensible.form.recurrence.option.Interval.prototype, {
dateLabelFormat: 'l, j. F',
strings: {
@@ -320,7 +322,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Duration) {
+ if (exists('Extensible.form.recurrence.option.Duration')) {
Ext.apply(Extensible.form.recurrence.option.Duration.prototype, {
strings: {
andContinuing: 'und endet',
@@ -332,7 +334,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Weekly) {
+ if (exists('Extensible.form.recurrence.option.Weekly')) {
Ext.apply(Extensible.form.recurrence.option.Weekly.prototype, {
strings: {
on: 'am'
@@ -340,7 +342,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Monthly) {
+ if (exists('Extensible.form.recurrence.option.Monthly')) {
Ext.apply(Extensible.form.recurrence.option.Monthly.prototype, {
strings: {
// E.g. "on the 15th day of each month/year"
@@ -359,6 +361,41 @@ Ext.onReady(function() {
});
}
+ /*
+ * Strings for agenda view, added in x.x.x
+ */
+ if (exists('Extensible.calendar.template.AgendaBody')) {
+ Ext.apply(Extensible.calendar.template.AgendaBody.prototype, {
+ dayDateFormat: 'D. j. M.',
+ hourFormat: 'G:i',
+ allDayText: 'Ganzer Tag',
+ locationText: 'Ort',
+ webLinkText: 'Web Link',
+ notesText: 'Bemerkung',
+ noEventsText: 'Für den gewählten Datumsbereich existieren keine Termine.',
+ prevLinkText: 'Zurück',
+ nextLinkText: 'Weiter',
+ reminderTooltip: 'Erinnerung ist aktiviert',
+ recurringTooltip: 'Wiederkehrender Termin'
+ });
+ }
+ if (exists('Extensible.calendar.view.AgendaHeader')) {
+ Ext.apply(Extensible.calendar.view.AgendaHeader.prototype, {
+ dateRangeOneDay: 'Ein Tag',
+ dateRangeOneWeek: 'Eine Woche',
+ dateRangeOneMonth: 'Ein Monat',
+ dateRangeThreeMonths: 'Drei Monate',
+ dateRangeOneYear: 'Ein Jahr',
+ dateRangeText: 'Datumsbereich',
+ groupByMonths: 'Monat',
+ groupByWeek: 'Woche',
+ groupByNone: 'Nichts',
+ groupByText: 'Gruppieren nach',
+ showDetailsText: 'Details zeigen',
+ addBtnText: 'Neuer Termin',
+ resetBtnText: 'Zurücksetzen'
+ });
+ }
});
\ No newline at end of file
diff --git a/src/locale/extensible-lang-en.js b/src/locale/extensible-lang-en.js
index 31de4188..1cb746a2 100644
--- a/src/locale/extensible-lang-en.js
+++ b/src/locale/extensible-lang-en.js
@@ -46,6 +46,8 @@ Ext.onReady(function() {
dayText: 'Day',
weekText: 'Week',
monthText: 'Month',
+ agendaText: 'Agenda',
+ listText: 'List',
jumpToText: 'Jump to:',
goText: 'Go',
multiDayText: '{0} Days', // deprecated
@@ -365,4 +367,41 @@ Ext.onReady(function() {
});
}
+ /*
+ * Strings for agenda view. Added in x.x.x
+ */
+ if (exists('Extensible.calendar.template.AgendaBody')) {
+ Ext.apply(Extensible.calendar.template.AgendaBody.prototype, {
+ dayDateFormat: 'D M j',
+ hourFormat: 'g:ia',
+ allDayText: 'All day',
+ locationText: 'Location',
+ webLinkText: 'Web Link',
+ notesText: 'Notes',
+ noEventsText: 'There are no events for the selected date range.',
+ prevLinkText: 'Previous',
+ nextLinkText: 'Next',
+ reminderTooltip: 'Reminder is activated',
+ recurringTooltip: 'Recurring event'
+ });
+ }
+
+ if (exists('Extensible.calendar.view.AgendaHeader')) {
+ Ext.apply(Extensible.calendar.view.AgendaHeader.prototype, {
+ dateRangeOneDay: 'One day',
+ dateRangeOneWeek: 'One week',
+ dateRangeOneMonth: 'One month',
+ dateRangeThreeMonths: 'Three months',
+ dateRangeOneYear: 'One year',
+ dateRangeText: 'Date range',
+ groupByMonths: 'Month',
+ groupByWeek: 'Week',
+ groupByNone: 'None',
+ groupByText: 'Group by',
+ showDetailsText: 'Show details',
+ addBtnText: 'Add event',
+ resetBtnText: 'Reset'
+ });
+ }
+
});
\ No newline at end of file
diff --git a/src/locale/extensible-lang-en_GB.js b/src/locale/extensible-lang-en_GB.js
index caecb686..4d679ea7 100644
--- a/src/locale/extensible-lang-en_GB.js
+++ b/src/locale/extensible-lang-en_GB.js
@@ -277,13 +277,13 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.FrequencyCombo) {
+ if (exists('Extensible.form.recurrence.FrequencyCombo')) {
Ext.apply(Extensible.form.recurrence.FrequencyCombo.prototype, {
fieldLabel: 'Repeats'
});
}
- if (Extensible.form.recurrence.RangeEditWindow) {
+ if (exists('Extensible.form.recurrence.RangeEditWindow')) {
Ext.apply(Extensible.form.recurrence.RangeEditWindow.prototype, {
title: 'Recurring Event Options',
saveButtonText: 'Save',
@@ -291,7 +291,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.RangeEditPanel) {
+ if (exists('Extensible.form.recurrence.RangeEditPanel')) {
Ext.apply(Extensible.form.recurrence.RangeEditPanel.prototype, {
headerText: 'There are multiple events in this series. How would you like your changes applied?',
optionSingleButtonText: 'Single',
@@ -303,7 +303,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Interval) {
+ if (exists('Extensible.form.recurrence.option.Interval')) {
Ext.apply(Extensible.form.recurrence.option.Interval.prototype, {
dateLabelFormat: 'l, F j',
strings: {
@@ -321,7 +321,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Duration) {
+ if (exists('Extensible.form.recurrence.option.Duration')) {
Ext.apply(Extensible.form.recurrence.option.Duration.prototype, {
strings: {
andContinuing: 'and continuing',
@@ -333,7 +333,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Weekly) {
+ if (exists('Extensible.form.recurrence.option.Weekly')) {
Ext.apply(Extensible.form.recurrence.option.Weekly.prototype, {
strings: {
on: 'on'
@@ -341,7 +341,7 @@ Ext.onReady(function() {
});
}
- if (Extensible.form.recurrence.option.Monthly) {
+ if (exists('Extensible.form.recurrence.option.Monthly')) {
Ext.apply(Extensible.form.recurrence.option.Monthly.prototype, {
strings: {
// E.g. "on the 15th day of each month/year"
@@ -359,4 +359,39 @@ Ext.onReady(function() {
}
});
}
+
+ if (exists('Extensible.calendar.template.AgendaBody')) {
+ Ext.apply(Extensible.calendar.template.AgendaBody.prototype, {
+ dayDateFormat: 'D M j',
+ hourFormat: 'G:i',
+ allDayText: 'All day',
+ locationText: 'Location',
+ webLinkText: 'Web Link',
+ notesText: 'Notes',
+ noEventsText: 'There are no events for the selected date range.',
+ prevLinkText: 'Previous',
+ nextLinkText: 'Next',
+ reminderTooltip: 'Reminder is activated',
+ recurringTooltip: 'Recurring event'
+ });
+ }
+
+ if (exists('Extensible.calendar.view.AgendaHeader')) {
+ Ext.apply(Extensible.calendar.view.AgendaHeader.prototype, {
+ dateRangeOneDay: 'One day',
+ dateRangeOneWeek: 'One week',
+ dateRangeOneMonth: 'One month',
+ dateRangeThreeMonths: 'Three months',
+ dateRangeOneYear: 'One year',
+ dateRangeText: 'Date range',
+ groupByMonths: 'Month',
+ groupByWeek: 'Week',
+ groupByNone: 'None',
+ groupByText: 'Group by',
+ showDetailsText: 'Show details',
+ addBtnText: 'Add event',
+ resetBtnText: 'Reset'
+ });
+ }
+
});
\ No newline at end of file